import ext2fs lib to prep for new e2fsprogs
authorMike Frysinger <vapier@gentoo.org>
Mon, 9 May 2005 22:10:42 +0000 (22:10 -0000)
committerMike Frysinger <vapier@gentoo.org>
Mon, 9 May 2005 22:10:42 +0000 (22:10 -0000)
82 files changed:
e2fsprogs/ext2fs/alloc.c [new file with mode: 0644]
e2fsprogs/ext2fs/alloc_sb.c [new file with mode: 0644]
e2fsprogs/ext2fs/alloc_stats.c [new file with mode: 0644]
e2fsprogs/ext2fs/alloc_tables.c [new file with mode: 0644]
e2fsprogs/ext2fs/badblocks.c [new file with mode: 0644]
e2fsprogs/ext2fs/bb_compat.c [new file with mode: 0644]
e2fsprogs/ext2fs/bb_inode.c [new file with mode: 0644]
e2fsprogs/ext2fs/bitmaps.c [new file with mode: 0644]
e2fsprogs/ext2fs/bitops.c [new file with mode: 0644]
e2fsprogs/ext2fs/bitops.h [new file with mode: 0644]
e2fsprogs/ext2fs/block.c [new file with mode: 0644]
e2fsprogs/ext2fs/bmap.c [new file with mode: 0644]
e2fsprogs/ext2fs/bmove.c [new file with mode: 0644]
e2fsprogs/ext2fs/brel.h [new file with mode: 0644]
e2fsprogs/ext2fs/brel_ma.c [new file with mode: 0644]
e2fsprogs/ext2fs/check_desc.c [new file with mode: 0644]
e2fsprogs/ext2fs/closefs.c [new file with mode: 0644]
e2fsprogs/ext2fs/cmp_bitmaps.c [new file with mode: 0644]
e2fsprogs/ext2fs/dblist.c [new file with mode: 0644]
e2fsprogs/ext2fs/dblist_dir.c [new file with mode: 0644]
e2fsprogs/ext2fs/dir_iterate.c [new file with mode: 0644]
e2fsprogs/ext2fs/dirblock.c [new file with mode: 0644]
e2fsprogs/ext2fs/dirhash.c [new file with mode: 0644]
e2fsprogs/ext2fs/dosio.c [new file with mode: 0644]
e2fsprogs/ext2fs/dosio.h [new file with mode: 0644]
e2fsprogs/ext2fs/dupfs.c [new file with mode: 0644]
e2fsprogs/ext2fs/e2image.h [new file with mode: 0644]
e2fsprogs/ext2fs/expanddir.c [new file with mode: 0644]
e2fsprogs/ext2fs/ext2_err.h [new file with mode: 0644]
e2fsprogs/ext2fs/ext2_ext_attr.h [new file with mode: 0644]
e2fsprogs/ext2fs/ext2_fs.h [new file with mode: 0644]
e2fsprogs/ext2fs/ext2_io.h [new file with mode: 0644]
e2fsprogs/ext2fs/ext2_types.h [new file with mode: 0644]
e2fsprogs/ext2fs/ext2fs.h [new file with mode: 0644]
e2fsprogs/ext2fs/ext2fsP.h [new file with mode: 0644]
e2fsprogs/ext2fs/ext_attr.c [new file with mode: 0644]
e2fsprogs/ext2fs/fileio.c [new file with mode: 0644]
e2fsprogs/ext2fs/finddev.c [new file with mode: 0644]
e2fsprogs/ext2fs/flushb.c [new file with mode: 0644]
e2fsprogs/ext2fs/freefs.c [new file with mode: 0644]
e2fsprogs/ext2fs/gen_bitmap.c [new file with mode: 0644]
e2fsprogs/ext2fs/get_pathname.c [new file with mode: 0644]
e2fsprogs/ext2fs/getsectsize.c [new file with mode: 0644]
e2fsprogs/ext2fs/getsize.c [new file with mode: 0644]
e2fsprogs/ext2fs/icount.c [new file with mode: 0644]
e2fsprogs/ext2fs/imager.c [new file with mode: 0644]
e2fsprogs/ext2fs/ind_block.c [new file with mode: 0644]
e2fsprogs/ext2fs/initialize.c [new file with mode: 0644]
e2fsprogs/ext2fs/inline.c [new file with mode: 0644]
e2fsprogs/ext2fs/inode.c [new file with mode: 0644]
e2fsprogs/ext2fs/inode_io.c [new file with mode: 0644]
e2fsprogs/ext2fs/io_manager.c [new file with mode: 0644]
e2fsprogs/ext2fs/irel.h [new file with mode: 0644]
e2fsprogs/ext2fs/irel_ma.c [new file with mode: 0644]
e2fsprogs/ext2fs/ismounted.c [new file with mode: 0644]
e2fsprogs/ext2fs/jfs_compat.h [new file with mode: 0644]
e2fsprogs/ext2fs/jfs_dat.h [new file with mode: 0644]
e2fsprogs/ext2fs/jfs_user.h [new file with mode: 0644]
e2fsprogs/ext2fs/kernel-jbd.h [new file with mode: 0644]
e2fsprogs/ext2fs/kernel-list.h [new file with mode: 0644]
e2fsprogs/ext2fs/link.c [new file with mode: 0644]
e2fsprogs/ext2fs/llseek.c [new file with mode: 0644]
e2fsprogs/ext2fs/lookup.c [new file with mode: 0644]
e2fsprogs/ext2fs/mkdir.c [new file with mode: 0644]
e2fsprogs/ext2fs/mkjournal.c [new file with mode: 0644]
e2fsprogs/ext2fs/namei.c [new file with mode: 0644]
e2fsprogs/ext2fs/native.c [new file with mode: 0644]
e2fsprogs/ext2fs/newdir.c [new file with mode: 0644]
e2fsprogs/ext2fs/openfs.c [new file with mode: 0644]
e2fsprogs/ext2fs/read_bb.c [new file with mode: 0644]
e2fsprogs/ext2fs/read_bb_file.c [new file with mode: 0644]
e2fsprogs/ext2fs/res_gdt.c [new file with mode: 0644]
e2fsprogs/ext2fs/rs_bitmap.c [new file with mode: 0644]
e2fsprogs/ext2fs/rw_bitmaps.c [new file with mode: 0644]
e2fsprogs/ext2fs/sparse.c [new file with mode: 0644]
e2fsprogs/ext2fs/swapfs.c [new file with mode: 0644]
e2fsprogs/ext2fs/test_io.c [new file with mode: 0644]
e2fsprogs/ext2fs/unix_io.c [new file with mode: 0644]
e2fsprogs/ext2fs/unlink.c [new file with mode: 0644]
e2fsprogs/ext2fs/valid_blk.c [new file with mode: 0644]
e2fsprogs/ext2fs/version.c [new file with mode: 0644]
e2fsprogs/ext2fs/write_bb_file.c [new file with mode: 0644]

diff --git a/e2fsprogs/ext2fs/alloc.c b/e2fsprogs/ext2fs/alloc.c
new file mode 100644 (file)
index 0000000..7385123
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * alloc.c --- allocate new inodes, blocks for ext2fs
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ * 
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <string.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Right now, just search forward from the parent directory's block
+ * group to find the next free inode.
+ *
+ * Should have a special policy for directories.
+ */
+errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, 
+                          int mode EXT2FS_ATTR((unused)),
+                          ext2fs_inode_bitmap map, ext2_ino_t *ret)
+{
+       ext2_ino_t      dir_group = 0;
+       ext2_ino_t      i;
+       ext2_ino_t      start_inode;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+       
+       if (!map)
+               map = fs->inode_map;
+       if (!map)
+               return EXT2_ET_NO_INODE_BITMAP;
+       
+       if (dir > 0) 
+               dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super);
+
+       start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->super)) + 1;
+       if (start_inode < EXT2_FIRST_INODE(fs->super))
+               start_inode = EXT2_FIRST_INODE(fs->super);
+       i = start_inode;
+
+       do {
+               if (!ext2fs_fast_test_inode_bitmap(map, i))
+                       break;
+               i++;
+               if (i > fs->super->s_inodes_count)
+                       i = EXT2_FIRST_INODE(fs->super);
+       } while (i != start_inode);
+       
+       if (ext2fs_test_inode_bitmap(map, i))
+               return EXT2_ET_INODE_ALLOC_FAIL;
+       *ret = i;
+       return 0;
+}
+
+/*
+ * Stupid algorithm --- we now just search forward starting from the
+ * goal.  Should put in a smarter one someday....
+ */
+errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal,
+                          ext2fs_block_bitmap map, blk_t *ret)
+{
+       blk_t   i;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (!map)
+               map = fs->block_map;
+       if (!map)
+               return EXT2_ET_NO_BLOCK_BITMAP;
+       if (!goal || (goal >= fs->super->s_blocks_count))
+               goal = fs->super->s_first_data_block;
+       i = goal;
+       do {
+               if (!ext2fs_fast_test_block_bitmap(map, i)) {
+                       *ret = i;
+                       return 0;
+               }
+               i++;
+               if (i >= fs->super->s_blocks_count)
+                       i = fs->super->s_first_data_block;
+       } while (i != goal);
+       return EXT2_ET_BLOCK_ALLOC_FAIL;
+}
+
+/*
+ * This function zeros out the allocated block, and updates all of the
+ * appropriate filesystem records.
+ */
+errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal,
+                            char *block_buf, blk_t *ret)
+{
+       errcode_t       retval;
+       blk_t           block;
+       char            *buf = 0;
+
+       if (!block_buf) {
+               retval = ext2fs_get_mem(fs->blocksize, &buf);
+               if (retval)
+                       return retval;
+               block_buf = buf;
+       }
+       memset(block_buf, 0, fs->blocksize);
+
+       if (!fs->block_map) {
+               retval = ext2fs_read_block_bitmap(fs);
+               if (retval)
+                       goto fail;
+       }
+
+       retval = ext2fs_new_block(fs, goal, 0, &block);
+       if (retval)
+               goto fail;
+
+       retval = io_channel_write_blk(fs->io, block, 1, block_buf);
+       if (retval)
+               goto fail;
+       
+       ext2fs_block_alloc_stats(fs, block, +1);
+       *ret = block;
+       return 0;
+
+fail:
+       if (buf)
+               ext2fs_free_mem(&buf);
+       return retval;
+}
+
+errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish,
+                                int num, ext2fs_block_bitmap map, blk_t *ret)
+{
+       blk_t   b = start;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (!map)
+               map = fs->block_map;
+       if (!map)
+               return EXT2_ET_NO_BLOCK_BITMAP;
+       if (!b)
+               b = fs->super->s_first_data_block;
+       if (!finish)
+               finish = start;
+       if (!num)
+               num = 1;
+       do {
+               if (b+num-1 > fs->super->s_blocks_count)
+                       b = fs->super->s_first_data_block;
+               if (ext2fs_fast_test_block_bitmap_range(map, b, num)) {
+                       *ret = b;
+                       return 0;
+               }
+               b++;
+       } while (b != finish);
+       return EXT2_ET_BLOCK_ALLOC_FAIL;
+}
+
diff --git a/e2fsprogs/ext2fs/alloc_sb.c b/e2fsprogs/ext2fs/alloc_sb.c
new file mode 100644 (file)
index 0000000..ef40b93
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * alloc_sb.c --- Allocate the superblock and block group descriptors for a 
+ * newly initialized filesystem.  Used by mke2fs when initializing a filesystem
+ *
+ * Copyright (C) 1994, 1995, 1996, 2003 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+int ext2fs_reserve_super_and_bgd(ext2_filsys fs, 
+                                dgrp_t group,
+                                ext2fs_block_bitmap bmap)
+{
+       blk_t   super_blk, old_desc_blk, new_desc_blk;
+       int     j, old_desc_blocks, num_blocks;
+
+       num_blocks = ext2fs_super_and_bgd_loc(fs, group, &super_blk, 
+                                             &old_desc_blk, &new_desc_blk, 0);
+
+       if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+               old_desc_blocks = fs->super->s_first_meta_bg;
+       else
+               old_desc_blocks = 
+                       fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
+
+       if (super_blk || (group == 0))
+               ext2fs_mark_block_bitmap(bmap, super_blk);
+
+       if (old_desc_blk) {
+               for (j=0; j < old_desc_blocks; j++)
+                       ext2fs_mark_block_bitmap(bmap, old_desc_blk + j);
+       }
+       if (new_desc_blk)
+               ext2fs_mark_block_bitmap(bmap, new_desc_blk);
+
+       return num_blocks;
+}
diff --git a/e2fsprogs/ext2fs/alloc_stats.c b/e2fsprogs/ext2fs/alloc_stats.c
new file mode 100644 (file)
index 0000000..4088f7b
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * alloc_stats.c --- Update allocation statistics for ext2fs
+ *
+ * Copyright (C) 2001 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ * 
+ */
+
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino,
+                              int inuse, int isdir)
+{
+       int     group = ext2fs_group_of_ino(fs, ino);
+
+       if (inuse > 0)
+               ext2fs_mark_inode_bitmap(fs->inode_map, ino);
+       else
+               ext2fs_unmark_inode_bitmap(fs->inode_map, ino);
+       fs->group_desc[group].bg_free_inodes_count -= inuse;
+       if (isdir)
+               fs->group_desc[group].bg_used_dirs_count += inuse;
+       fs->super->s_free_inodes_count -= inuse;
+       ext2fs_mark_super_dirty(fs);
+       ext2fs_mark_ib_dirty(fs);
+}
+
+void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse)
+{
+       ext2fs_inode_alloc_stats2(fs, ino, inuse, 0);
+}
+
+void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse)
+{
+       int     group = ext2fs_group_of_blk(fs, blk);
+
+       if (inuse > 0)
+               ext2fs_mark_block_bitmap(fs->block_map, blk);
+       else
+               ext2fs_unmark_block_bitmap(fs->block_map, blk);
+       fs->group_desc[group].bg_free_blocks_count -= inuse;
+       fs->super->s_free_blocks_count -= inuse;
+       ext2fs_mark_super_dirty(fs);
+       ext2fs_mark_bb_dirty(fs);
+}
diff --git a/e2fsprogs/ext2fs/alloc_tables.c b/e2fsprogs/ext2fs/alloc_tables.c
new file mode 100644 (file)
index 0000000..0326321
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * alloc_tables.c --- Allocate tables for a newly initialized
+ * filesystem.  Used by mke2fs when initializing a filesystem
+ *
+ * Copyright (C) 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
+                                     ext2fs_block_bitmap bmap)
+{
+       errcode_t       retval;
+       blk_t           group_blk, start_blk, last_blk, new_blk, blk;
+       int             j;
+
+       group_blk = fs->super->s_first_data_block +
+               (group * fs->super->s_blocks_per_group);
+       
+       last_blk = group_blk + fs->super->s_blocks_per_group;
+       if (last_blk >= fs->super->s_blocks_count)
+               last_blk = fs->super->s_blocks_count - 1;
+
+       if (!bmap)
+               bmap = fs->block_map;
+       
+       /*
+        * Allocate the block and inode bitmaps, if necessary
+        */
+       if (fs->stride) {
+               start_blk = group_blk + fs->inode_blocks_per_group;
+               start_blk += ((fs->stride * group) %
+                             (last_blk - start_blk));
+               if (start_blk > last_blk)
+                       start_blk = group_blk;
+       } else
+               start_blk = group_blk;
+
+       if (!fs->group_desc[group].bg_block_bitmap) {
+               retval = ext2fs_get_free_blocks(fs, start_blk, last_blk,
+                                               1, bmap, &new_blk);
+               if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) 
+                       retval = ext2fs_get_free_blocks(fs, group_blk,
+                                       last_blk, 1, bmap, &new_blk);
+               if (retval)
+                       return retval;
+               ext2fs_mark_block_bitmap(bmap, new_blk);
+               fs->group_desc[group].bg_block_bitmap = new_blk;
+       }
+
+       if (!fs->group_desc[group].bg_inode_bitmap) {
+               retval = ext2fs_get_free_blocks(fs, start_blk, last_blk,
+                                               1, bmap, &new_blk);
+               if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) 
+                       retval = ext2fs_get_free_blocks(fs, group_blk,
+                                       last_blk, 1, bmap, &new_blk);
+               if (retval)
+                       return retval;
+               ext2fs_mark_block_bitmap(bmap, new_blk);
+               fs->group_desc[group].bg_inode_bitmap = new_blk;
+       }
+
+       /*
+        * Allocate the inode table
+        */
+       if (!fs->group_desc[group].bg_inode_table) {
+               retval = ext2fs_get_free_blocks(fs, group_blk, last_blk,
+                                               fs->inode_blocks_per_group,
+                                               bmap, &new_blk);
+               if (retval)
+                       return retval;
+               for (j=0, blk = new_blk;
+                    j < fs->inode_blocks_per_group;
+                    j++, blk++)
+                       ext2fs_mark_block_bitmap(bmap, blk);
+               fs->group_desc[group].bg_inode_table = new_blk;
+       }
+
+       
+       return 0;
+}
+
+       
+
+errcode_t ext2fs_allocate_tables(ext2_filsys fs)
+{
+       errcode_t       retval;
+       dgrp_t          i;
+
+       for (i = 0; i < fs->group_desc_count; i++) {
+               retval = ext2fs_allocate_group_table(fs, i, fs->block_map);
+               if (retval)
+                       return retval;
+       }
+       return 0;
+}
+
diff --git a/e2fsprogs/ext2fs/badblocks.c b/e2fsprogs/ext2fs/badblocks.c
new file mode 100644 (file)
index 0000000..4b76ef0
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * badblocks.c --- routines to manipulate the bad block structure
+ * 
+ * Copyright (C) 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+/*
+ * Helper function for making a badblocks list
+ */
+static errcode_t make_u32_list(int size, int num, __u32 *list,
+                              ext2_u32_list *ret)
+{
+       ext2_u32_list   bb;
+       errcode_t       retval;
+       
+       retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_list), &bb);
+       if (retval)
+               return retval;
+       memset(bb, 0, sizeof(struct ext2_struct_u32_list));
+       bb->magic = EXT2_ET_MAGIC_BADBLOCKS_LIST;
+       bb->size = size ? size : 10;
+       bb->num = num;
+       retval = ext2fs_get_mem(bb->size * sizeof(blk_t), &bb->list);
+       if (!bb->list) {
+               ext2fs_free_mem(&bb);
+               return retval;
+       }
+       if (list)
+               memcpy(bb->list, list, bb->size * sizeof(blk_t));
+       else
+               memset(bb->list, 0, bb->size * sizeof(blk_t));
+       *ret = bb;
+       return 0;
+}
+       
+
+/*
+ * This procedure creates an empty u32 list.
+ */
+errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size)
+{
+       return make_u32_list(size, 0, 0, ret);
+}
+
+/*
+ * This procedure creates an empty badblocks list.
+ */
+errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, int size)
+{
+       return make_u32_list(size, 0, 0, (ext2_badblocks_list *) ret);
+}
+
+
+/*
+ * This procedure copies a badblocks list
+ */
+errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest)
+{
+       errcode_t       retval;
+       
+       retval = make_u32_list(src->size, src->num, src->list, dest);
+       if (retval)
+               return retval;
+       (*dest)->badblocks_flags = src->badblocks_flags;
+       return 0;
+}
+
+errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
+                               ext2_badblocks_list *dest)
+{
+       return ext2fs_u32_copy((ext2_u32_list) src,
+                              (ext2_u32_list *) dest);
+}
+
+/*
+ * This procedure frees a badblocks list.
+ *
+ * (note: moved to closefs.c)
+ */
+
+
+/*
+ * This procedure adds a block to a badblocks list.
+ */
+errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk)
+{
+       errcode_t       retval;
+       int             i, j;
+       unsigned long   old_size;
+
+       EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST);
+
+       if (bb->num >= bb->size) {
+               old_size = bb->size * sizeof(__u32);
+               bb->size += 100;
+               retval = ext2fs_resize_mem(old_size, bb->size * sizeof(__u32),
+                                          &bb->list);
+               if (retval) {
+                       bb->size -= 100;
+                       return retval;
+               }
+       }
+
+       /*
+        * Add special case code for appending to the end of the list
+        */
+       i = bb->num-1;
+       if ((bb->num != 0) && (bb->list[i] == blk))
+               return 0;
+       if ((bb->num == 0) || (bb->list[i] < blk)) {
+               bb->list[bb->num++] = blk;
+               return 0;
+       }
+
+       j = bb->num;
+       for (i=0; i < bb->num; i++) {
+               if (bb->list[i] == blk)
+                       return 0;
+               if (bb->list[i] > blk) {
+                       j = i;
+                       break;
+               }
+       }
+       for (i=bb->num; i > j; i--)
+               bb->list[i] = bb->list[i-1];
+       bb->list[j] = blk;
+       bb->num++;
+       return 0;
+}
+
+errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, blk_t blk)
+{
+       return ext2fs_u32_list_add((ext2_u32_list) bb, (__u32) blk);
+}
+
+/*
+ * This procedure finds a particular block is on a badblocks
+ * list.
+ */
+int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk)
+{
+       int     low, high, mid;
+
+       if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST)
+               return -1;
+
+       if (bb->num == 0)
+               return -1;
+
+       low = 0;
+       high = bb->num-1;
+       if (blk == bb->list[low])
+               return low;
+       if (blk == bb->list[high])
+               return high;
+
+       while (low < high) {
+               mid = (low+high)/2;
+               if (mid == low || mid == high)
+                       break;
+               if (blk == bb->list[mid])
+                       return mid;
+               if (blk < bb->list[mid])
+                       high = mid;
+               else
+                       low = mid;
+       }
+       return -1;
+}
+
+/*
+ * This procedure tests to see if a particular block is on a badblocks
+ * list.
+ */
+int ext2fs_u32_list_test(ext2_u32_list bb, __u32 blk)
+{
+       if (ext2fs_u32_list_find(bb, blk) < 0)
+               return 0;
+       else
+               return 1;
+}
+
+int ext2fs_badblocks_list_test(ext2_badblocks_list bb, blk_t blk)
+{
+       return ext2fs_u32_list_test((ext2_u32_list) bb, (__u32) blk);
+}
+
+
+/*
+ * Remove a block from the badblock list
+ */
+int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk)
+{
+       int     remloc, i;
+
+       if (bb->num == 0)
+               return -1;
+
+       remloc = ext2fs_u32_list_find(bb, blk);
+       if (remloc < 0)
+               return -1;
+
+       for (i = remloc ; i < bb->num-1; i++)
+               bb->list[i] = bb->list[i+1];
+       bb->num--;
+       return 0;
+}
+
+void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk)
+{
+       ext2fs_u32_list_del(bb, blk);
+}
+
+errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb,
+                                       ext2_u32_iterate *ret)
+{
+       ext2_u32_iterate iter;
+       errcode_t               retval;
+
+       EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST);
+
+       retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_iterate), &iter);
+       if (retval)
+               return retval;
+
+       iter->magic = EXT2_ET_MAGIC_BADBLOCKS_ITERATE;
+       iter->bb = bb;
+       iter->ptr = 0;
+       *ret = iter;
+       return 0;
+}
+
+errcode_t ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb,
+                                             ext2_badblocks_iterate *ret)
+{
+       return ext2fs_u32_list_iterate_begin((ext2_u32_list) bb,
+                                             (ext2_u32_iterate *) ret);
+}
+
+
+int ext2fs_u32_list_iterate(ext2_u32_iterate iter, __u32 *blk)
+{
+       ext2_u32_list   bb;
+
+       if (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE)
+               return 0;
+
+       bb = iter->bb;
+
+       if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST)
+               return 0;
+       
+       if (iter->ptr < bb->num) {
+               *blk = bb->list[iter->ptr++];
+               return 1;
+       } 
+       *blk = 0;
+       return 0;
+}
+
+int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, blk_t *blk)
+{
+       return ext2fs_u32_list_iterate((ext2_u32_iterate) iter,
+                                      (__u32 *) blk);
+}
+
+
+void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter)
+{
+       if (!iter || (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE))
+               return;
+
+       iter->bb = 0;
+       ext2fs_free_mem(&iter);
+}
+
+void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter)
+{
+       ext2fs_u32_list_iterate_end((ext2_u32_iterate) iter);
+}
+
+
+int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2)
+{
+       EXT2_CHECK_MAGIC(bb1, EXT2_ET_MAGIC_BADBLOCKS_LIST);
+       EXT2_CHECK_MAGIC(bb2, EXT2_ET_MAGIC_BADBLOCKS_LIST);
+
+       if (bb1->num != bb2->num)
+               return 0;
+
+       if (memcmp(bb1->list, bb2->list, bb1->num * sizeof(blk_t)) != 0)
+               return 0;
+       return 1;
+}
+
+int ext2fs_badblocks_equal(ext2_badblocks_list bb1, ext2_badblocks_list bb2)
+{
+       return ext2fs_u32_list_equal((ext2_u32_list) bb1,
+                                    (ext2_u32_list) bb2);
+}
+
+int ext2fs_u32_list_count(ext2_u32_list bb)
+{
+       return bb->num;
+}
diff --git a/e2fsprogs/ext2fs/bb_compat.c b/e2fsprogs/ext2fs/bb_compat.c
new file mode 100644 (file)
index 0000000..40f7343
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * bb_compat.c --- compatibility badblocks routines
+ * 
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+errcode_t badblocks_list_create(badblocks_list *ret, int size)
+{
+       return ext2fs_badblocks_list_create(ret, size);
+}
+
+void badblocks_list_free(badblocks_list bb)
+{
+       ext2fs_badblocks_list_free(bb);
+}
+
+errcode_t badblocks_list_add(badblocks_list bb, blk_t blk)
+{
+       return ext2fs_badblocks_list_add(bb, blk);
+}
+
+int badblocks_list_test(badblocks_list bb, blk_t blk)
+{
+       return ext2fs_badblocks_list_test(bb, blk);
+}
+
+errcode_t badblocks_list_iterate_begin(badblocks_list bb,
+                                      badblocks_iterate *ret)
+{
+       return ext2fs_badblocks_list_iterate_begin(bb, ret);
+}
+
+int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk)
+{
+       return ext2fs_badblocks_list_iterate(iter, blk);
+}
+
+void badblocks_list_iterate_end(badblocks_iterate iter)
+{
+       ext2fs_badblocks_list_iterate_end(iter);
+}
diff --git a/e2fsprogs/ext2fs/bb_inode.c b/e2fsprogs/ext2fs/bb_inode.c
new file mode 100644 (file)
index 0000000..dd8e7c3
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * bb_inode.c --- routines to update the bad block inode.
+ * 
+ * WARNING: This routine modifies a lot of state in the filesystem; if
+ * this routine returns an error, the bad block inode may be in an
+ * inconsistent state.
+ * 
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ * 
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct set_badblock_record {
+       ext2_badblocks_iterate  bb_iter;
+       int             bad_block_count;
+       blk_t           *ind_blocks;
+       int             max_ind_blocks;
+       int             ind_blocks_size;
+       int             ind_blocks_ptr;
+       char            *block_buf;
+       errcode_t       err;
+};
+
+static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
+                             e2_blkcnt_t blockcnt,
+                             blk_t ref_block, int ref_offset,
+                             void *priv_data);
+static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
+                               e2_blkcnt_t blockcnt,
+                               blk_t ref_block, int ref_offset,
+                               void *priv_data);
+       
+/*
+ * Given a bad blocks bitmap, update the bad blocks inode to reflect
+ * the map.
+ */
+errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list)
+{
+       errcode_t                       retval;
+       struct set_badblock_record      rec;
+       struct ext2_inode               inode;
+       
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (!fs->block_map)
+               return EXT2_ET_NO_BLOCK_BITMAP;
+       
+       rec.bad_block_count = 0;
+       rec.ind_blocks_size = rec.ind_blocks_ptr = 0;
+       rec.max_ind_blocks = 10;
+       retval = ext2fs_get_mem(rec.max_ind_blocks * sizeof(blk_t),
+                               &rec.ind_blocks);
+       if (retval)
+               return retval;
+       memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t));
+       retval = ext2fs_get_mem(fs->blocksize, &rec.block_buf);
+       if (retval)
+               goto cleanup;
+       memset(rec.block_buf, 0, fs->blocksize);
+       rec.err = 0;
+       
+       /*
+        * First clear the old bad blocks (while saving the indirect blocks) 
+        */
+       retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
+                                      BLOCK_FLAG_DEPTH_TRAVERSE, 0,
+                                      clear_bad_block_proc, &rec);
+       if (retval)
+               goto cleanup;
+       if (rec.err) {
+               retval = rec.err;
+               goto cleanup;
+       }
+       
+       /*
+        * Now set the bad blocks!
+        *
+        * First, mark the bad blocks as used.  This prevents a bad
+        * block from being used as an indirecto block for the bad
+        * block inode (!).
+        */
+       if (bb_list) {
+               retval = ext2fs_badblocks_list_iterate_begin(bb_list,
+                                                            &rec.bb_iter);
+               if (retval)
+                       goto cleanup;
+               retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO,
+                                              BLOCK_FLAG_APPEND, 0,
+                                              set_bad_block_proc, &rec);
+               ext2fs_badblocks_list_iterate_end(rec.bb_iter);
+               if (retval) 
+                       goto cleanup;
+               if (rec.err) {
+                       retval = rec.err;
+                       goto cleanup;
+               }
+       }
+       
+       /*
+        * Update the bad block inode's mod time and block count
+        * field.  
+        */
+       retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode);
+       if (retval)
+               goto cleanup;
+       
+       inode.i_atime = inode.i_mtime = time(0);
+       if (!inode.i_ctime)
+               inode.i_ctime = time(0);
+       inode.i_blocks = rec.bad_block_count * (fs->blocksize / 512);
+       inode.i_size = rec.bad_block_count * fs->blocksize;
+
+       retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode);
+       if (retval)
+               goto cleanup;
+       
+cleanup:
+       ext2fs_free_mem(&rec.ind_blocks);
+       ext2fs_free_mem(&rec.block_buf);
+       return retval;
+}
+
+/*
+ * Helper function for update_bb_inode()
+ *
+ * Clear the bad blocks in the bad block inode, while saving the
+ * indirect blocks.
+ */
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
+                               e2_blkcnt_t blockcnt,
+                               blk_t ref_block EXT2FS_ATTR((unused)),
+                               int ref_offset EXT2FS_ATTR((unused)),
+                               void *priv_data)
+{
+       struct set_badblock_record *rec = (struct set_badblock_record *)
+               priv_data;
+       errcode_t       retval;
+       unsigned long   old_size;
+
+       if (!*block_nr)
+               return 0;
+
+       /*
+        * If the block number is outrageous, clear it and ignore it.
+        */
+       if (*block_nr >= fs->super->s_blocks_count ||
+           *block_nr < fs->super->s_first_data_block) {
+               *block_nr = 0;
+               return BLOCK_CHANGED;
+       }
+
+       if (blockcnt < 0) {
+               if (rec->ind_blocks_size >= rec->max_ind_blocks) {
+                       old_size = rec->max_ind_blocks * sizeof(blk_t);
+                       rec->max_ind_blocks += 10;
+                       retval = ext2fs_resize_mem(old_size, 
+                                  rec->max_ind_blocks * sizeof(blk_t),
+                                  &rec->ind_blocks);
+                       if (retval) {
+                               rec->max_ind_blocks -= 10;
+                               rec->err = retval;
+                               return BLOCK_ABORT;
+                       }
+               }
+               rec->ind_blocks[rec->ind_blocks_size++] = *block_nr;
+       }
+
+       /*
+        * Mark the block as unused, and update accounting information
+        */
+       ext2fs_block_alloc_stats(fs, *block_nr, -1);
+       
+       *block_nr = 0;
+       return BLOCK_CHANGED;
+}
+
+       
+/*
+ * Helper function for update_bb_inode()
+ *
+ * Set the block list in the bad block inode, using the supplied bitmap.
+ */
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
+                             e2_blkcnt_t blockcnt,
+                             blk_t ref_block EXT2FS_ATTR((unused)),
+                             int ref_offset EXT2FS_ATTR((unused)),
+                             void *priv_data)
+{
+       struct set_badblock_record *rec = (struct set_badblock_record *)
+               priv_data;
+       errcode_t       retval;
+       blk_t           blk;
+
+       if (blockcnt >= 0) {
+               /*
+                * Get the next bad block.
+                */
+               if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk))
+                       return BLOCK_ABORT;
+               rec->bad_block_count++;
+       } else {
+               /*
+                * An indirect block; fetch a block from the
+                * previously used indirect block list.  The block
+                * most be not marked as used; if so, get another one.
+                * If we run out of reserved indirect blocks, allocate
+                * a new one.
+                */
+       retry:
+               if (rec->ind_blocks_ptr < rec->ind_blocks_size) {
+                       blk = rec->ind_blocks[rec->ind_blocks_ptr++];
+                       if (ext2fs_test_block_bitmap(fs->block_map, blk))
+                               goto retry;
+               } else {
+                       retval = ext2fs_new_block(fs, 0, 0, &blk);
+                       if (retval) {
+                               rec->err = retval;
+                               return BLOCK_ABORT;
+                       }
+               }
+               retval = io_channel_write_blk(fs->io, blk, 1, rec->block_buf);
+               if (retval) {
+                       rec->err = retval;
+                       return BLOCK_ABORT;
+               }
+       }
+       
+       /*
+        * Update block counts
+        */
+       ext2fs_block_alloc_stats(fs, blk, +1);
+       
+       *block_nr = blk;
+       return BLOCK_CHANGED;
+}
+
+
+
+
+
+
diff --git a/e2fsprogs/ext2fs/bitmaps.c b/e2fsprogs/ext2fs/bitmaps.c
new file mode 100644 (file)
index 0000000..7edd28d
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * bitmaps.c --- routines to read, write, and manipulate the inode and
+ * block bitmaps.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+static errcode_t make_bitmap(__u32 start, __u32 end, __u32 real_end,
+                            const char *descr, char *init_map,
+                            ext2fs_generic_bitmap *ret)
+{
+       ext2fs_generic_bitmap   bitmap;
+       errcode_t               retval;
+       size_t                  size;
+
+       retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap), 
+                               &bitmap);
+       if (retval)
+               return retval;
+
+       bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+       bitmap->fs = NULL;
+       bitmap->start = start;
+       bitmap->end = end;
+       bitmap->real_end = real_end;
+       bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK;
+       if (descr) {
+               retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description);
+               if (retval) {
+                       ext2fs_free_mem(&bitmap);
+                       return retval;
+               }
+               strcpy(bitmap->description, descr);
+       } else
+               bitmap->description = 0;
+
+       size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1);
+       retval = ext2fs_get_mem(size, &bitmap->bitmap);
+       if (retval) {
+               ext2fs_free_mem(&bitmap->description);
+               ext2fs_free_mem(&bitmap);
+               return retval;
+       }
+
+       if (init_map)
+               memcpy(bitmap->bitmap, init_map, size);
+       else
+               memset(bitmap->bitmap, 0, size);
+       *ret = bitmap;
+       return 0;
+}
+
+errcode_t ext2fs_allocate_generic_bitmap(__u32 start,
+                                        __u32 end,
+                                        __u32 real_end,
+                                        const char *descr,
+                                        ext2fs_generic_bitmap *ret)
+{
+       return make_bitmap(start, end, real_end, descr, 0, ret);
+}
+
+errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
+                            ext2fs_generic_bitmap *dest)
+{
+       errcode_t               retval;
+       ext2fs_generic_bitmap   new_map;
+
+       retval = make_bitmap(src->start, src->end, src->real_end,
+                            src->description, src->bitmap, &new_map);
+       if (retval)
+               return retval;
+       new_map->magic = src->magic;
+       new_map->fs = src->fs;
+       new_map->base_error_code = src->base_error_code;
+       *dest = new_map;
+       return 0;
+}
+
+void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map)
+{
+       __u32   i, j;
+
+       for (i=map->end+1, j = i - map->start; i <= map->real_end; i++, j++)
+               ext2fs_set_bit(j, map->bitmap);
+
+       return;
+}      
+
+errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs,
+                                      const char *descr,
+                                      ext2fs_inode_bitmap *ret)
+{
+       ext2fs_inode_bitmap bitmap;
+       errcode_t       retval;
+       __u32           start, end, real_end;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       fs->write_bitmaps = ext2fs_write_bitmaps;
+
+       start = 1;
+       end = fs->super->s_inodes_count;
+       real_end = (EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count);
+
+       retval = ext2fs_allocate_generic_bitmap(start, end, real_end,
+                                               descr, &bitmap);
+       if (retval)
+               return retval;
+       
+       bitmap->magic = EXT2_ET_MAGIC_INODE_BITMAP;
+       bitmap->fs = fs;
+       bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK;
+       
+       *ret = bitmap;
+       return 0;
+}
+
+errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs,
+                                      const char *descr,
+                                      ext2fs_block_bitmap *ret)
+{
+       ext2fs_block_bitmap bitmap;
+       errcode_t       retval;
+       __u32           start, end, real_end;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       fs->write_bitmaps = ext2fs_write_bitmaps;
+
+       start = fs->super->s_first_data_block;
+       end = fs->super->s_blocks_count-1;
+       real_end = (EXT2_BLOCKS_PER_GROUP(fs->super)  
+                   * fs->group_desc_count)-1 + start;
+       
+       retval = ext2fs_allocate_generic_bitmap(start, end, real_end,
+                                               descr, &bitmap);
+       if (retval)
+               return retval;
+
+       bitmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP;
+       bitmap->fs = fs;
+       bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK;
+       
+       *ret = bitmap;
+       return 0;
+}
+
+errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap,
+                                       ext2_ino_t end, ext2_ino_t *oend)
+{
+       EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_INODE_BITMAP);
+       
+       if (end > bitmap->real_end)
+               return EXT2_ET_FUDGE_INODE_BITMAP_END;
+       if (oend)
+               *oend = bitmap->end;
+       bitmap->end = end;
+       return 0;
+}
+
+errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap,
+                                       blk_t end, blk_t *oend)
+{
+       EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_BLOCK_BITMAP);
+       
+       if (end > bitmap->real_end)
+               return EXT2_ET_FUDGE_BLOCK_BITMAP_END;
+       if (oend)
+               *oend = bitmap->end;
+       bitmap->end = end;
+       return 0;
+}
+
+void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap)
+{
+       if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP))
+               return;
+
+       memset(bitmap->bitmap, 0,
+              (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1));
+}
+
+void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap)
+{
+       if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP))
+               return;
+
+       memset(bitmap->bitmap, 0,
+              (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1));
+}
diff --git a/e2fsprogs/ext2fs/bitops.c b/e2fsprogs/ext2fs/bitops.c
new file mode 100644 (file)
index 0000000..207c44d
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * bitops.c --- Bitmap frobbing code.  See bitops.h for the inlined
+ *     routines.
+ * 
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef _EXT2_HAVE_ASM_BITOPS_
+
+/*
+ * For the benefit of those who are trying to port Linux to another
+ * architecture, here are some C-language equivalents.  You should
+ * recode these in the native assmebly language, if at all possible.
+ *
+ * C language equivalents written by Theodore Ts'o, 9/26/92.
+ * Modified by Pete A. Zaitcev 7/14/95 to be portable to big endian
+ * systems, as well as non-32 bit systems.
+ */
+
+int ext2fs_set_bit(int nr,void * addr)
+{
+       int             mask, retval;
+       unsigned char   *ADDR = (unsigned char *) addr;
+
+       ADDR += nr >> 3;
+       mask = 1 << (nr & 0x07);
+       retval = mask & *ADDR;
+       *ADDR |= mask;
+       return retval;
+}
+
+int ext2fs_clear_bit(int nr, void * addr)
+{
+       int             mask, retval;
+       unsigned char   *ADDR = (unsigned char *) addr;
+
+       ADDR += nr >> 3;
+       mask = 1 << (nr & 0x07);
+       retval = mask & *ADDR;
+       *ADDR &= ~mask;
+       return retval;
+}
+
+int ext2fs_test_bit(int nr, const void * addr)
+{
+       int                     mask;
+       const unsigned char     *ADDR = (const unsigned char *) addr;
+
+       ADDR += nr >> 3;
+       mask = 1 << (nr & 0x07);
+       return (mask & *ADDR);
+}
+
+#endif /* !_EXT2_HAVE_ASM_BITOPS_ */
+
+void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg,
+                       const char *description)
+{
+#ifndef OMIT_COM_ERR
+       if (description)
+               com_err(0, errcode, "#%lu for %s", arg, description);
+       else
+               com_err(0, errcode, "#%lu", arg);
+#endif
+}
+
+void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap,
+                           int code, unsigned long arg)
+{
+#ifndef OMIT_COM_ERR
+       if (bitmap->description)
+               com_err(0, bitmap->base_error_code+code,
+                       "#%lu for %s", arg, bitmap->description);
+       else
+               com_err(0, bitmap->base_error_code + code, "#%lu", arg);
+#endif
+}
+
diff --git a/e2fsprogs/ext2fs/bitops.h b/e2fsprogs/ext2fs/bitops.h
new file mode 100644 (file)
index 0000000..b223809
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * bitops.h --- Bitmap frobbing code.  The byte swapping routines are
+ *     also included here.
+ * 
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ * 
+ * i386 bitops operations taken from <asm/bitops.h>, Copyright 1992,
+ * Linus Torvalds.
+ */
+
+
+extern int ext2fs_set_bit(int nr,void * addr);
+extern int ext2fs_clear_bit(int nr, void * addr);
+extern int ext2fs_test_bit(int nr, const void * addr);
+extern __u16 ext2fs_swab16(__u16 val);
+extern __u32 ext2fs_swab32(__u32 val);
+
+#ifdef WORDS_BIGENDIAN
+#define ext2fs_cpu_to_le32(x) ext2fs_swab32((x))
+#define ext2fs_le32_to_cpu(x) ext2fs_swab32((x))
+#define ext2fs_cpu_to_le16(x) ext2fs_swab16((x))
+#define ext2fs_le16_to_cpu(x) ext2fs_swab16((x))
+#define ext2fs_cpu_to_be32(x) ((__u32)(x))
+#define ext2fs_be32_to_cpu(x) ((__u32)(x))
+#define ext2fs_cpu_to_be16(x) ((__u16)(x))
+#define ext2fs_be16_to_cpu(x) ((__u16)(x))
+#else
+#define ext2fs_cpu_to_le32(x) ((__u32)(x))
+#define ext2fs_le32_to_cpu(x) ((__u32)(x))
+#define ext2fs_cpu_to_le16(x) ((__u16)(x))
+#define ext2fs_le16_to_cpu(x) ((__u16)(x))
+#define ext2fs_cpu_to_be32(x) ext2fs_swab32((x))
+#define ext2fs_be32_to_cpu(x) ext2fs_swab32((x))
+#define ext2fs_cpu_to_be16(x) ext2fs_swab16((x))
+#define ext2fs_be16_to_cpu(x) ext2fs_swab16((x))
+#endif
+
+/*
+ * EXT2FS bitmap manipulation routines.
+ */
+
+/* Support for sending warning messages from the inline subroutines */
+extern const char *ext2fs_block_string;
+extern const char *ext2fs_inode_string;
+extern const char *ext2fs_mark_string;
+extern const char *ext2fs_unmark_string;
+extern const char *ext2fs_test_string;
+extern void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg,
+                              const char *description);
+extern void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap,
+                               int code, unsigned long arg);
+
+extern int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block);
+extern int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
+                                      blk_t block);
+extern int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block);
+
+extern int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode);
+extern int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+                                      ext2_ino_t inode);
+extern int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode);
+
+extern void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap,
+                                         blk_t block);
+extern void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
+                                           blk_t block);
+extern int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap,
+                                        blk_t block);
+
+extern void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+                                         ext2_ino_t inode);
+extern void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+                                           ext2_ino_t inode);
+extern int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
+                                        ext2_ino_t inode);
+extern blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap);
+extern ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap);
+extern blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap);
+extern ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap);
+
+extern void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+                                          blk_t block, int num);
+extern void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+                                            blk_t block, int num);
+extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
+                                         blk_t block, int num);
+extern void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+                                               blk_t block, int num);
+extern void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+                                                 blk_t block, int num);
+extern int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
+                                              blk_t block, int num);
+extern void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map);
+
+/* These two routines moved to gen_bitmap.c */
+extern int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+                                        __u32 bitno);
+extern int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+                                          blk_t bitno);
+/*
+ * The inline routines themselves...
+ * 
+ * If NO_INLINE_FUNCS is defined, then we won't try to do inline
+ * functions at all; they will be included as normal functions in
+ * inline.c
+ */
+#ifdef NO_INLINE_FUNCS
+#if (defined(__GNUC__) && (defined(__i386__) || defined(__i486__) || \
+                          defined(__i586__) || defined(__mc68000__) || \
+                          defined(__sparc__)))
+       /* This prevents bitops.c from trying to include the C */
+       /* function version of these functions */
+#define _EXT2_HAVE_ASM_BITOPS_
+#endif
+#endif /* NO_INLINE_FUNCS */
+
+#if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS))
+#ifdef INCLUDE_INLINE_FUNCS
+#define _INLINE_ extern
+#else
+#ifdef __GNUC__
+#define _INLINE_ extern __inline__
+#else                          /* For Watcom C */
+#define _INLINE_ extern inline
+#endif
+#endif
+
+#if ((defined __GNUC__) && !defined(_EXT2_USE_C_VERSIONS_) && \
+     (defined(__i386__) || defined(__i486__) || defined(__i586__)))
+
+#define _EXT2_HAVE_ASM_BITOPS_
+#define _EXT2_HAVE_ASM_SWAB_
+#define _EXT2_HAVE_ASM_FINDBIT_
+
+/*
+ * These are done by inline assembly for speed reasons.....
+ *
+ * All bitoperations return 0 if the bit was cleared before the
+ * operation and != 0 if it was not.  Bit 0 is the LSB of addr; bit 32
+ * is the LSB of (addr+1).
+ */
+
+/*
+ * Some hacks to defeat gcc over-optimizations..
+ */
+struct __dummy_h { unsigned long a[100]; };
+#define EXT2FS_ADDR (*(struct __dummy_h *) addr)
+#define EXT2FS_CONST_ADDR (*(const struct __dummy_h *) addr)   
+
+_INLINE_ int ext2fs_set_bit(int nr, void * addr)
+{
+       int oldbit;
+
+       __asm__ __volatile__("btsl %2,%1\n\tsbbl %0,%0"
+               :"=r" (oldbit),"=m" (EXT2FS_ADDR)
+               :"r" (nr));
+       return oldbit;
+}
+
+_INLINE_ int ext2fs_clear_bit(int nr, void * addr)
+{
+       int oldbit;
+
+       __asm__ __volatile__("btrl %2,%1\n\tsbbl %0,%0"
+               :"=r" (oldbit),"=m" (EXT2FS_ADDR)
+               :"r" (nr));
+       return oldbit;
+}
+
+_INLINE_ int ext2fs_test_bit(int nr, const void * addr)
+{
+       int oldbit;
+
+       __asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0"
+               :"=r" (oldbit)
+               :"m" (EXT2FS_CONST_ADDR),"r" (nr));
+       return oldbit;
+}
+
+#if 0
+_INLINE_ int ext2fs_find_first_bit_set(void * addr, unsigned size)
+{
+       int d0, d1, d2;
+       int res;
+
+       if (!size)
+               return 0;
+       /* This looks at memory. Mark it volatile to tell gcc not to move it around */
+       __asm__ __volatile__(
+               "cld\n\t"                            
+               "xorl %%eax,%%eax\n\t"
+               "xorl %%edx,%%edx\n\t"
+               "repe; scasl\n\t"
+               "je 1f\n\t"
+               "movl -4(%%edi),%%eax\n\t"
+               "subl $4,%%edi\n\t"
+               "bsfl %%eax,%%edx\n"
+               "1:\tsubl %%esi,%%edi\n\t"
+               "shll $3,%%edi\n\t"
+               "addl %%edi,%%edx"
+               :"=d" (res), "=&c" (d0), "=&D" (d1), "=&a" (d2)
+               :"1" ((size + 31) >> 5), "2" (addr), "S" (addr));
+       return res;
+}
+
+_INLINE_ int ext2fs_find_next_bit_set (void * addr, int size, int offset)
+{
+       unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
+       int set = 0, bit = offset & 31, res;
+       
+       if (bit) {
+               /*
+                * Look for zero in first byte
+                */
+               __asm__("bsfl %1,%0\n\t"
+                       "jne 1f\n\t"
+                       "movl $32, %0\n"
+                       "1:"
+                       : "=r" (set)
+                       : "r" (*p >> bit));
+               if (set < (32 - bit))
+                       return set + offset;
+               set = 32 - bit;
+               p++;
+       }
+       /*
+        * No bit found yet, search remaining full bytes for a bit
+        */
+       res = ext2fs_find_first_bit_set(p, size - 32 * (p - (unsigned long *) addr));
+       return (offset + set + res);
+}
+#endif
+
+#ifdef EXT2FS_ENABLE_SWAPFS
+_INLINE_ __u32 ext2fs_swab32(__u32 val)
+{
+#ifdef EXT2FS_REQUIRE_486
+       __asm__("bswap %0" : "=r" (val) : "0" (val));
+#else
+       __asm__("xchgb %b0,%h0\n\t"     /* swap lower bytes     */
+               "rorl $16,%0\n\t"       /* swap words           */
+               "xchgb %b0,%h0"         /* swap higher bytes    */
+               :"=q" (val)
+               : "0" (val));
+#endif
+       return val;
+}
+
+_INLINE_ __u16 ext2fs_swab16(__u16 val)
+{
+       __asm__("xchgb %b0,%h0"         /* swap bytes           */ \
+               : "=q" (val) \
+               :  "0" (val)); \
+               return val;
+}
+#endif
+
+#undef EXT2FS_ADDR
+
+#endif /* i386 */
+
+#ifdef __mc68000__
+
+#define _EXT2_HAVE_ASM_BITOPS_
+
+_INLINE_ int ext2fs_set_bit(int nr,void * addr)
+{
+       char retval;
+
+       __asm__ __volatile__ ("bfset %2@{%1:#1}; sne %0"
+            : "=d" (retval) : "d" (nr^7), "a" (addr));
+
+       return retval;
+}
+
+_INLINE_ int ext2fs_clear_bit(int nr, void * addr)
+{
+       char retval;
+
+       __asm__ __volatile__ ("bfclr %2@{%1:#1}; sne %0"
+            : "=d" (retval) : "d" (nr^7), "a" (addr));
+
+       return retval;
+}
+
+_INLINE_ int ext2fs_test_bit(int nr, const void * addr)
+{
+       char retval;
+
+       __asm__ __volatile__ ("bftst %2@{%1:#1}; sne %0"
+            : "=d" (retval) : "d" (nr^7), "a" (addr));
+
+       return retval;
+}
+
+#endif /* __mc68000__ */
+
+
+#if !defined(_EXT2_HAVE_ASM_SWAB_) && defined(EXT2FS_ENABLE_SWAPFS)
+
+_INLINE_ __u16 ext2fs_swab16(__u16 val)
+{
+       return (val >> 8) | (val << 8);
+}
+
+_INLINE_ __u32 ext2fs_swab32(__u32 val)
+{
+       return ((val>>24) | ((val>>8)&0xFF00) |
+               ((val<<8)&0xFF0000) | (val<<24));
+}
+
+#endif /* !_EXT2_HAVE_ASM_SWAB */
+
+#if !defined(_EXT2_HAVE_ASM_FINDBIT_)
+_INLINE_ int ext2fs_find_first_bit_set(void * addr, unsigned size)
+{
+       char    *cp = (unsigned char *) addr;
+       int     res = 0, d0;
+
+       if (!size)
+               return 0;
+
+       while ((size > res) && (*cp == 0)) {
+               cp++;
+               res += 8;
+       }
+       d0 = ffs(*cp);
+       if (d0 == 0)
+               return size;
+       
+       return res + d0 - 1;
+}
+
+_INLINE_ int ext2fs_find_next_bit_set (void * addr, int size, int offset)
+{
+       unsigned char * p;
+       int set = 0, bit = offset & 7, res = 0, d0;
+       
+       res = offset >> 3;
+       p = ((unsigned char *) addr) + res;
+       
+       if (bit) {
+               set = ffs(*p & ~((1 << bit) - 1));
+               if (set)
+                       return (offset & ~7) + set - 1;
+               p++;
+               res += 8;
+       }
+       while ((size > res) && (*p == 0)) {
+               p++;
+               res += 8;
+       }
+       d0 = ffs(*p);
+       if (d0 == 0)
+               return size;
+
+       return (res + d0 - 1);
+}
+#endif 
+
+_INLINE_ int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap,
+                                       blk_t bitno);
+
+_INLINE_ int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap,
+                                       blk_t bitno)
+{
+       if ((bitno < bitmap->start) || (bitno > bitmap->end)) {
+               ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, bitno);
+               return 0;
+       }
+       return ext2fs_test_bit(bitno - bitmap->start, bitmap->bitmap);
+}
+
+_INLINE_ int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap,
+                                      blk_t block)
+{
+       return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap)
+                                      bitmap,
+                                         block);
+}
+
+_INLINE_ int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
+                                        blk_t block)
+{
+       return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, 
+                                           block);
+}
+
+_INLINE_ int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap,
+                                      blk_t block)
+{
+       return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, 
+                                         block);
+}
+
+_INLINE_ int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+                                      ext2_ino_t inode)
+{
+       return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, 
+                                         inode);
+}
+
+_INLINE_ int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+                                        ext2_ino_t inode)
+{
+       return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, 
+                                    inode);
+}
+
+_INLINE_ int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
+                                      ext2_ino_t inode)
+{
+       return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, 
+                                         inode);
+}
+
+_INLINE_ void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap,
+                                           blk_t block)
+{
+#ifdef EXT2FS_DEBUG_FAST_OPS
+       if ((block < bitmap->start) || (block > bitmap->end)) {
+               ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block,
+                                  bitmap->description);
+               return;
+       }
+#endif 
+       ext2fs_set_bit(block - bitmap->start, bitmap->bitmap);
+}
+
+_INLINE_ void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap,
+                                             blk_t block)
+{
+#ifdef EXT2FS_DEBUG_FAST_OPS
+       if ((block < bitmap->start) || (block > bitmap->end)) {
+               ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK,
+                                  block, bitmap->description);
+               return;
+       }
+#endif
+       ext2fs_clear_bit(block - bitmap->start, bitmap->bitmap);
+}
+
+_INLINE_ int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap,
+                                           blk_t block)
+{
+#ifdef EXT2FS_DEBUG_FAST_OPS
+       if ((block < bitmap->start) || (block > bitmap->end)) {
+               ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST,
+                                  block, bitmap->description);
+               return 0;
+       }
+#endif
+       return ext2fs_test_bit(block - bitmap->start, bitmap->bitmap);
+}
+
+_INLINE_ void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+                                           ext2_ino_t inode)
+{
+#ifdef EXT2FS_DEBUG_FAST_OPS
+       if ((inode < bitmap->start) || (inode > bitmap->end)) {
+               ext2fs_warn_bitmap(EXT2_ET_BAD_INODE_MARK,
+                                  inode, bitmap->description);
+               return;
+       }
+#endif
+       ext2fs_set_bit(inode - bitmap->start, bitmap->bitmap);
+}
+
+_INLINE_ void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap,
+                                             ext2_ino_t inode)
+{
+#ifdef EXT2FS_DEBUG_FAST_OPS
+       if ((inode < bitmap->start) || (inode > bitmap->end)) {
+               ext2fs_warn_bitmap(EXT2_ET_BAD_INODE_UNMARK,
+                                  inode, bitmap->description);
+               return;
+       }
+#endif
+       ext2fs_clear_bit(inode - bitmap->start, bitmap->bitmap);
+}
+
+_INLINE_ int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap,
+                                          ext2_ino_t inode)
+{
+#ifdef EXT2FS_DEBUG_FAST_OPS
+       if ((inode < bitmap->start) || (inode > bitmap->end)) {
+               ext2fs_warn_bitmap(EXT2_ET_BAD_INODE_TEST,
+                                  inode, bitmap->description);
+               return 0;
+       }
+#endif
+       return ext2fs_test_bit(inode - bitmap->start, bitmap->bitmap);
+}
+
+_INLINE_ blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap)
+{
+       return bitmap->start;
+}
+
+_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap)
+{
+       return bitmap->start;
+}
+
+_INLINE_ blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap)
+{
+       return bitmap->end;
+}
+
+_INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap)
+{
+       return bitmap->end;
+}
+
+_INLINE_ int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
+                                           blk_t block, int num)
+{
+       int     i;
+
+       if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
+               ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST,
+                                  block, bitmap->description);
+               return 0;
+       }
+       for (i=0; i < num; i++) {
+               if (ext2fs_fast_test_block_bitmap(bitmap, block+i))
+                       return 0;
+       }
+       return 1;
+}
+
+_INLINE_ int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap,
+                                                blk_t block, int num)
+{
+       int     i;
+
+#ifdef EXT2FS_DEBUG_FAST_OPS
+       if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
+               ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST,
+                                  block, bitmap->description);
+               return 0;
+       }
+#endif
+       for (i=0; i < num; i++) {
+               if (ext2fs_fast_test_block_bitmap(bitmap, block+i))
+                       return 0;
+       }
+       return 1;
+}
+
+_INLINE_ void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+                                            blk_t block, int num)
+{
+       int     i;
+       
+       if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
+               ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block,
+                                  bitmap->description);
+               return;
+       }
+       for (i=0; i < num; i++)
+               ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap);
+}
+
+_INLINE_ void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+                                                 blk_t block, int num)
+{
+       int     i;
+       
+#ifdef EXT2FS_DEBUG_FAST_OPS
+       if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
+               ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block,
+                                  bitmap->description);
+               return;
+       }
+#endif 
+       for (i=0; i < num; i++)
+               ext2fs_set_bit(block + i - bitmap->start, bitmap->bitmap);
+}
+
+_INLINE_ void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+                                              blk_t block, int num)
+{
+       int     i;
+       
+       if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
+               ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block,
+                                  bitmap->description);
+               return;
+       }
+       for (i=0; i < num; i++)
+               ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap);
+}
+
+_INLINE_ void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap,
+                                                   blk_t block, int num)
+{
+       int     i;
+       
+#ifdef EXT2FS_DEBUG_FAST_OPS
+       if ((block < bitmap->start) || (block+num-1 > bitmap->end)) {
+               ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block,
+                                  bitmap->description);
+               return;
+       }
+#endif 
+       for (i=0; i < num; i++)
+               ext2fs_clear_bit(block + i - bitmap->start, bitmap->bitmap);
+}
+#undef _INLINE_
+#endif
+
diff --git a/e2fsprogs/ext2fs/block.c b/e2fsprogs/ext2fs/block.c
new file mode 100644 (file)
index 0000000..7685680
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ * block.c --- iterate over all blocks in an inode
+ * 
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct block_context {
+       ext2_filsys     fs;
+       int (*func)(ext2_filsys fs,
+                   blk_t       *blocknr,
+                   e2_blkcnt_t bcount,
+                   blk_t       ref_blk,
+                   int         ref_offset,
+                   void        *priv_data);
+       e2_blkcnt_t     bcount;
+       int             bsize;
+       int             flags;
+       errcode_t       errcode;
+       char    *ind_buf;
+       char    *dind_buf;
+       char    *tind_buf;
+       void    *priv_data;
+};
+
+static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
+                            int ref_offset, struct block_context *ctx)
+{
+       int     ret = 0, changed = 0;
+       int     i, flags, limit, offset;
+       blk_t   *block_nr;
+
+       limit = ctx->fs->blocksize >> 2;
+       if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+           !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
+               ret = (*ctx->func)(ctx->fs, ind_block,
+                                  BLOCK_COUNT_IND, ref_block,
+                                  ref_offset, ctx->priv_data);
+       if (!*ind_block || (ret & BLOCK_ABORT)) {
+               ctx->bcount += limit;
+               return ret;
+       }
+       if (*ind_block >= ctx->fs->super->s_blocks_count ||
+           *ind_block < ctx->fs->super->s_first_data_block) {
+               ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
+               ret |= BLOCK_ERROR;
+               return ret;
+       }
+       ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block, 
+                                            ctx->ind_buf);
+       if (ctx->errcode) {
+               ret |= BLOCK_ERROR;
+               return ret;
+       }
+
+       block_nr = (blk_t *) ctx->ind_buf;
+       offset = 0;
+       if (ctx->flags & BLOCK_FLAG_APPEND) {
+               for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
+                       flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
+                                            *ind_block, offset, 
+                                            ctx->priv_data);
+                       changed |= flags;
+                       if (flags & BLOCK_ABORT) {
+                               ret |= BLOCK_ABORT;
+                               break;
+                       }
+                       offset += sizeof(blk_t);
+               }
+       } else {
+               for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
+                       if (*block_nr == 0)
+                               continue;
+                       flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
+                                            *ind_block, offset, 
+                                            ctx->priv_data);
+                       changed |= flags;
+                       if (flags & BLOCK_ABORT) {
+                               ret |= BLOCK_ABORT;
+                               break;
+                       }
+                       offset += sizeof(blk_t);
+               }
+       }
+       if (changed & BLOCK_CHANGED) {
+               ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block,
+                                                     ctx->ind_buf);
+               if (ctx->errcode)
+                       ret |= BLOCK_ERROR | BLOCK_ABORT;
+       }
+       if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+           !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
+           !(ret & BLOCK_ABORT))
+               ret |= (*ctx->func)(ctx->fs, ind_block,
+                                   BLOCK_COUNT_IND, ref_block,
+                                   ref_offset, ctx->priv_data);
+       return ret;
+}
+       
+static int block_iterate_dind(blk_t *dind_block, blk_t ref_block,
+                             int ref_offset, struct block_context *ctx)
+{
+       int     ret = 0, changed = 0;
+       int     i, flags, limit, offset;
+       blk_t   *block_nr;
+
+       limit = ctx->fs->blocksize >> 2;
+       if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
+                           BLOCK_FLAG_DATA_ONLY)))
+               ret = (*ctx->func)(ctx->fs, dind_block,
+                                  BLOCK_COUNT_DIND, ref_block,
+                                  ref_offset, ctx->priv_data);
+       if (!*dind_block || (ret & BLOCK_ABORT)) {
+               ctx->bcount += limit*limit;
+               return ret;
+       }
+       if (*dind_block >= ctx->fs->super->s_blocks_count ||
+           *dind_block < ctx->fs->super->s_first_data_block) {
+               ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
+               ret |= BLOCK_ERROR;
+               return ret;
+       }
+       ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block, 
+                                            ctx->dind_buf);
+       if (ctx->errcode) {
+               ret |= BLOCK_ERROR;
+               return ret;
+       }
+
+       block_nr = (blk_t *) ctx->dind_buf;
+       offset = 0;
+       if (ctx->flags & BLOCK_FLAG_APPEND) {
+               for (i = 0; i < limit; i++, block_nr++) {
+                       flags = block_iterate_ind(block_nr,
+                                                 *dind_block, offset,
+                                                 ctx);
+                       changed |= flags;
+                       if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+                               ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+                               break;
+                       }
+                       offset += sizeof(blk_t);
+               }
+       } else {
+               for (i = 0; i < limit; i++, block_nr++) {
+                       if (*block_nr == 0) {
+                               ctx->bcount += limit;
+                               continue;
+                       }
+                       flags = block_iterate_ind(block_nr,
+                                                 *dind_block, offset,
+                                                 ctx);
+                       changed |= flags;
+                       if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+                               ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+                               break;
+                       }
+                       offset += sizeof(blk_t);
+               }
+       }
+       if (changed & BLOCK_CHANGED) {
+               ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block,
+                                                     ctx->dind_buf);
+               if (ctx->errcode)
+                       ret |= BLOCK_ERROR | BLOCK_ABORT;
+       }
+       if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+           !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
+           !(ret & BLOCK_ABORT))
+               ret |= (*ctx->func)(ctx->fs, dind_block,
+                                   BLOCK_COUNT_DIND, ref_block,
+                                   ref_offset, ctx->priv_data);
+       return ret;
+}
+       
+static int block_iterate_tind(blk_t *tind_block, blk_t ref_block,
+                             int ref_offset, struct block_context *ctx)
+{
+       int     ret = 0, changed = 0;
+       int     i, flags, limit, offset;
+       blk_t   *block_nr;
+
+       limit = ctx->fs->blocksize >> 2;
+       if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
+                           BLOCK_FLAG_DATA_ONLY)))
+               ret = (*ctx->func)(ctx->fs, tind_block,
+                                  BLOCK_COUNT_TIND, ref_block,
+                                  ref_offset, ctx->priv_data);
+       if (!*tind_block || (ret & BLOCK_ABORT)) {
+               ctx->bcount += limit*limit*limit;
+               return ret;
+       }
+       if (*tind_block >= ctx->fs->super->s_blocks_count ||
+           *tind_block < ctx->fs->super->s_first_data_block) {
+               ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
+               ret |= BLOCK_ERROR;
+               return ret;
+       }
+       ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block, 
+                                            ctx->tind_buf);
+       if (ctx->errcode) {
+               ret |= BLOCK_ERROR;
+               return ret;
+       }
+
+       block_nr = (blk_t *) ctx->tind_buf;
+       offset = 0;
+       if (ctx->flags & BLOCK_FLAG_APPEND) {
+               for (i = 0; i < limit; i++, block_nr++) {
+                       flags = block_iterate_dind(block_nr,
+                                                  *tind_block,
+                                                  offset, ctx);
+                       changed |= flags;
+                       if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+                               ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+                               break;
+                       }
+                       offset += sizeof(blk_t);
+               }
+       } else {
+               for (i = 0; i < limit; i++, block_nr++) {
+                       if (*block_nr == 0) {
+                               ctx->bcount += limit*limit;
+                               continue;
+                       }
+                       flags = block_iterate_dind(block_nr,
+                                                  *tind_block,
+                                                  offset, ctx);
+                       changed |= flags;
+                       if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
+                               ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
+                               break;
+                       }
+                       offset += sizeof(blk_t);
+               }
+       }
+       if (changed & BLOCK_CHANGED) {
+               ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block,
+                                                     ctx->tind_buf);
+               if (ctx->errcode)
+                       ret |= BLOCK_ERROR | BLOCK_ABORT;
+       }
+       if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
+           !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
+           !(ret & BLOCK_ABORT))
+               ret |= (*ctx->func)(ctx->fs, tind_block,
+                                   BLOCK_COUNT_TIND, ref_block,
+                                   ref_offset, ctx->priv_data);
+       
+       return ret;
+}
+       
+errcode_t ext2fs_block_iterate2(ext2_filsys fs,
+                               ext2_ino_t ino,
+                               int     flags,
+                               char *block_buf,
+                               int (*func)(ext2_filsys fs,
+                                           blk_t       *blocknr,
+                                           e2_blkcnt_t blockcnt,
+                                           blk_t       ref_blk,
+                                           int         ref_offset,
+                                           void        *priv_data),
+                               void *priv_data)
+{
+       int     i;
+       int     got_inode = 0;
+       int     ret = 0;
+       blk_t   blocks[EXT2_N_BLOCKS];  /* directory data blocks */
+       struct ext2_inode inode;
+       errcode_t       retval;
+       struct block_context ctx;
+       int     limit;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       /*
+        * Check to see if we need to limit large files
+        */
+       if (flags & BLOCK_FLAG_NO_LARGE) {
+               ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
+               if (ctx.errcode)
+                       return ctx.errcode;
+               got_inode = 1;
+               if (!LINUX_S_ISDIR(inode.i_mode) &&
+                   (inode.i_size_high != 0))
+                       return EXT2_ET_FILE_TOO_BIG;
+       }
+
+       retval = ext2fs_get_blocks(fs, ino, blocks);
+       if (retval)
+               return retval;
+
+       limit = fs->blocksize >> 2;
+
+       ctx.fs = fs;
+       ctx.func = func;
+       ctx.priv_data = priv_data;
+       ctx.flags = flags;
+       ctx.bcount = 0;
+       if (block_buf) {
+               ctx.ind_buf = block_buf;
+       } else {
+               retval = ext2fs_get_mem(fs->blocksize * 3, &ctx.ind_buf);
+               if (retval)
+                       return retval;
+       }
+       ctx.dind_buf = ctx.ind_buf + fs->blocksize;
+       ctx.tind_buf = ctx.dind_buf + fs->blocksize;
+
+       /*
+        * Iterate over the HURD translator block (if present)
+        */
+       if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
+           !(flags & BLOCK_FLAG_DATA_ONLY)) {
+               ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
+               if (ctx.errcode)
+                       goto abort_exit;
+               got_inode = 1;
+               if (inode.osd1.hurd1.h_i_translator) {
+                       ret |= (*ctx.func)(fs,
+                                          &inode.osd1.hurd1.h_i_translator,
+                                          BLOCK_COUNT_TRANSLATOR,
+                                          0, 0, priv_data);
+                       if (ret & BLOCK_ABORT)
+                               goto abort_exit;
+               }
+       }
+       
+       /*
+        * Iterate over normal data blocks
+        */
+       for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
+               if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
+                       ret |= (*ctx.func)(fs, &blocks[i],
+                                           ctx.bcount, 0, i, priv_data);
+                       if (ret & BLOCK_ABORT)
+                               goto abort_exit;
+               }
+       }
+       if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
+               ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK,
+                                        0, EXT2_IND_BLOCK, &ctx);
+               if (ret & BLOCK_ABORT)
+                       goto abort_exit;
+       } else
+               ctx.bcount += limit;
+       if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
+               ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK,
+                                         0, EXT2_DIND_BLOCK, &ctx);
+               if (ret & BLOCK_ABORT)
+                       goto abort_exit;
+       } else
+               ctx.bcount += limit * limit;
+       if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
+               ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK,
+                                         0, EXT2_TIND_BLOCK, &ctx);
+               if (ret & BLOCK_ABORT)
+                       goto abort_exit;
+       }
+
+abort_exit:
+       if (ret & BLOCK_CHANGED) {
+               if (!got_inode) {
+                       retval = ext2fs_read_inode(fs, ino, &inode);
+                       if (retval)
+                               return retval;
+               }
+               for (i=0; i < EXT2_N_BLOCKS; i++)
+                       inode.i_block[i] = blocks[i];
+               retval = ext2fs_write_inode(fs, ino, &inode);
+               if (retval)
+                       return retval;
+       }
+
+       if (!block_buf)
+               ext2fs_free_mem(&ctx.ind_buf);
+
+       return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
+}
+
+/*
+ * Emulate the old ext2fs_block_iterate function!
+ */
+
+struct xlate {
+       int (*func)(ext2_filsys fs,
+                   blk_t       *blocknr,
+                   int         bcount,
+                   void        *priv_data);
+       void *real_private;
+};
+
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt,
+                     blk_t ref_block EXT2FS_ATTR((unused)),
+                     int ref_offset EXT2FS_ATTR((unused)),
+                     void *priv_data)
+{
+       struct xlate *xl = (struct xlate *) priv_data;
+
+       return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private);
+}
+
+errcode_t ext2fs_block_iterate(ext2_filsys fs,
+                              ext2_ino_t ino,
+                              int      flags,
+                              char *block_buf,
+                              int (*func)(ext2_filsys fs,
+                                          blk_t        *blocknr,
+                                          int  blockcnt,
+                                          void *priv_data),
+                              void *priv_data)
+{
+       struct xlate xl;
+       
+       xl.real_private = priv_data;
+       xl.func = func;
+
+       return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags,
+                                    block_buf, xlate_func, &xl);
+}
+
diff --git a/e2fsprogs/ext2fs/bmap.c b/e2fsprogs/ext2fs/bmap.c
new file mode 100644 (file)
index 0000000..e840044
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * bmap.c --- logical to physical block mapping
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#if defined(__GNUC__) && !defined(NO_INLINE_FUNCS)
+#define _BMAP_INLINE_  __inline__
+#else
+#define _BMAP_INLINE_
+#endif
+
+extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino,
+                            struct ext2_inode *inode, 
+                            char *block_buf, int bmap_flags,
+                            blk_t block, blk_t *phys_blk);
+
+#define inode_bmap(inode, nr) ((inode)->i_block[(nr)])
+
+static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags, 
+                                             blk_t ind, char *block_buf, 
+                                             int *blocks_alloc,
+                                             blk_t nr, blk_t *ret_blk)
+{
+       errcode_t       retval;
+       blk_t           b;
+
+       if (!ind) {
+               if (flags & BMAP_SET)
+                       return EXT2_ET_SET_BMAP_NO_IND;
+               *ret_blk = 0;
+               return 0;
+       }
+       retval = io_channel_read_blk(fs->io, ind, 1, block_buf);
+       if (retval)
+               return retval;
+
+       if (flags & BMAP_SET) {
+               b = *ret_blk;
+#ifdef EXT2FS_ENABLE_SWAPFS
+               if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+                   (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
+                       b = ext2fs_swab32(b);
+#endif
+               ((blk_t *) block_buf)[nr] = b;
+               return io_channel_write_blk(fs->io, ind, 1, block_buf);
+       }
+
+       b = ((blk_t *) block_buf)[nr];
+
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+           (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+               b = ext2fs_swab32(b);
+#endif
+
+       if (!b && (flags & BMAP_ALLOC)) {
+               b = nr ? ((blk_t *) block_buf)[nr-1] : 0;
+               retval = ext2fs_alloc_block(fs, b,
+                                           block_buf + fs->blocksize, &b);
+               if (retval)
+                       return retval;
+
+#ifdef EXT2FS_ENABLE_SWAPFS
+               if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+                   (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
+                       ((blk_t *) block_buf)[nr] = ext2fs_swab32(b);
+               else
+#endif
+                       ((blk_t *) block_buf)[nr] = b;
+
+               retval = io_channel_write_blk(fs->io, ind, 1, block_buf);
+               if (retval)
+                       return retval;
+
+               (*blocks_alloc)++;
+       }
+
+       *ret_blk = b;
+       return 0;
+}
+
+static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags,
+                                              blk_t dind, char *block_buf, 
+                                              int *blocks_alloc,
+                                              blk_t nr, blk_t *ret_blk)
+{
+       blk_t           b;
+       errcode_t       retval;
+       blk_t           addr_per_block;
+       
+       addr_per_block = (blk_t) fs->blocksize >> 2;
+
+       retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf, 
+                               blocks_alloc, nr / addr_per_block, &b);
+       if (retval)
+               return retval;
+       retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
+                               nr % addr_per_block, ret_blk);
+       return retval;
+}
+
+static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags,
+                                              blk_t tind, char *block_buf, 
+                                              int *blocks_alloc,
+                                              blk_t nr, blk_t *ret_blk)
+{
+       blk_t           b;
+       errcode_t       retval;
+       blk_t           addr_per_block;
+       
+       addr_per_block = (blk_t) fs->blocksize >> 2;
+
+       retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf, 
+                                blocks_alloc, nr / addr_per_block, &b);
+       if (retval)
+               return retval;
+       retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
+                               nr % addr_per_block, ret_blk);
+       return retval;
+}
+
+errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
+                     char *block_buf, int bmap_flags, blk_t block,
+                     blk_t *phys_blk)
+{
+       struct ext2_inode inode_buf;
+       blk_t addr_per_block;
+       blk_t   b;
+       char    *buf = 0;
+       errcode_t       retval = 0;
+       int             blocks_alloc = 0, inode_dirty = 0;
+
+       if (!(bmap_flags & BMAP_SET))
+               *phys_blk = 0;
+
+       /* Read inode structure if necessary */
+       if (!inode) {
+               retval = ext2fs_read_inode(fs, ino, &inode_buf);
+               if (retval)
+                       return retval;
+               inode = &inode_buf;
+       }
+       addr_per_block = (blk_t) fs->blocksize >> 2;
+
+       if (!block_buf) {
+               retval = ext2fs_get_mem(fs->blocksize * 2, &buf);
+               if (retval)
+                       return retval;
+               block_buf = buf;
+       }
+
+       if (block < EXT2_NDIR_BLOCKS) {
+               if (bmap_flags & BMAP_SET) {
+                       b = *phys_blk;
+#ifdef EXT2FS_ENABLE_SWAPFS
+                       if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+                           (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+                               b = ext2fs_swab32(b);
+#endif
+                       inode_bmap(inode, block) = b;
+                       inode_dirty++;
+                       goto done;
+               }
+
+               *phys_blk = inode_bmap(inode, block);
+               b = block ? inode_bmap(inode, block-1) : 0;
+               
+               if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) {
+                       retval = ext2fs_alloc_block(fs, b, block_buf, &b);
+                       if (retval)
+                               goto done;
+                       inode_bmap(inode, block) = b;
+                       blocks_alloc++;
+                       *phys_blk = b;
+               }
+               goto done;
+       }
+       
+       /* Indirect block */
+       block -= EXT2_NDIR_BLOCKS;
+       if (block < addr_per_block) {
+               b = inode_bmap(inode, EXT2_IND_BLOCK);
+               if (!b) {
+                       if (!(bmap_flags & BMAP_ALLOC)) {
+                               if (bmap_flags & BMAP_SET)
+                                       retval = EXT2_ET_SET_BMAP_NO_IND;
+                               goto done;
+                       }
+
+                       b = inode_bmap(inode, EXT2_IND_BLOCK-1);
+                       retval = ext2fs_alloc_block(fs, b, block_buf, &b);
+                       if (retval)
+                               goto done;
+                       inode_bmap(inode, EXT2_IND_BLOCK) = b;
+                       blocks_alloc++;
+               }
+               retval = block_ind_bmap(fs, bmap_flags, b, block_buf, 
+                                       &blocks_alloc, block, phys_blk);
+               goto done;
+       }
+       
+       /* Doubly indirect block  */
+       block -= addr_per_block;
+       if (block < addr_per_block * addr_per_block) {
+               b = inode_bmap(inode, EXT2_DIND_BLOCK);
+               if (!b) {
+                       if (!(bmap_flags & BMAP_ALLOC)) {
+                               if (bmap_flags & BMAP_SET)
+                                       retval = EXT2_ET_SET_BMAP_NO_IND;
+                               goto done;
+                       }
+
+                       b = inode_bmap(inode, EXT2_IND_BLOCK);
+                       retval = ext2fs_alloc_block(fs, b, block_buf, &b);
+                       if (retval)
+                               goto done;
+                       inode_bmap(inode, EXT2_DIND_BLOCK) = b;
+                       blocks_alloc++;
+               }
+               retval = block_dind_bmap(fs, bmap_flags, b, block_buf, 
+                                        &blocks_alloc, block, phys_blk);
+               goto done;
+       }
+
+       /* Triply indirect block */
+       block -= addr_per_block * addr_per_block;
+       b = inode_bmap(inode, EXT2_TIND_BLOCK);
+       if (!b) {
+               if (!(bmap_flags & BMAP_ALLOC)) {
+                       if (bmap_flags & BMAP_SET)
+                               retval = EXT2_ET_SET_BMAP_NO_IND;
+                       goto done;
+               }
+
+               b = inode_bmap(inode, EXT2_DIND_BLOCK);
+               retval = ext2fs_alloc_block(fs, b, block_buf, &b);
+               if (retval)
+                       goto done;
+               inode_bmap(inode, EXT2_TIND_BLOCK) = b;
+               blocks_alloc++;
+       }
+       retval = block_tind_bmap(fs, bmap_flags, b, block_buf, 
+                                &blocks_alloc, block, phys_blk);
+done:
+       if (buf)
+               ext2fs_free_mem(&buf);
+       if ((retval == 0) && (blocks_alloc || inode_dirty)) {
+               inode->i_blocks += (blocks_alloc * fs->blocksize) / 512;
+               retval = ext2fs_write_inode(fs, ino, inode);
+       }
+       return retval;
+}
+
+
+
diff --git a/e2fsprogs/ext2fs/bmove.c b/e2fsprogs/ext2fs/bmove.c
new file mode 100644 (file)
index 0000000..0b2ebb1
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * bmove.c --- Move blocks around to make way for a particular
+ *     filesystem structure.
+ *
+ * Copyright (C) 1997 Theodore Ts'o.  This file may be redistributed
+ * under the terms of the GNU Public License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+struct process_block_struct {
+       ext2_ino_t              ino;
+       struct ext2_inode *     inode;
+       ext2fs_block_bitmap     reserve;
+       ext2fs_block_bitmap     alloc_map;
+       errcode_t               error;
+       char                    *buf;
+       int                     add_dir;
+       int                     flags;
+};
+
+static int process_block(ext2_filsys fs, blk_t *block_nr,
+                        e2_blkcnt_t blockcnt, blk_t ref_block,
+                        int ref_offset, void *priv_data)
+{
+       struct process_block_struct *pb;
+       errcode_t       retval;
+       int             ret;
+       blk_t           block, orig;
+
+       pb = (struct process_block_struct *) priv_data;
+       block = orig = *block_nr;
+       ret = 0;
+       
+       /*
+        * Let's see if this is one which we need to relocate
+        */
+       if (ext2fs_test_block_bitmap(pb->reserve, block)) {
+               do {
+                       if (++block >= fs->super->s_blocks_count)
+                               block = fs->super->s_first_data_block;
+                       if (block == orig) {
+                               pb->error = EXT2_ET_BLOCK_ALLOC_FAIL;
+                               return BLOCK_ABORT;
+                       }
+               } while (ext2fs_test_block_bitmap(pb->reserve, block) ||
+                        ext2fs_test_block_bitmap(pb->alloc_map, block));
+
+               retval = io_channel_read_blk(fs->io, orig, 1, pb->buf);
+               if (retval) {
+                       pb->error = retval;
+                       return BLOCK_ABORT;
+               }
+               retval = io_channel_write_blk(fs->io, block, 1, pb->buf);
+               if (retval) {
+                       pb->error = retval;
+                       return BLOCK_ABORT;
+               }
+               *block_nr = block;
+               ext2fs_mark_block_bitmap(pb->alloc_map, block);
+               ret = BLOCK_CHANGED;
+               if (pb->flags & EXT2_BMOVE_DEBUG)
+                       printf("ino=%ld, blockcnt=%lld, %d->%d\n", pb->ino,
+                              blockcnt, orig, block);
+       }
+       if (pb->add_dir) {
+               retval = ext2fs_add_dir_block(fs->dblist, pb->ino,
+                                             block, (int) blockcnt);
+               if (retval) {
+                       pb->error = retval;
+                       ret |= BLOCK_ABORT;
+               }
+       }
+       return ret;
+}
+
+errcode_t ext2fs_move_blocks(ext2_filsys fs,
+                            ext2fs_block_bitmap reserve,
+                            ext2fs_block_bitmap alloc_map,
+                            int flags)
+{
+       ext2_ino_t      ino;
+       struct ext2_inode inode;
+       errcode_t       retval;
+       struct process_block_struct pb;
+       ext2_inode_scan scan;
+       char            *block_buf;
+       
+       retval = ext2fs_open_inode_scan(fs, 0, &scan);
+       if (retval)
+               return retval;
+
+       pb.reserve = reserve;
+       pb.error = 0;
+       pb.alloc_map = alloc_map ? alloc_map : fs->block_map;
+       pb.flags = flags;
+       
+       retval = ext2fs_get_mem(fs->blocksize * 4, &block_buf);
+       if (retval)
+               return retval;
+       pb.buf = block_buf + fs->blocksize * 3;
+
+       /*
+        * If GET_DBLIST is set in the flags field, then we should
+        * gather directory block information while we're doing the
+        * block move.
+        */
+       if (flags & EXT2_BMOVE_GET_DBLIST) {
+               if (fs->dblist) {
+                       ext2fs_free_dblist(fs->dblist);
+                       fs->dblist = NULL;
+               }
+               retval = ext2fs_init_dblist(fs, 0);
+               if (retval)
+                       return retval;
+       }
+
+       retval = ext2fs_get_next_inode(scan, &ino, &inode);
+       if (retval)
+               return retval;
+       
+       while (ino) {
+               if ((inode.i_links_count == 0) ||
+                   !ext2fs_inode_has_valid_blocks(&inode))
+                       goto next;
+               
+               pb.ino = ino;
+               pb.inode = &inode;
+
+               pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) &&
+                             flags & EXT2_BMOVE_GET_DBLIST);
+
+               retval = ext2fs_block_iterate2(fs, ino, 0, block_buf,
+                                             process_block, &pb);
+               if (retval)
+                       return retval;
+               if (pb.error)
+                       return pb.error;
+
+       next:
+               retval = ext2fs_get_next_inode(scan, &ino, &inode);
+               if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
+                       goto next;
+       }
+       return 0;
+}
+
diff --git a/e2fsprogs/ext2fs/brel.h b/e2fsprogs/ext2fs/brel.h
new file mode 100644 (file)
index 0000000..be97243
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * brel.h
+ * 
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+struct ext2_block_relocate_entry {
+       blk_t   new;
+       __s16   offset;
+       __u16   flags;
+       union {
+               blk_t           block_ref;
+               ext2_ino_t      inode_ref;
+       } owner;
+};
+
+#define RELOCATE_TYPE_REF  0x0007
+#define RELOCATE_BLOCK_REF 0x0001
+#define RELOCATE_INODE_REF 0x0002
+
+typedef struct ext2_block_relocation_table *ext2_brel;
+
+struct ext2_block_relocation_table {
+       __u32   magic;
+       char    *name;
+       blk_t   current;
+       void    *priv_data;
+
+       /*
+        * Add a block relocation entry.
+        */
+       errcode_t (*put)(ext2_brel brel, blk_t old,
+                             struct ext2_block_relocate_entry *ent);
+
+       /*
+        * Get a block relocation entry.
+        */
+       errcode_t (*get)(ext2_brel brel, blk_t old,
+                             struct ext2_block_relocate_entry *ent);
+
+       /*
+        * Initialize for iterating over the block relocation entries.
+        */
+       errcode_t (*start_iter)(ext2_brel brel);
+       
+       /*
+        * The iterator function for the inode relocation entries.
+        * Returns an inode number of 0 when out of entries.
+        */
+       errcode_t (*next)(ext2_brel brel, blk_t *old,
+                         struct ext2_block_relocate_entry *ent);
+
+       /*
+        * Move the inode relocation table from one block number to
+        * another.
+        */
+       errcode_t (*move)(ext2_brel brel, blk_t old, blk_t new);
+
+       /*
+        * Remove a block relocation entry.
+        */
+       errcode_t (*delete)(ext2_brel brel, blk_t old);
+
+
+       /*
+        * Free the block relocation table.
+        */
+       errcode_t (*free)(ext2_brel brel);
+};
+
+errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block,
+                                   ext2_brel *brel);
+
+#define ext2fs_brel_put(brel, old, ent) ((brel)->put((brel), old, ent))
+#define ext2fs_brel_get(brel, old, ent) ((brel)->get((brel), old, ent))
+#define ext2fs_brel_start_iter(brel) ((brel)->start_iter((brel)))
+#define ext2fs_brel_next(brel, old, ent) ((brel)->next((brel), old, ent))
+#define ext2fs_brel_move(brel, old, new) ((brel)->move((brel), old, new))
+#define ext2fs_brel_delete(brel, old) ((brel)->delete((brel), old))
+#define ext2fs_brel_free(brel) ((brel)->free((brel)))
+
diff --git a/e2fsprogs/ext2fs/brel_ma.c b/e2fsprogs/ext2fs/brel_ma.c
new file mode 100644 (file)
index 0000000..d422bb2
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * brel_ma.c
+ * 
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * TODO: rewrite to not use a direct array!!!  (Fortunately this
+ * module isn't really used yet.)
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "brel.h"
+
+static errcode_t bma_put(ext2_brel brel, blk_t old,
+                       struct ext2_block_relocate_entry *ent);
+static errcode_t bma_get(ext2_brel brel, blk_t old,
+                       struct ext2_block_relocate_entry *ent);
+static errcode_t bma_start_iter(ext2_brel brel);
+static errcode_t bma_next(ext2_brel brel, blk_t *old,
+                        struct ext2_block_relocate_entry *ent);
+static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new);
+static errcode_t bma_delete(ext2_brel brel, blk_t old);
+static errcode_t bma_free(ext2_brel brel);
+
+struct brel_ma {
+       __u32 magic;
+       blk_t max_block;
+       struct ext2_block_relocate_entry *entries;
+};
+
+errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block,
+                                     ext2_brel *new_brel)
+{
+       ext2_brel               brel = 0;
+       errcode_t       retval;
+       struct brel_ma  *ma = 0;
+       size_t          size;
+
+       *new_brel = 0;
+
+       /*
+        * Allocate memory structures
+        */
+       retval = ext2fs_get_mem(sizeof(struct ext2_block_relocation_table),
+                               &brel);
+       if (retval)
+               goto errout;
+       memset(brel, 0, sizeof(struct ext2_block_relocation_table));
+       
+       retval = ext2fs_get_mem(strlen(name)+1, &brel->name);
+       if (retval)
+               goto errout;
+       strcpy(brel->name, name);
+       
+       retval = ext2fs_get_mem(sizeof(struct brel_ma), &ma);
+       if (retval)
+               goto errout;
+       memset(ma, 0, sizeof(struct brel_ma));
+       brel->priv_data = ma;
+       
+       size = (size_t) (sizeof(struct ext2_block_relocate_entry) *
+                        (max_block+1));
+       retval = ext2fs_get_mem(size, &ma->entries);
+       if (retval)
+               goto errout;
+       memset(ma->entries, 0, size);
+       ma->max_block = max_block;
+
+       /*
+        * Fill in the brel data structure
+        */
+       brel->put = bma_put;
+       brel->get = bma_get;
+       brel->start_iter = bma_start_iter;
+       brel->next = bma_next;
+       brel->move = bma_move;
+       brel->delete = bma_delete;
+       brel->free = bma_free;
+       
+       *new_brel = brel;
+       return 0;
+
+errout:
+       bma_free(brel);
+       return retval;
+}
+
+static errcode_t bma_put(ext2_brel brel, blk_t old,
+                       struct ext2_block_relocate_entry *ent)
+{
+       struct brel_ma  *ma;
+
+       ma = brel->priv_data;
+       if (old > ma->max_block)
+               return EXT2_ET_INVALID_ARGUMENT;
+       ma->entries[(unsigned)old] = *ent;
+       return 0;
+}
+
+static errcode_t bma_get(ext2_brel brel, blk_t old,
+                       struct ext2_block_relocate_entry *ent)
+{
+       struct brel_ma  *ma;
+
+       ma = brel->priv_data;
+       if (old > ma->max_block)
+               return EXT2_ET_INVALID_ARGUMENT;
+       if (ma->entries[(unsigned)old].new == 0)
+               return ENOENT;
+       *ent = ma->entries[old];
+       return 0;
+}
+
+static errcode_t bma_start_iter(ext2_brel brel)
+{
+       brel->current = 0;
+       return 0;
+}
+
+static errcode_t bma_next(ext2_brel brel, blk_t *old,
+                         struct ext2_block_relocate_entry *ent)
+{
+       struct brel_ma  *ma;
+
+       ma = brel->priv_data;
+       while (++brel->current < ma->max_block) {
+               if (ma->entries[(unsigned)brel->current].new == 0)
+                       continue;
+               *old = brel->current;
+               *ent = ma->entries[(unsigned)brel->current];
+               return 0;
+       }
+       *old = 0;
+       return 0;
+}
+
+static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new)
+{
+       struct brel_ma  *ma;
+
+       ma = brel->priv_data;
+       if ((old > ma->max_block) || (new > ma->max_block))
+               return EXT2_ET_INVALID_ARGUMENT;
+       if (ma->entries[(unsigned)old].new == 0)
+               return ENOENT;
+       ma->entries[(unsigned)new] = ma->entries[old];
+       ma->entries[(unsigned)old].new = 0;
+       return 0;
+}
+
+static errcode_t bma_delete(ext2_brel brel, blk_t old)
+{
+       struct brel_ma  *ma;
+
+       ma = brel->priv_data;
+       if (old > ma->max_block)
+               return EXT2_ET_INVALID_ARGUMENT;
+       if (ma->entries[(unsigned)old].new == 0)
+               return ENOENT;
+       ma->entries[(unsigned)old].new = 0;
+       return 0;
+}
+
+static errcode_t bma_free(ext2_brel brel)
+{
+       struct brel_ma  *ma;
+
+       if (!brel)
+               return 0;
+
+       ma = brel->priv_data;
+
+       if (ma) {
+               if (ma->entries)
+                       ext2fs_free_mem(&ma->entries);
+               ext2fs_free_mem(&ma);
+       }
+       if (brel->name)
+               ext2fs_free_mem(&brel->name);
+       ext2fs_free_mem(&brel);
+       return 0;
+}
diff --git a/e2fsprogs/ext2fs/check_desc.c b/e2fsprogs/ext2fs/check_desc.c
new file mode 100644 (file)
index 0000000..2a754c7
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * check_desc.c --- Check the group descriptors of an ext2 filesystem
+ * 
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * This routine sanity checks the group descriptors
+ */
+errcode_t ext2fs_check_desc(ext2_filsys fs)
+{
+       dgrp_t i;
+       blk_t block = fs->super->s_first_data_block;
+       blk_t next;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       for (i = 0; i < fs->group_desc_count; i++) {
+               next = block + fs->super->s_blocks_per_group;
+               /*
+                * Check to make sure block bitmap for group is
+                * located within the group.
+                */
+               if (fs->group_desc[i].bg_block_bitmap < block ||
+                   fs->group_desc[i].bg_block_bitmap >= next)
+                       return EXT2_ET_GDESC_BAD_BLOCK_MAP;
+               /*
+                * Check to make sure inode bitmap for group is
+                * located within the group
+                */
+               if (fs->group_desc[i].bg_inode_bitmap < block ||
+                   fs->group_desc[i].bg_inode_bitmap >= next)
+                       return EXT2_ET_GDESC_BAD_INODE_MAP;
+               /*
+                * Check to make sure inode table for group is located
+                * within the group
+                */
+               if (fs->group_desc[i].bg_inode_table < block ||
+                   ((fs->group_desc[i].bg_inode_table +
+                     fs->inode_blocks_per_group) >= next))
+                       return EXT2_ET_GDESC_BAD_INODE_TABLE;
+               
+               block = next;
+       }
+       return 0;
+}
diff --git a/e2fsprogs/ext2fs/closefs.c b/e2fsprogs/ext2fs/closefs.c
new file mode 100644 (file)
index 0000000..8539a1c
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * closefs.c --- close an ext2 filesystem
+ * 
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <string.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static int test_root(int a, int b)
+{
+       if (a == 0)
+               return 1;
+       while (1) {
+               if (a == 1)
+                       return 1;
+               if (a % b)
+                       return 0;
+               a = a / b;
+       }
+}
+
+int ext2fs_bg_has_super(ext2_filsys fs, int group_block)
+{
+       if (!(fs->super->s_feature_ro_compat &
+             EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
+               return 1;
+
+       if (test_root(group_block, 3) || (test_root(group_block, 5)) ||
+           test_root(group_block, 7))
+               return 1;
+
+       return 0;
+}
+
+int ext2fs_super_and_bgd_loc(ext2_filsys fs, 
+                            dgrp_t group,
+                            blk_t *ret_super_blk,
+                            blk_t *ret_old_desc_blk,
+                            blk_t *ret_new_desc_blk,
+                            int *ret_meta_bg)
+{
+       blk_t   group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0;
+       unsigned int meta_bg, meta_bg_size;
+       int     numblocks, has_super;
+       int     old_desc_blocks;
+
+       group_block = fs->super->s_first_data_block +
+               (group * fs->super->s_blocks_per_group);
+
+       if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+               old_desc_blocks = fs->super->s_first_meta_bg;
+       else
+               old_desc_blocks = 
+                       fs->desc_blocks + fs->super->s_reserved_gdt_blocks;
+
+       if (group == fs->group_desc_count-1) {
+               numblocks = (fs->super->s_blocks_count -
+                            fs->super->s_first_data_block) %
+                       fs->super->s_blocks_per_group;
+               if (!numblocks)
+                       numblocks = fs->super->s_blocks_per_group;
+       } else
+               numblocks = fs->super->s_blocks_per_group;
+
+       has_super = ext2fs_bg_has_super(fs, group);
+
+       if (has_super) {
+               super_blk = group_block;
+               numblocks--;
+       }
+       meta_bg_size = (fs->blocksize / sizeof (struct ext2_group_desc));
+       meta_bg = group / meta_bg_size;
+
+       if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
+           (meta_bg < fs->super->s_first_meta_bg)) {
+               if (has_super) {
+                       old_desc_blk = group_block + 1;
+                       numblocks -= old_desc_blocks;
+               }
+       } else {
+               if (((group % meta_bg_size) == 0) ||
+                   ((group % meta_bg_size) == 1) ||
+                   ((group % meta_bg_size) == (meta_bg_size-1))) {
+                       if (has_super)
+                               has_super = 1;
+                       new_desc_blk = group_block + has_super;
+                       numblocks--;
+               }
+       }
+               
+       numblocks -= 2 + fs->inode_blocks_per_group;
+
+       if (ret_super_blk)
+               *ret_super_blk = super_blk;
+       if (ret_old_desc_blk)
+               *ret_old_desc_blk = old_desc_blk;
+       if (ret_new_desc_blk)
+               *ret_new_desc_blk = new_desc_blk;
+       if (ret_meta_bg)
+               *ret_meta_bg = meta_bg;
+       return (numblocks);
+}
+
+
+/*
+ * This function forces out the primary superblock.  We need to only
+ * write out those fields which we have changed, since if the
+ * filesystem is mounted, it may have changed some of the other
+ * fields.
+ *
+ * It takes as input a superblock which has already been byte swapped
+ * (if necessary).
+ *
+ */
+static errcode_t write_primary_superblock(ext2_filsys fs,
+                                         struct ext2_super_block *super)
+{
+       __u16           *old_super, *new_super;
+       int             check_idx, write_idx, size;
+       errcode_t       retval;
+
+       if (!fs->io->manager->write_byte || !fs->orig_super) {
+               io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET);
+               retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE,
+                                             super);
+               io_channel_set_blksize(fs->io, fs->blocksize);
+               return retval;
+       }
+
+       old_super = (__u16 *) fs->orig_super;
+       new_super = (__u16 *) super;
+
+       for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) {
+               if (old_super[check_idx] == new_super[check_idx])
+                       continue;
+               write_idx = check_idx;
+               for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++)
+                       if (old_super[check_idx] == new_super[check_idx])
+                               break;
+               size = 2 * (check_idx - write_idx);
+#if 0
+               printf("Writing %d bytes starting at %d\n",
+                      size, write_idx*2);
+#endif
+               retval = io_channel_write_byte(fs->io,
+                              SUPERBLOCK_OFFSET + (2 * write_idx), size,
+                                              new_super + write_idx);
+               if (retval)
+                       return retval;
+       }
+       memcpy(fs->orig_super, super, SUPERBLOCK_SIZE);
+       return 0;
+}
+
+
+/*
+ * Updates the revision to EXT2_DYNAMIC_REV
+ */
+void ext2fs_update_dynamic_rev(ext2_filsys fs)
+{
+       struct ext2_super_block *sb = fs->super;
+
+       if (sb->s_rev_level > EXT2_GOOD_OLD_REV)
+               return;
+
+       sb->s_rev_level = EXT2_DYNAMIC_REV;
+       sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
+       sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
+       /* s_uuid is handled by e2fsck already */
+       /* other fields should be left alone */
+}
+
+static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group,
+                                   blk_t group_block,
+                                   struct ext2_super_block *super_shadow)
+{
+       dgrp_t  sgrp = group;
+       
+       if (sgrp > ((1 << 16) - 1))
+               sgrp = (1 << 16) - 1;
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if (fs->flags & EXT2_FLAG_SWAP_BYTES)
+               super_shadow->s_block_group_nr = ext2fs_swab16(sgrp);
+       else
+#endif
+               fs->super->s_block_group_nr = sgrp;
+
+       return io_channel_write_blk(fs->io, group_block, -SUPERBLOCK_SIZE, 
+                                   super_shadow);
+}
+
+
+errcode_t ext2fs_flush(ext2_filsys fs)
+{
+       dgrp_t          i,j;
+       blk_t           group_block;
+       errcode_t       retval;
+       unsigned long   fs_state;
+       struct ext2_super_block *super_shadow = 0;
+       struct ext2_group_desc *group_shadow = 0;
+       struct ext2_group_desc *s, *t;
+       char    *group_ptr;
+       int     old_desc_blocks;
+       
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       fs_state = fs->super->s_state;
+
+       fs->super->s_wtime = time(NULL);
+       fs->super->s_block_group_nr = 0;
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+               retval = EXT2_ET_NO_MEMORY;
+               retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow);
+               if (retval)
+                       goto errout;
+               retval = ext2fs_get_mem((size_t)(fs->blocksize *
+                                                fs->desc_blocks),
+                                       &group_shadow);
+               if (retval)
+                       goto errout;
+               memset(group_shadow, 0, (size_t) fs->blocksize *
+                      fs->desc_blocks);
+
+               /* swap the group descriptors */
+               for (j=0, s=fs->group_desc, t=group_shadow;
+                    j < fs->group_desc_count; j++, t++, s++) {
+                       *t = *s;
+                       ext2fs_swap_group_desc(t);
+               }
+       } else {
+               super_shadow = fs->super;
+               group_shadow = fs->group_desc;
+       }
+#else
+       super_shadow = fs->super;
+       group_shadow = fs->group_desc;
+#endif
+       
+       /*
+        * If this is an external journal device, don't write out the
+        * block group descriptors or any of the backup superblocks
+        */
+       if (fs->super->s_feature_incompat &
+           EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+               goto write_primary_superblock_only;
+
+       /*
+        * Set the state of the FS to be non-valid.  (The state has
+        * already been backed up earlier, and will be restored after
+        * we write out the backup superblocks.)
+        */
+       fs->super->s_state &= ~EXT2_VALID_FS;
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+               *super_shadow = *fs->super;
+               ext2fs_swap_super(super_shadow);
+       }
+#endif
+
+       /*
+        * Write out the master group descriptors, and the backup
+        * superblocks and group descriptors.
+        */
+       group_block = fs->super->s_first_data_block;
+       group_ptr = (char *) group_shadow;
+       if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG)
+               old_desc_blocks = fs->super->s_first_meta_bg;
+       else
+               old_desc_blocks = fs->desc_blocks;
+
+       for (i = 0; i < fs->group_desc_count; i++) {
+               blk_t   super_blk, old_desc_blk, new_desc_blk;
+               int     meta_bg;
+
+               ext2fs_super_and_bgd_loc(fs, i, &super_blk, &old_desc_blk, 
+                                        &new_desc_blk, &meta_bg);
+
+               if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) {
+                       retval = write_backup_super(fs, i, super_blk,
+                                                   super_shadow);
+                       if (retval)
+                               goto errout;
+               }
+               if (fs->flags & EXT2_FLAG_SUPER_ONLY)
+                       continue;
+               if ((old_desc_blk) && 
+                   (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) {
+                       retval = io_channel_write_blk(fs->io,
+                             old_desc_blk, old_desc_blocks, group_ptr);
+                       if (retval)
+                               goto errout;
+               }
+               if (new_desc_blk) {
+                       retval = io_channel_write_blk(fs->io, new_desc_blk,
+                               1, group_ptr + (meta_bg*fs->blocksize));
+                       if (retval)
+                               goto errout;
+               }
+       }
+       fs->super->s_block_group_nr = 0;
+       fs->super->s_state = fs_state;
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+               *super_shadow = *fs->super;
+               ext2fs_swap_super(super_shadow);
+       }
+#endif
+
+       /*
+        * If the write_bitmaps() function is present, call it to
+        * flush the bitmaps.  This is done this way so that a simple
+        * program that doesn't mess with the bitmaps doesn't need to
+        * drag in the bitmaps.c code.
+        */
+       if (fs->write_bitmaps) {
+               retval = fs->write_bitmaps(fs);
+               if (retval)
+                       goto errout;
+       }
+
+write_primary_superblock_only:
+       /*
+        * Write out master superblock.  This has to be done
+        * separately, since it is located at a fixed location
+        * (SUPERBLOCK_OFFSET).  We flush all other pending changes
+        * out to disk first, just to avoid a race condition with an
+        * insy-tinsy window....
+        */
+       retval = io_channel_flush(fs->io);
+       retval = write_primary_superblock(fs, super_shadow);
+       if (retval)
+               goto errout;
+
+       fs->flags &= ~EXT2_FLAG_DIRTY;
+
+       retval = io_channel_flush(fs->io);
+errout:
+       fs->super->s_state = fs_state;
+       if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+               if (super_shadow)
+                       ext2fs_free_mem(&super_shadow);
+               if (group_shadow)
+                       ext2fs_free_mem(&group_shadow);
+       }
+       return retval;
+}
+
+errcode_t ext2fs_close(ext2_filsys fs)
+{
+       errcode_t       retval;
+       
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (fs->flags & EXT2_FLAG_DIRTY) {
+               retval = ext2fs_flush(fs);
+               if (retval)
+                       return retval;
+       }
+       if (fs->write_bitmaps) {
+               retval = fs->write_bitmaps(fs);
+               if (retval)
+                       return retval;
+       }
+       ext2fs_free(fs);
+       return 0;
+}
+
diff --git a/e2fsprogs/ext2fs/cmp_bitmaps.c b/e2fsprogs/ext2fs/cmp_bitmaps.c
new file mode 100644 (file)
index 0000000..51cc3d0
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * cmp_bitmaps.c --- routines to compare inode and block bitmaps.
+ *
+ * Copyright (C) 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1,
+                                     ext2fs_block_bitmap bm2)
+{
+       blk_t   i;
+       
+       EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_BLOCK_BITMAP);
+       EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_BLOCK_BITMAP);
+
+       if ((bm1->start != bm2->start) ||
+           (bm1->end != bm2->end) ||
+           (memcmp(bm1->bitmap, bm2->bitmap,
+                   (size_t) (bm1->end - bm1->start)/8)))
+               return EXT2_ET_NEQ_BLOCK_BITMAP;
+
+       for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++)
+               if (ext2fs_fast_test_block_bitmap(bm1, i) !=
+                   ext2fs_fast_test_block_bitmap(bm2, i))
+                       return EXT2_ET_NEQ_BLOCK_BITMAP;
+
+       return 0;
+}
+
+errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1,
+                                     ext2fs_inode_bitmap bm2)
+{
+       ext2_ino_t      i;
+       
+       EXT2_CHECK_MAGIC(bm1, EXT2_ET_MAGIC_INODE_BITMAP);
+       EXT2_CHECK_MAGIC(bm2, EXT2_ET_MAGIC_INODE_BITMAP);
+
+       if ((bm1->start != bm2->start) ||
+           (bm1->end != bm2->end) ||
+           (memcmp(bm1->bitmap, bm2->bitmap,
+                   (size_t) (bm1->end - bm1->start)/8)))
+               return EXT2_ET_NEQ_INODE_BITMAP;
+
+       for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++)
+               if (ext2fs_fast_test_inode_bitmap(bm1, i) !=
+                   ext2fs_fast_test_inode_bitmap(bm2, i))
+                       return EXT2_ET_NEQ_INODE_BITMAP;
+
+       return 0;
+}
+
diff --git a/e2fsprogs/ext2fs/dblist.c b/e2fsprogs/ext2fs/dblist.c
new file mode 100644 (file)
index 0000000..d5833d7
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * dblist.c -- directory block list functions
+ * 
+ * Copyright 1997 by Theodore Ts'o
+ * 
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ * 
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static EXT2_QSORT_TYPE dir_block_cmp(const void *a, const void *b);
+
+/*
+ * Returns the number of directories in the filesystem as reported by
+ * the group descriptors.  Of course, the group descriptors could be
+ * wrong!
+ */
+errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs)
+{
+       dgrp_t  i;
+       ext2_ino_t      num_dirs, max_dirs;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+       
+       num_dirs = 0;
+       max_dirs = fs->super->s_inodes_per_group;
+       for (i = 0; i < fs->group_desc_count; i++) {
+               if (fs->group_desc[i].bg_used_dirs_count > max_dirs)
+                       num_dirs += max_dirs / 8;
+               else
+                       num_dirs += fs->group_desc[i].bg_used_dirs_count;
+       }
+       if (num_dirs > fs->super->s_inodes_count)
+               num_dirs = fs->super->s_inodes_count;
+
+       *ret_num_dirs = num_dirs;
+
+       return 0;
+}
+
+/*
+ * helper function for making a new directory block list (for
+ * initialize and copy).
+ */
+static errcode_t make_dblist(ext2_filsys fs, ext2_ino_t size, ext2_ino_t count,
+                            struct ext2_db_entry *list,
+                            ext2_dblist *ret_dblist)
+{
+       ext2_dblist     dblist;
+       errcode_t       retval;
+       size_t          len;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if ((ret_dblist == 0) && fs->dblist &&
+           (fs->dblist->magic == EXT2_ET_MAGIC_DBLIST))
+               return 0;
+
+       retval = ext2fs_get_mem(sizeof(struct ext2_struct_dblist), &dblist);
+       if (retval)
+               return retval;
+       memset(dblist, 0, sizeof(struct ext2_struct_dblist));
+
+       dblist->magic = EXT2_ET_MAGIC_DBLIST;
+       dblist->fs = fs;
+       if (size)
+               dblist->size = size;
+       else {
+               retval = ext2fs_get_num_dirs(fs, &dblist->size);
+               if (retval)
+                       goto cleanup;
+               dblist->size = (dblist->size * 2) + 12;
+       }
+       len = (size_t) sizeof(struct ext2_db_entry) * dblist->size;
+       dblist->count = count;
+       retval = ext2fs_get_mem(len, &dblist->list);
+       if (retval)
+               goto cleanup;
+       
+       if (list)
+               memcpy(dblist->list, list, len);
+       else
+               memset(dblist->list, 0, len);
+       if (ret_dblist)
+               *ret_dblist = dblist;
+       else
+               fs->dblist = dblist;
+       return 0;
+cleanup:
+       if (dblist)
+               ext2fs_free_mem(&dblist);
+       return retval;
+}
+
+/*
+ * Initialize a directory block list
+ */
+errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist)
+{
+       ext2_dblist     dblist;
+       errcode_t       retval;
+
+       retval = make_dblist(fs, 0, 0, 0, &dblist);
+       if (retval)
+               return retval;
+
+       dblist->sorted = 1;
+       if (ret_dblist)
+               *ret_dblist = dblist;
+       else
+               fs->dblist = dblist;
+
+       return 0;
+}
+
+/*
+ * Copy a directory block list
+ */
+errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest)
+{
+       ext2_dblist     dblist;
+       errcode_t       retval;
+
+       retval = make_dblist(src->fs, src->size, src->count, src->list,
+                            &dblist);
+       if (retval)
+               return retval;
+       dblist->sorted = src->sorted;
+       *dest = dblist;
+       return 0;
+}
+
+/*
+ * Close a directory block list
+ *
+ * (moved to closefs.c)
+ */
+
+
+/*
+ * Add a directory block to the directory block list
+ */
+errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk,
+                              int blockcnt)
+{
+       struct ext2_db_entry    *new_entry;
+       errcode_t               retval;
+       unsigned long           old_size;
+       
+       EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+       if (dblist->count >= dblist->size) {
+               old_size = dblist->size * sizeof(struct ext2_db_entry);
+               dblist->size += 100;
+               retval = ext2fs_resize_mem(old_size, (size_t) dblist->size *
+                                          sizeof(struct ext2_db_entry),
+                                          &dblist->list);
+               if (retval) {
+                       dblist->size -= 100;
+                       return retval;
+               }
+       }
+       new_entry = dblist->list + ( (int) dblist->count++);
+       new_entry->blk = blk;
+       new_entry->ino = ino;
+       new_entry->blockcnt = blockcnt;
+
+       dblist->sorted = 0;
+
+       return 0;
+}
+
+/*
+ * Change the directory block to the directory block list
+ */
+errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk,
+                              int blockcnt)
+{
+       dgrp_t                  i;
+       
+       EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+       for (i=0; i < dblist->count; i++) {
+               if ((dblist->list[i].ino != ino) ||
+                   (dblist->list[i].blockcnt != blockcnt))
+                       continue;
+               dblist->list[i].blk = blk;
+               dblist->sorted = 0;
+               return 0;
+       }
+       return EXT2_ET_DB_NOT_FOUND;
+}
+
+void ext2fs_dblist_sort(ext2_dblist dblist,
+                       EXT2_QSORT_TYPE (*sortfunc)(const void *,
+                                                   const void *))
+{
+       if (!sortfunc)
+               sortfunc = dir_block_cmp;
+       qsort(dblist->list, (size_t) dblist->count,
+             sizeof(struct ext2_db_entry), sortfunc);
+       dblist->sorted = 1;
+}
+
+/*
+ * This function iterates over the directory block list
+ */
+errcode_t ext2fs_dblist_iterate(ext2_dblist dblist,
+                               int (*func)(ext2_filsys fs,
+                                           struct ext2_db_entry *db_info,
+                                           void        *priv_data),
+                               void *priv_data)
+{
+       ext2_ino_t      i;
+       int             ret;
+       
+       EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+       if (!dblist->sorted)
+               ext2fs_dblist_sort(dblist, 0);
+       for (i=0; i < dblist->count; i++) {
+               ret = (*func)(dblist->fs, &dblist->list[(int)i], priv_data);
+               if (ret & DBLIST_ABORT)
+                       return 0;
+       }
+       return 0;
+}
+
+static EXT2_QSORT_TYPE dir_block_cmp(const void *a, const void *b)
+{
+       const struct ext2_db_entry *db_a =
+               (const struct ext2_db_entry *) a;
+       const struct ext2_db_entry *db_b =
+               (const struct ext2_db_entry *) b;
+
+       if (db_a->blk != db_b->blk)
+               return (int) (db_a->blk - db_b->blk);
+       
+       if (db_a->ino != db_b->ino)
+               return (int) (db_a->ino - db_b->ino);
+
+       return (int) (db_a->blockcnt - db_b->blockcnt);
+}
+
+int ext2fs_dblist_count(ext2_dblist dblist)
+{
+       return (int) dblist->count;
+}
diff --git a/e2fsprogs/ext2fs/dblist_dir.c b/e2fsprogs/ext2fs/dblist_dir.c
new file mode 100644 (file)
index 0000000..f2e17a6
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * dblist_dir.c --- iterate by directory entry
+ *
+ * Copyright 1997 by Theodore Ts'o
+ * 
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ * 
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info,
+                      void *priv_data);
+
+errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist,
+                                   int flags,
+                                   char        *block_buf,
+                                   int (*func)(ext2_ino_t dir,
+                                               int     entry,
+                                               struct ext2_dir_entry *dirent,
+                                               int     offset,
+                                               int     blocksize,
+                                               char    *buf,
+                                               void    *priv_data),
+                                   void *priv_data)
+{
+       errcode_t               retval;
+       struct dir_context      ctx;
+
+       EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST);
+
+       ctx.dir = 0;
+       ctx.flags = flags;
+       if (block_buf)
+               ctx.buf = block_buf;
+       else {
+               retval = ext2fs_get_mem(dblist->fs->blocksize, &ctx.buf);
+               if (retval)
+                       return retval;
+       }
+       ctx.func = func;
+       ctx.priv_data = priv_data;
+       ctx.errcode = 0;
+
+       retval = ext2fs_dblist_iterate(dblist, db_dir_proc, &ctx);
+       
+       if (!block_buf)
+               ext2fs_free_mem(&ctx.buf);
+       if (retval)
+               return retval;
+       return ctx.errcode;
+}
+
+static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info,
+                      void *priv_data)
+{
+       struct dir_context      *ctx;
+
+       ctx = (struct dir_context *) priv_data;
+       ctx->dir = db_info->ino;
+       
+       return ext2fs_process_dir_block(fs, &db_info->blk,
+                                       db_info->blockcnt, 0, 0, priv_data);
+}
diff --git a/e2fsprogs/ext2fs/dir_iterate.c b/e2fsprogs/ext2fs/dir_iterate.c
new file mode 100644 (file)
index 0000000..003c0a3
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * dir_iterate.c --- ext2fs directory iteration operations
+ * 
+ * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+/*
+ * This function checks to see whether or not a potential deleted
+ * directory entry looks valid.  What we do is check the deleted entry
+ * and each successive entry to make sure that they all look valid and
+ * that the last deleted entry ends at the beginning of the next
+ * undeleted entry.  Returns 1 if the deleted entry looks valid, zero
+ * if not valid.
+ */
+static int ext2fs_validate_entry(char *buf, int offset, int final_offset)
+{
+       struct ext2_dir_entry *dirent;
+       
+       while (offset < final_offset) {
+               dirent = (struct ext2_dir_entry *)(buf + offset);
+               offset += dirent->rec_len;
+               if ((dirent->rec_len < 8) ||
+                   ((dirent->rec_len % 4) != 0) ||
+                   (((dirent->name_len & 0xFF)+8) > dirent->rec_len))
+                       return 0;
+       }
+       return (offset == final_offset);
+}
+
+errcode_t ext2fs_dir_iterate2(ext2_filsys fs,
+                             ext2_ino_t dir,
+                             int flags,
+                             char *block_buf,
+                             int (*func)(ext2_ino_t    dir,
+                                         int           entry,
+                                         struct ext2_dir_entry *dirent,
+                                         int   offset,
+                                         int   blocksize,
+                                         char  *buf,
+                                         void  *priv_data),
+                             void *priv_data)
+{
+       struct          dir_context     ctx;
+       errcode_t       retval;
+       
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       retval = ext2fs_check_directory(fs, dir);
+       if (retval)
+               return retval;
+       
+       ctx.dir = dir;
+       ctx.flags = flags;
+       if (block_buf)
+               ctx.buf = block_buf;
+       else {
+               retval = ext2fs_get_mem(fs->blocksize, &ctx.buf);
+               if (retval)
+                       return retval;
+       }
+       ctx.func = func;
+       ctx.priv_data = priv_data;
+       ctx.errcode = 0;
+       retval = ext2fs_block_iterate2(fs, dir, 0, 0,
+                                      ext2fs_process_dir_block, &ctx);
+       if (!block_buf)
+               ext2fs_free_mem(&ctx.buf);
+       if (retval)
+               return retval;
+       return ctx.errcode;
+}
+
+struct xlate {
+       int (*func)(struct ext2_dir_entry *dirent,
+                   int         offset,
+                   int         blocksize,
+                   char        *buf,
+                   void        *priv_data);
+       void *real_private;
+};
+
+static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)),
+                     int entry EXT2FS_ATTR((unused)),
+                     struct ext2_dir_entry *dirent, int offset,
+                     int blocksize, char *buf, void *priv_data)
+{
+       struct xlate *xl = (struct xlate *) priv_data;
+
+       return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private);
+}
+
+extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, 
+                             ext2_ino_t dir,
+                             int flags,
+                             char *block_buf,
+                             int (*func)(struct ext2_dir_entry *dirent,
+                                         int   offset,
+                                         int   blocksize,
+                                         char  *buf,
+                                         void  *priv_data),
+                             void *priv_data)
+{
+       struct xlate xl;
+       
+       xl.real_private = priv_data;
+       xl.func = func;
+
+       return ext2fs_dir_iterate2(fs, dir, flags, block_buf,
+                                  xlate_func, &xl);
+}
+
+
+/*
+ * Helper function which is private to this module.  Used by
+ * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate()
+ */
+int ext2fs_process_dir_block(ext2_filsys fs,
+                            blk_t      *blocknr,
+                            e2_blkcnt_t blockcnt,
+                            blk_t      ref_block EXT2FS_ATTR((unused)),
+                            int        ref_offset EXT2FS_ATTR((unused)),
+                            void       *priv_data)
+{
+       struct dir_context *ctx = (struct dir_context *) priv_data;
+       unsigned int    offset = 0;
+       unsigned int    next_real_entry = 0;
+       int             ret = 0;
+       int             changed = 0;
+       int             do_abort = 0;
+       int             entry, size;
+       struct ext2_dir_entry *dirent;
+
+       if (blockcnt < 0)
+               return 0;
+
+       entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE;
+       
+       ctx->errcode = ext2fs_read_dir_block(fs, *blocknr, ctx->buf);
+       if (ctx->errcode)
+               return BLOCK_ABORT;
+
+       while (offset < fs->blocksize) {
+               dirent = (struct ext2_dir_entry *) (ctx->buf + offset);
+               if (((offset + dirent->rec_len) > fs->blocksize) ||
+                   (dirent->rec_len < 8) ||
+                   ((dirent->rec_len % 4) != 0) ||
+                   (((dirent->name_len & 0xFF)+8) > dirent->rec_len)) {
+                       ctx->errcode = EXT2_ET_DIR_CORRUPTED;
+                       return BLOCK_ABORT;
+               }
+               if (!dirent->inode &&
+                   !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY))
+                       goto next;
+
+               ret = (ctx->func)(ctx->dir,
+                                 (next_real_entry > offset) ?
+                                 DIRENT_DELETED_FILE : entry,
+                                 dirent, offset,
+                                 fs->blocksize, ctx->buf,
+                                 ctx->priv_data);
+               if (entry < DIRENT_OTHER_FILE)
+                       entry++;
+                       
+               if (ret & DIRENT_CHANGED)
+                       changed++;
+               if (ret & DIRENT_ABORT) {
+                       do_abort++;
+                       break;
+               }
+next:          
+               if (next_real_entry == offset)
+                       next_real_entry += dirent->rec_len;
+               if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) {
+                       size = ((dirent->name_len & 0xFF) + 11) & ~3;
+
+                       if (dirent->rec_len != size)  {
+                               unsigned int final_offset;
+
+                               final_offset = offset + dirent->rec_len;
+                               offset += size;
+                               while (offset < final_offset &&
+                                      !ext2fs_validate_entry(ctx->buf,
+                                                             offset,
+                                                             final_offset))
+                                       offset += 4;
+                               continue;
+                       }
+               }
+               offset += dirent->rec_len;
+       }
+
+       if (changed) {
+               ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf);
+               if (ctx->errcode)
+                       return BLOCK_ABORT;
+       }
+       if (do_abort)
+               return BLOCK_ABORT;
+       return 0;
+}
+
diff --git a/e2fsprogs/ext2fs/dirblock.c b/e2fsprogs/ext2fs/dirblock.c
new file mode 100644 (file)
index 0000000..ebfc72c
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * dirblock.c --- directory block routines.
+ * 
+ * Copyright (C) 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
+                                void *buf, int flags EXT2FS_ATTR((unused)))
+{
+       errcode_t       retval;
+       char            *p, *end;
+       struct ext2_dir_entry *dirent;
+       unsigned int    name_len, rec_len, do_swap;
+       
+
+       retval = io_channel_read_blk(fs->io, block, 1, buf);
+       if (retval)
+               return retval;
+#ifdef EXT2FS_ENABLE_SWAPFS
+       do_swap = (fs->flags & (EXT2_FLAG_SWAP_BYTES|
+                               EXT2_FLAG_SWAP_BYTES_READ)) != 0;
+#endif
+       p = (char *) buf;
+       end = (char *) buf + fs->blocksize;
+       while (p < end-8) {
+               dirent = (struct ext2_dir_entry *) p;
+#ifdef EXT2FS_ENABLE_SWAPFS
+               if (do_swap) {
+                       dirent->inode = ext2fs_swab32(dirent->inode);
+                       dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+                       dirent->name_len = ext2fs_swab16(dirent->name_len);
+               }
+#endif
+               name_len = dirent->name_len;
+#ifdef WORDS_BIGENDIAN
+               if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+                       dirent->name_len = ext2fs_swab16(dirent->name_len);
+#endif
+               rec_len = dirent->rec_len;
+               if ((rec_len < 8) || (rec_len % 4)) {
+                       rec_len = 8;
+                       retval = EXT2_ET_DIR_CORRUPTED;
+               }
+               if (((name_len & 0xFF) + 8) > dirent->rec_len)
+                       retval = EXT2_ET_DIR_CORRUPTED;
+               p += rec_len;
+       }
+       return retval;
+}
+
+errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block,
+                                void *buf)
+{
+       return ext2fs_read_dir_block2(fs, block, buf, 0);
+}
+
+
+errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
+                                 void *inbuf, int flags EXT2FS_ATTR((unused)))
+{
+#ifdef EXT2FS_ENABLE_SWAPFS
+       int             do_swap = 0;
+       errcode_t       retval;
+       char            *p, *end;
+       char            *buf = 0;
+       struct ext2_dir_entry *dirent;
+
+       if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+           (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
+               do_swap = 1;
+
+#ifndef WORDS_BIGENDIAN
+       if (!do_swap)
+               return io_channel_write_blk(fs->io, block, 1, (char *) inbuf);
+#endif
+
+       retval = ext2fs_get_mem(fs->blocksize, &buf);
+       if (retval)
+               return retval;
+       memcpy(buf, inbuf, fs->blocksize);
+       p = buf;
+       end = buf + fs->blocksize;
+       while (p < end) {
+               dirent = (struct ext2_dir_entry *) p;
+               if ((dirent->rec_len < 8) ||
+                   (dirent->rec_len % 4)) {
+                       ext2fs_free_mem(&buf);
+                       return (EXT2_ET_DIR_CORRUPTED);
+               }
+               p += dirent->rec_len;
+               if (do_swap) {
+                       dirent->inode = ext2fs_swab32(dirent->inode);
+                       dirent->rec_len = ext2fs_swab16(dirent->rec_len);
+                       dirent->name_len = ext2fs_swab16(dirent->name_len);
+               }
+#ifdef WORDS_BIGENDIAN 
+               if (flags & EXT2_DIRBLOCK_V2_STRUCT)
+                       dirent->name_len = ext2fs_swab16(dirent->name_len);
+#endif
+       }
+       retval = io_channel_write_blk(fs->io, block, 1, buf);
+       ext2fs_free_mem(&buf);
+       return retval;
+#else
+       return io_channel_write_blk(fs->io, block, 1, (char *) inbuf);
+#endif
+}
+
+
+errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block,
+                                void *inbuf)
+{
+       return ext2fs_write_dir_block2(fs, block, inbuf, 0);
+}
+
diff --git a/e2fsprogs/ext2fs/dirhash.c b/e2fsprogs/ext2fs/dirhash.c
new file mode 100644 (file)
index 0000000..4d18593
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * dirhash.c -- Calculate the hash of a directory entry
+ *
+ * Copyright (c) 2001  Daniel Phillips
+ * 
+ * Copyright (c) 2002 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Keyed 32-bit hash function using TEA in a Davis-Meyer function
+ *   H0 = Key
+ *   Hi = E Mi(Hi-1) + Hi-1
+ *
+ * (see Applied Cryptography, 2nd edition, p448).
+ *
+ * Jeremy Fitzhardinge <jeremy@zip.com.au> 1998
+ * 
+ * This code is made available under the terms of the GPL
+ */
+#define DELTA 0x9E3779B9
+
+static void TEA_transform(__u32 buf[4], __u32 const in[])
+{
+       __u32   sum = 0;
+       __u32   b0 = buf[0], b1 = buf[1];
+       __u32   a = in[0], b = in[1], c = in[2], d = in[3];
+       int     n = 16;
+
+       do {                                                    
+               sum += DELTA;                                   
+               b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); 
+               b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); 
+       } while(--n);
+
+       buf[0] += b0;
+       buf[1] += b1;
+}
+
+/* F, G and H are basic MD4 functions: selection, majority, parity */
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+
+/*
+ * The generic round function.  The application is so specific that
+ * we don't bother protecting all the arguments with parens, as is generally
+ * good macro practice, in favor of extra legibility.
+ * Rotation is separate from addition to prevent recomputation
+ */
+#define ROUND(f, a, b, c, d, x, s)     \
+       (a += f(b, c, d) + x, a = (a << s) | (a >> (32-s)))
+#define K1 0
+#define K2 013240474631UL
+#define K3 015666365641UL
+
+/*
+ * Basic cut-down MD4 transform.  Returns only 32 bits of result.
+ */
+static void halfMD4Transform (__u32 buf[4], __u32 const in[])
+{
+       __u32   a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+       /* Round 1 */
+       ROUND(F, a, b, c, d, in[0] + K1,  3);
+       ROUND(F, d, a, b, c, in[1] + K1,  7);
+       ROUND(F, c, d, a, b, in[2] + K1, 11);
+       ROUND(F, b, c, d, a, in[3] + K1, 19);
+       ROUND(F, a, b, c, d, in[4] + K1,  3);
+       ROUND(F, d, a, b, c, in[5] + K1,  7);
+       ROUND(F, c, d, a, b, in[6] + K1, 11);
+       ROUND(F, b, c, d, a, in[7] + K1, 19);
+
+       /* Round 2 */
+       ROUND(G, a, b, c, d, in[1] + K2,  3);
+       ROUND(G, d, a, b, c, in[3] + K2,  5);
+       ROUND(G, c, d, a, b, in[5] + K2,  9);
+       ROUND(G, b, c, d, a, in[7] + K2, 13);
+       ROUND(G, a, b, c, d, in[0] + K2,  3);
+       ROUND(G, d, a, b, c, in[2] + K2,  5);
+       ROUND(G, c, d, a, b, in[4] + K2,  9);
+       ROUND(G, b, c, d, a, in[6] + K2, 13);
+
+       /* Round 3 */
+       ROUND(H, a, b, c, d, in[3] + K3,  3);
+       ROUND(H, d, a, b, c, in[7] + K3,  9);
+       ROUND(H, c, d, a, b, in[2] + K3, 11);
+       ROUND(H, b, c, d, a, in[6] + K3, 15);
+       ROUND(H, a, b, c, d, in[1] + K3,  3);
+       ROUND(H, d, a, b, c, in[5] + K3,  9);
+       ROUND(H, c, d, a, b, in[0] + K3, 11);
+       ROUND(H, b, c, d, a, in[4] + K3, 15);
+
+       buf[0] += a;
+       buf[1] += b;
+       buf[2] += c;
+       buf[3] += d;
+}
+
+#undef ROUND
+#undef F
+#undef G
+#undef H
+#undef K1
+#undef K2
+#undef K3
+
+/* The old legacy hash */
+static ext2_dirhash_t dx_hack_hash (const char *name, int len)
+{
+       __u32 hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9;
+       while (len--) {
+               __u32 hash = hash1 + (hash0 ^ (*name++ * 7152373));
+               
+               if (hash & 0x80000000) hash -= 0x7fffffff;
+               hash1 = hash0;
+               hash0 = hash;
+       }
+       return (hash0 << 1);
+}
+
+static void str2hashbuf(const char *msg, int len, __u32 *buf, int num)
+{
+       __u32   pad, val;
+       int     i;
+
+       pad = (__u32)len | ((__u32)len << 8);
+       pad |= pad << 16;
+
+       val = pad;
+       if (len > num*4)
+               len = num * 4;
+       for (i=0; i < len; i++) {
+               if ((i % 4) == 0)
+                       val = pad;
+               val = msg[i] + (val << 8);
+               if ((i % 4) == 3) {
+                       *buf++ = val;
+                       val = pad;
+                       num--;
+               }
+       }
+       if (--num >= 0)
+               *buf++ = val;
+       while (--num >= 0)
+               *buf++ = pad;
+}
+
+/*
+ * Returns the hash of a filename.  If len is 0 and name is NULL, then
+ * this function can be used to test whether or not a hash version is
+ * supported.
+ * 
+ * The seed is an 4 longword (32 bits) "secret" which can be used to
+ * uniquify a hash.  If the seed is all zero's, then some default seed
+ * may be used.
+ * 
+ * A particular hash version specifies whether or not the seed is
+ * represented, and whether or not the returned hash is 32 bits or 64
+ * bits.  32 bit hashes will return 0 for the minor hash.
+ */
+errcode_t ext2fs_dirhash(int version, const char *name, int len,
+                        const __u32 *seed,
+                        ext2_dirhash_t *ret_hash,
+                        ext2_dirhash_t *ret_minor_hash)
+{
+       __u32   hash;
+       __u32   minor_hash = 0;
+       const char      *p;
+       int             i;
+       __u32           in[8], buf[4];
+
+       /* Initialize the default seed for the hash checksum functions */
+       buf[0] = 0x67452301;
+       buf[1] = 0xefcdab89;
+       buf[2] = 0x98badcfe;
+       buf[3] = 0x10325476;
+
+       /* Check to see if the seed is all zero's */
+       if (seed) {
+               for (i=0; i < 4; i++) {
+                       if (seed[i])
+                               break;
+               }
+               if (i < 4)
+                       memcpy(buf, seed, sizeof(buf));
+       }
+               
+       switch (version) {
+       case EXT2_HASH_LEGACY:
+               hash = dx_hack_hash(name, len);
+               break;
+       case EXT2_HASH_HALF_MD4:
+               p = name;
+               while (len > 0) {
+                       str2hashbuf(p, len, in, 8);
+                       halfMD4Transform(buf, in);
+                       len -= 32;
+                       p += 32;
+               }
+               minor_hash = buf[2];
+               hash = buf[1];
+               break;
+       case EXT2_HASH_TEA:
+               p = name;
+               while (len > 0) {
+                       str2hashbuf(p, len, in, 4);
+                       TEA_transform(buf, in);
+                       len -= 16;
+                       p += 16;
+               }
+               hash = buf[0];
+               minor_hash = buf[1];
+               break;
+       default:
+               *ret_hash = 0;
+               return EXT2_ET_DIRHASH_UNSUPP;
+       }
+       *ret_hash = hash & ~1;
+       if (ret_minor_hash)
+               *ret_minor_hash = minor_hash;
+       return 0;
+}
diff --git a/e2fsprogs/ext2fs/dosio.c b/e2fsprogs/ext2fs/dosio.c
new file mode 100644 (file)
index 0000000..d695b18
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ * dosio.c -- Disk I/O module for the ext2fs/DOS library.
+ *
+ * Copyright (c) 1997 by Theodore Ts'o.
+ * 
+ * Copyright (c) 1997 Mark Habersack
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ */
+
+#include <stdio.h>
+#include <bios.h>
+#include <string.h>
+#include <ctype.h>
+#include <io.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include <ext2fs/ext2_types.h>
+#include "utils.h"
+#include "dosio.h"
+#include "et/com_err.h"
+#include "ext2_err.h"
+#include "ext2fs/io.h"
+
+/*
+ * Some helper macros
+ */
+#define LINUX_EXT2FS       0x83
+#define LINUX_SWAP         0x82
+#define WRITE_ERR(_msg_) write(2, _msg_, strlen(_msg_))
+#define WRITE_ERR_S(_msg_) write(2, _msg_, sizeof(_msg_))
+
+/*
+ * Exported variables
+ */
+unsigned long        _dio_error;
+unsigned long        _dio_hw_error;
+
+/*
+ * Array of all opened partitions
+ */
+static PARTITION        **partitions = NULL;
+static unsigned short   npart = 0; /* Number of mapped partitions */
+static PARTITION        *active = NULL;
+
+/*
+ * I/O Manager routine prototypes
+ */
+static errcode_t dos_open(const char *dev, int flags, io_channel *channel);
+static errcode_t dos_close(io_channel channel);
+static errcode_t dos_set_blksize(io_channel channel, int blksize);
+static errcode_t dos_read_blk(io_channel channel, unsigned long block,
+                                             int count, void *buf);
+static errcode_t dos_write_blk(io_channel channel, unsigned long block,
+                               int count, const void *buf);
+static errcode_t dos_flush(io_channel channel);
+
+static struct struct_io_manager struct_dos_manager = {
+        EXT2_ET_MAGIC_IO_MANAGER,
+        "DOS I/O Manager",
+        dos_open,
+        dos_close,
+        dos_set_blksize,
+        dos_read_blk,
+        dos_write_blk,
+        dos_flush
+};
+io_manager dos_io_manager = &struct_dos_manager;
+
+/*
+ * Macro taken from unix_io.c
+ */
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+          if ((struct)->magic != (code)) return (code)
+
+/*
+ * Calculates a CHS address of a sector from its LBA
+ * offset for the given partition.
+ */
+static void lba2chs(unsigned long lba_addr, CHS *chs, PARTITION *part)
+{
+  unsigned long      abss;
+
+  chs->offset = lba_addr & 0x000001FF;
+  abss = (lba_addr >> 9) + part->start;
+  chs->cyl    = abss / (part->sects * part->heads);
+  chs->head   = (abss / part->sects) % part->heads;
+  chs->sector = (abss % part->sects) + 1;
+}
+
+#ifdef __TURBOC__
+#pragma argsused
+#endif
+/*
+ * Scans the passed partition table looking for *pno partition
+ * that has LINUX_EXT2FS type.
+ *
+ * TODO:
+ * For partition numbers >5 Linux uses DOS extended partitions -
+ * dive into them an return an appropriate entry. Also dive into
+ * extended partitions when scanning for a first Linux/ext2fs.
+ */
+static PTABLE_ENTRY *scan_partition_table(PTABLE_ENTRY *pentry,
+                                          unsigned short phys,
+                                          unsigned char *pno)
+{
+  unsigned        i;
+
+  if(*pno != 0xFF && *pno >= 5)
+     return NULL; /* We don't support extended partitions for now */
+
+  if(*pno != 0xFF)
+  {
+    if(pentry[*pno].type == LINUX_EXT2FS)
+      return &pentry[*pno];
+    else
+    {
+      if(!pentry[*pno].type)
+        *pno = 0xFE;
+      else if(pentry[*pno].type == LINUX_SWAP)
+        *pno = 0xFD;
+      return NULL;
+    }
+  }
+
+  for(i = 0; i < 4; i++)
+    if(pentry[i].type == LINUX_EXT2FS)
+    {
+      *pno = i;
+      return &pentry[i];
+    }
+
+  return NULL;
+}
+
+/*
+ * Allocate libext2fs structures associated with I/O manager
+ */
+static io_channel alloc_io_channel(PARTITION *part)
+{
+  io_channel     ioch;
+
+  ioch = (io_channel)malloc(sizeof(struct struct_io_channel));
+  if (!ioch)
+         return NULL;
+  memset(ioch, 0, sizeof(struct struct_io_channel));
+  ioch->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+  ioch->manager = dos_io_manager;
+  ioch->name = (char *)malloc(strlen(part->dev)+1);
+  if (!ioch->name) {
+         free(ioch);
+         return NULL;
+  }
+  strcpy(ioch->name, part->dev);
+  ioch->private_data = part;
+  ioch->block_size = 1024; /* The smallest ext2fs block size */
+  ioch->read_error = 0;
+  ioch->write_error = 0;
+
+  return ioch;
+}
+
+#ifdef __TURBOC__
+#pragma argsused
+#endif
+/*
+ * Open the 'name' partition, initialize all information structures
+ * we need to keep and create libext2fs I/O manager.
+ */
+static errcode_t dos_open(const char *dev, int flags, io_channel *channel)
+{
+  unsigned char  *tmp, sec[512];
+  PARTITION      *part;
+  PTABLE_ENTRY   *pent;
+  PARTITION        **newparts;
+  
+  if(!dev)
+  {
+    _dio_error = ERR_BADDEV;
+    return EXT2_ET_BAD_DEVICE_NAME;
+  }
+
+  /*
+   * First check whether the dev name is OK
+   */
+  tmp = (unsigned char*)strrchr(dev, '/');
+  if(!tmp)
+  {
+    _dio_error = ERR_BADDEV;
+    return EXT2_ET_BAD_DEVICE_NAME;
+  }
+  *tmp = 0;
+  if(strcmp(dev, "/dev"))
+  {
+    _dio_error = ERR_BADDEV;
+    return EXT2_ET_BAD_DEVICE_NAME;
+  }
+  *tmp++ = '/';
+
+  /*
+   * Check whether the partition data is already in cache
+   */
+
+  part = (PARTITION*)malloc(sizeof(PARTITION));
+  if (!part)
+         return ENOMEM;
+  {
+    int   i = 0;
+
+    for(;i < npart; i++)
+      if(!strcmp(partitions[i]->dev, dev))
+      {
+        /* Found it! Make it the active one */
+        active = partitions[i];
+        *channel = alloc_io_channel(active);
+       if (!*channel)
+               return ENOMEM;
+        return 0;
+      }
+  }
+
+  /*
+   * Drive number & optionally partn number
+   */
+  switch(tmp[0])
+  {
+    case 'h':
+    case 's':
+      part->phys = 0x80;
+      part->phys += toupper(tmp[2]) - 'A';
+      /*
+       * Do we have the partition number?
+       */
+      if(tmp[3])
+        part->pno = isdigit((int)tmp[3]) ? tmp[3] - '0' - 1: 0;
+      else
+        part->pno = 0xFF;
+      break;
+
+    case 'f':
+      if(tmp[2])
+        part->phys = isdigit((int)tmp[2]) ? tmp[2] - '0' : 0;
+      else
+        part->phys = 0x00; /* We'll assume /dev/fd0 */
+      break;
+
+    default:
+      _dio_error = ERR_BADDEV;
+      return ENODEV;
+  }
+
+  if(part->phys < 0x80)
+  {
+     /* We don't support floppies for now */
+     _dio_error = ERR_NOTSUPP;
+     return EINVAL;
+  }
+
+  part->dev = strdup(dev);
+
+  /*
+   * Get drive's geometry
+   */
+  _dio_hw_error = biosdisk(DISK_GET_GEOMETRY,
+                           part->phys,
+                           0, /* head */
+                           0, /* cylinder */
+                           1, /* sector */
+                           1, /* just one sector */
+                           sec);
+
+  if(!HW_OK())
+  {
+    _dio_error = ERR_HARDWARE;
+    if (part)
+           free(part);
+    return EFAULT;
+  }
+
+  /*
+   * Calculate the geometry
+   */
+  part->cyls  = (unsigned short)(((sec[0] >> 6) << 8) + sec[1] + 1);
+  part->heads = sec[3] + 1;
+  part->sects = sec[0] & 0x3F;
+
+  /*
+   * Now that we know all we need, let's look for the partition
+   */
+  _dio_hw_error = biosdisk(DISK_READ, part->phys, 0, 0, 1, 1, sec);
+
+  if(!HW_OK())
+  {
+    _dio_error = ERR_HARDWARE;
+    if (part)
+           free(part);
+    return EFAULT;
+  }
+
+  pent = (PTABLE_ENTRY*)&sec[0x1BE];
+  pent = scan_partition_table(pent, part->phys, &part->pno);
+
+  if(!pent)
+  {
+    _dio_error = part->pno == 0xFE ? ERR_EMPTYPART :
+                 part->pno == 0xFD ? ERR_LINUXSWAP : ERR_NOTEXT2FS;
+    if (part)
+           free(part);
+    return ENODEV;
+  }
+
+  /*
+   * Calculate the remaining figures
+   */
+  {
+    unsigned long    fsec, fhead, fcyl;
+
+    fsec = (unsigned long)(pent->start_sec & 0x3F);
+    fhead = (unsigned long)pent->start_head;
+    fcyl = ((pent->start_sec >> 6) << 8) + pent->start_cyl;
+    part->start = fsec + fhead * part->sects + fcyl *
+                  (part->heads * part->sects) - 1;
+    part->len = pent->size;
+  }
+
+  /*
+   * Add the partition to the table
+   */
+  newparts = (PARTITION**)realloc(partitions, sizeof(PARTITION) * npart);
+  if (!newparts) {
+         free(part);
+         return ENOMEM;
+  }
+  partitions = newparts;
+  partitions[npart++] = active = part;
+
+  /*
+   * Now alloc all libe2fs structures
+   */
+  *channel = alloc_io_channel(active);
+  if (!*channel)
+         return ENOMEM;
+
+  return 0;
+}
+
+static errcode_t dos_close(io_channel channel)
+{
+       if (channel->name)
+               free(channel->name);
+       if (channel)
+               free(channel);
+
+       return 0;
+}
+
+static errcode_t dos_set_blksize(io_channel channel, int blksize)
+{
+  channel->block_size = blksize;
+
+  return 0;
+}
+
+static errcode_t dos_read_blk(io_channel channel, unsigned long block,
+                                             int count, void *buf)
+{
+  PARTITION     *part;
+  size_t        size;
+  ext2_loff_t   loc;
+  CHS           chs;
+
+  EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+  part = (PARTITION*)channel->private_data;
+
+  size = (size_t)((count < 0) ? -count : count * channel->block_size);
+  loc = (ext2_loff_t) block * channel->block_size;
+
+  lba2chs(loc, &chs, part);
+  /*
+   * Potential bug here:
+   *   If DJGPP is used then reads of >18 sectors will fail!
+   *   Have to rewrite biosdisk.
+   */
+  _dio_hw_error = biosdisk(DISK_READ,
+                           part->phys,
+                           chs.head,
+                           chs.cyl,
+                           chs.sector,
+                           size < 512 ? 1 : size/512,
+                           buf);
+
+  if(!HW_OK())
+  {
+    _dio_error = ERR_HARDWARE;
+    return EFAULT;
+  }
+
+  return 0;
+}
+
+static errcode_t dos_write_blk(io_channel channel, unsigned long block,
+                               int count, const void *buf)
+{
+  PARTITION     *part;
+  size_t        size;
+  ext2_loff_t   loc;
+  CHS           chs;
+
+  EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+  part = (PARTITION*)channel->private_data;
+
+  if(count == 1)
+    size = (size_t)channel->block_size;
+  else
+  {
+    if (count < 0)
+      size = (size_t)-count;
+    else
+      size = (size_t)(count * channel->block_size);
+  }
+
+  loc = (ext2_loff_t)block * channel->block_size;
+  lba2chs(loc, &chs, part);
+  _dio_hw_error = biosdisk(DISK_WRITE,
+                           part->phys,
+                           chs.head,
+                           chs.cyl,
+                           chs.sector,
+                           size < 512 ? 1 : size/512,
+                           (void*)buf);
+
+  if(!HW_OK())
+  {
+    _dio_error = ERR_HARDWARE;
+    return EFAULT;
+  }
+
+  return 0;
+}
+
+#ifdef __TURBOC__
+#pragma argsused
+#endif
+static errcode_t dos_flush(io_channel channel)
+{
+  /*
+   * No buffers, no flush...
+   */
+  return 0;
+}
diff --git a/e2fsprogs/ext2fs/dosio.h b/e2fsprogs/ext2fs/dosio.h
new file mode 100644 (file)
index 0000000..a0d652d
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * v1.0
+ *
+ * Disk I/O include file for the ext2fs/DOS library.
+ *
+ * Copyright (c) 1997 Mark Habersack
+ * This file may be distributed under the terms of the GNU Public License.
+ *
+ */
+#ifndef __diskio_h
+#define __diskio_h
+#ifdef __TURBOC__
+#ifndef __LARGE__
+# error "ext2fs/DOS library requires LARGE model!"
+#endif
+#endif
+
+#ifdef __TURBOC__
+#include "msdos.h"
+#endif
+
+/*
+ * A helper structure used in LBA => CHS conversion
+ */
+typedef struct
+{
+  unsigned short       cyl;     /* Cylinder (or track) */
+  unsigned short       head;
+  unsigned short       sector;
+  unsigned short       offset;  /* Offset of byte within the sector */
+} CHS;
+
+/*
+ * All partition data we need is here
+ */
+typedef struct
+{
+  char                 *dev;  /* _Linux_ device name (like "/dev/hda1") */
+  unsigned char        phys;  /* Physical DOS drive number */
+  unsigned long        start; /* LBA address of partition start */
+  unsigned long        len;   /* length of partition in sectors */
+  unsigned char        pno;   /* Partition number (read from *dev) */
+
+  /* This partition's drive geometry */
+  unsigned short       cyls;
+  unsigned short       heads;
+  unsigned short       sects;
+} PARTITION;
+
+/*
+ * PC partition table entry format
+ */
+#ifdef __DJGPP__
+#pragma pack(1)
+#endif
+typedef struct
+{
+  unsigned char        active;
+  unsigned char        start_head;
+  unsigned char        start_sec;
+  unsigned char        start_cyl;
+  unsigned char        type;
+  unsigned char        end_head;
+  unsigned char        end_sec;
+  unsigned char        end_cyl;
+  unsigned long        first_sec_rel;
+  unsigned long        size;
+} PTABLE_ENTRY;
+#ifdef __DJGPP__
+#pragma pack()
+#endif
+
+/*
+ * INT 0x13 operation codes
+ */
+#define DISK_READ          0x02
+#define DISK_WRITE         0x03
+#define DISK_GET_GEOMETRY  0x08
+#define DISK_READY         0x10
+
+/*
+ * Errors to put in _dio_error
+ */
+#define ERR_BADDEV         0x00000001L
+#define ERR_HARDWARE       0x00000002L
+#define ERR_NOTSUPP        0x00000003L
+#define ERR_NOTEXT2FS      0x00000004L
+#define ERR_EMPTYPART      0x00000005L
+#define ERR_LINUXSWAP      0x00000006L
+
+/*
+ * Functions in diskio.c
+ */
+
+/*
+ * Variable contains last module's error
+ */
+extern unsigned long        _dio_error;
+
+/*
+ * This one contains last hardware error (if _dio_error == ERR_HARDWARE)
+ */
+extern unsigned long        _dio_hw_error;
+
+/*
+ * Macros to check for disk hardware errors
+ */
+#define HW_OK()             ((unsigned char)_dio_hw_error == 0x00)
+#define HW_BAD_CMD()        ((unsigned char)_dio_hw_error == 0x01)
+#define HW_NO_ADDR_MARK()   ((unsigned char)_dio_hw_error == 0x02)
+#define HW_WRITE_PROT()     ((unsigned char)_dio_hw_error == 0x03)
+#define HW_NO_SECTOR()      ((unsigned char)_dio_hw_error == 0x04)
+#define HW_RESET_FAIL()     ((unsigned char)_dio_hw_error == 0x05)
+#define HW_DISK_CHANGED()   ((unsigned char)_dio_hw_error == 0x06)
+#define HW_DRIVE_FAIL()     ((unsigned char)_dio_hw_error == 0x07)
+#define HW_DMA_OVERRUN()    ((unsigned char)_dio_hw_error == 0x08)
+#define HW_DMA_BOUNDARY()   ((unsigned char)_dio_hw_error == 0x09)
+#define HW_BAD_SECTOR()     ((unsigned char)_dio_hw_error == 0x0A)
+#define HW_BAD_TRACK()      ((unsigned char)_dio_hw_error == 0x0B)
+#define HW_UNSUPP_TRACK()   ((unsigned char)_dio_hw_error == 0x0C)
+#define HW_BAD_CRC_ECC()    ((unsigned char)_dio_hw_error == 0x10)
+#define HW_CRC_ECC_CORR()   ((unsigned char)_dio_hw_error == 0x11)
+#define HW_CONTR_FAIL()     ((unsigned char)_dio_hw_error == 0x20)
+#define HW_SEEK_FAIL()      ((unsigned char)_dio_hw_error == 0x40)
+#define HW_ATTACH_FAIL()    ((unsigned char)_dio_hw_error == 0x80)
+#define HW_DRIVE_NREADY()   ((unsigned char)_dio_hw_error == 0xAA)
+#define HW_UNDEF_ERROR()    ((unsigned char)_dio_hw_error == 0xBB)
+#define HW_WRITE_FAULT()    ((unsigned char)_dio_hw_error == 0xCC)
+#define HW_STATUS_ERROR()   ((unsigned char)_dio_hw_error == 0xE0)
+#define HW_SENSE_FAIL()     ((unsigned char)_dio_hw_error == 0xFF)
+
+
+/*
+ * Open the specified partition.
+ * String 'dev' must have a format:
+ *
+ *  /dev/{sd|hd|fd}[X]
+ *
+ * where,
+ *
+ *  only one of the option in curly braces can be used and X is an optional
+ *  partition number for the given device. If X is not specified, function
+ *  scans the drive's partition table in search for the first Linux ext2fs
+ *  partition (signature 0x83). Along the way it dives into every extended
+ *  partition encountered.
+ *  Scan ends if either (a) there are no more used partition entries, or
+ *  (b) there is no Xth partition.
+ *
+ * Routine returns 0 on success and !=0 otherwise.
+ */
+int open_partition(char *dev);
+
+#endif /* __diskio_h */
diff --git a/e2fsprogs/ext2fs/dupfs.c b/e2fsprogs/ext2fs/dupfs.c
new file mode 100644 (file)
index 0000000..f8919c2
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * dupfs.c --- duplicate a ext2 filesystem handle
+ * 
+ * Copyright (C) 1997, 1998, 2001, 2003, 2005 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <time.h>
+#include <string.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest)
+{
+       ext2_filsys     fs;
+       errcode_t       retval;
+
+       EXT2_CHECK_MAGIC(src, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+       
+       retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs);
+       if (retval)
+               return retval;
+
+       *fs = *src;
+       fs->device_name = 0;
+       fs->super = 0;
+       fs->orig_super = 0;
+       fs->group_desc = 0;
+       fs->inode_map = 0;
+       fs->block_map = 0;
+       fs->badblocks = 0;
+       fs->dblist = 0;
+
+       io_channel_bumpcount(fs->io);
+       if (fs->icache)
+               fs->icache->refcount++;
+
+       retval = ext2fs_get_mem(strlen(src->device_name)+1, &fs->device_name);
+       if (retval)
+               goto errout;
+       strcpy(fs->device_name, src->device_name);
+
+       retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super);
+       if (retval)
+               goto errout;
+       memcpy(fs->super, src->super, SUPERBLOCK_SIZE);
+
+       retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super);
+       if (retval)
+               goto errout;
+       memcpy(fs->orig_super, src->orig_super, SUPERBLOCK_SIZE);
+
+       retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize,
+                               &fs->group_desc);
+       if (retval)
+               goto errout;
+       memcpy(fs->group_desc, src->group_desc,
+              (size_t) fs->desc_blocks * fs->blocksize);
+
+       if (src->inode_map) {
+               retval = ext2fs_copy_bitmap(src->inode_map, &fs->inode_map);
+               if (retval)
+                       goto errout;
+       }
+       if (src->block_map) {
+               retval = ext2fs_copy_bitmap(src->block_map, &fs->block_map);
+               if (retval)
+                       goto errout;
+       }
+       if (src->badblocks) {
+               retval = ext2fs_badblocks_copy(src->badblocks, &fs->badblocks);
+               if (retval)
+                       goto errout;
+       }
+       if (src->dblist) {
+               retval = ext2fs_copy_dblist(src->dblist, &fs->dblist);
+               if (retval)
+                       goto errout;
+       }
+       *dest = fs;
+       return 0;
+errout:
+       ext2fs_free(fs);
+       return retval;
+       
+}
+
diff --git a/e2fsprogs/ext2fs/e2image.h b/e2fsprogs/ext2fs/e2image.h
new file mode 100644 (file)
index 0000000..e12b7d6
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * e2image.h --- header file describing the ext2 image format
+ * 
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * Note: this uses the POSIX IO interfaces, unlike most of the other
+ * functions in this library.  So sue me.  
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+
+struct ext2_image_hdr {
+       __u32   magic_number;   /* This must be EXT2_ET_MAGIC_E2IMAGE */
+       char    magic_descriptor[16]; /* "Ext2 Image 1.0", w/ null padding */
+       char    fs_hostname[64];/* Hostname of machine of image */
+       char    fs_netaddr[32]; /* Network address */
+       __u32   fs_netaddr_type;/* 0 = IPV4, 1 = IPV6, etc. */
+       __u32   fs_device;      /* Device number of image */
+       char    fs_device_name[64]; /* Device name */
+       char    fs_uuid[16];    /* UUID of filesystem */
+       __u32   fs_blocksize;   /* Block size of the filesystem */
+       __u32   fs_reserved[8];
+       
+       __u32   image_device;   /* Device number of image file */
+       __u32   image_inode;    /* Inode number of image file */
+       __u32   image_time;     /* Time of image creation */
+       __u32   image_reserved[8];
+
+       __u32   offset_super;   /* Byte offset of the sb and descriptors */
+       __u32   offset_inode;   /* Byte offset of the inode table  */
+       __u32   offset_inodemap; /* Byte offset of the inode bitmaps */
+       __u32   offset_blockmap; /* Byte offset of the inode bitmaps */
+       __u32   offset_reserved[8];
+};
+
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
diff --git a/e2fsprogs/ext2fs/expanddir.c b/e2fsprogs/ext2fs/expanddir.c
new file mode 100644 (file)
index 0000000..10a5149
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * expand.c --- expand an ext2fs directory
+ * 
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999  Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct expand_dir_struct {
+       int             done;
+       int             newblocks;
+       errcode_t       err;
+};
+
+static int expand_dir_proc(ext2_filsys fs,
+                          blk_t        *blocknr,
+                          e2_blkcnt_t  blockcnt,
+                          blk_t        ref_block EXT2FS_ATTR((unused)),
+                          int          ref_offset EXT2FS_ATTR((unused)),
+                          void         *priv_data)
+{
+       struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
+       blk_t   new_blk;
+       static blk_t    last_blk = 0;
+       char            *block;
+       errcode_t       retval;
+       
+       if (*blocknr) {
+               last_blk = *blocknr;
+               return 0;
+       }
+       retval = ext2fs_new_block(fs, last_blk, 0, &new_blk);
+       if (retval) {
+               es->err = retval;
+               return BLOCK_ABORT;
+       }
+       if (blockcnt > 0) {
+               retval = ext2fs_new_dir_block(fs, 0, 0, &block);
+               if (retval) {
+                       es->err = retval;
+                       return BLOCK_ABORT;
+               }
+               es->done = 1;
+               retval = ext2fs_write_dir_block(fs, new_blk, block);
+       } else {
+               retval = ext2fs_get_mem(fs->blocksize, &block);
+               if (retval) {
+                       es->err = retval;
+                       return BLOCK_ABORT;
+               }
+               memset(block, 0, fs->blocksize);
+               retval = io_channel_write_blk(fs->io, new_blk, 1, block);
+       }       
+       if (retval) {
+               es->err = retval;
+               return BLOCK_ABORT;
+       }
+       ext2fs_free_mem(&block);
+       *blocknr = new_blk;
+       ext2fs_block_alloc_stats(fs, new_blk, +1);
+       es->newblocks++;
+
+       if (es->done)
+               return (BLOCK_CHANGED | BLOCK_ABORT);
+       else
+               return BLOCK_CHANGED;
+}
+
+errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir)
+{
+       errcode_t       retval;
+       struct expand_dir_struct es;
+       struct ext2_inode       inode;
+       
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (!(fs->flags & EXT2_FLAG_RW))
+               return EXT2_ET_RO_FILSYS;
+
+       if (!fs->block_map)
+               return EXT2_ET_NO_BLOCK_BITMAP;
+
+       retval = ext2fs_check_directory(fs, dir);
+       if (retval)
+               return retval;
+       
+       es.done = 0;
+       es.err = 0;
+       es.newblocks = 0;
+       
+       retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND,
+                                      0, expand_dir_proc, &es);
+
+       if (es.err)
+               return es.err;
+       if (!es.done)
+               return EXT2_ET_EXPAND_DIR_ERR;
+
+       /*
+        * Update the size and block count fields in the inode.
+        */
+       retval = ext2fs_read_inode(fs, dir, &inode);
+       if (retval)
+               return retval;
+       
+       inode.i_size += fs->blocksize;
+       inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
+
+       retval = ext2fs_write_inode(fs, dir, &inode);
+       if (retval)
+               return retval;
+
+       return 0;
+}
diff --git a/e2fsprogs/ext2fs/ext2_err.h b/e2fsprogs/ext2fs/ext2_err.h
new file mode 100644 (file)
index 0000000..57b44e1
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * ext2_err.h:
+ * This file is automatically generated; please do not edit it.
+ */
+
+#include <et/com_err.h>
+
+#define EXT2_ET_BASE                             (2133571328L)
+#define EXT2_ET_MAGIC_EXT2FS_FILSYS              (2133571329L)
+#define EXT2_ET_MAGIC_BADBLOCKS_LIST             (2133571330L)
+#define EXT2_ET_MAGIC_BADBLOCKS_ITERATE          (2133571331L)
+#define EXT2_ET_MAGIC_INODE_SCAN                 (2133571332L)
+#define EXT2_ET_MAGIC_IO_CHANNEL                 (2133571333L)
+#define EXT2_ET_MAGIC_UNIX_IO_CHANNEL            (2133571334L)
+#define EXT2_ET_MAGIC_IO_MANAGER                 (2133571335L)
+#define EXT2_ET_MAGIC_BLOCK_BITMAP               (2133571336L)
+#define EXT2_ET_MAGIC_INODE_BITMAP               (2133571337L)
+#define EXT2_ET_MAGIC_GENERIC_BITMAP             (2133571338L)
+#define EXT2_ET_MAGIC_TEST_IO_CHANNEL            (2133571339L)
+#define EXT2_ET_MAGIC_DBLIST                     (2133571340L)
+#define EXT2_ET_MAGIC_ICOUNT                     (2133571341L)
+#define EXT2_ET_MAGIC_PQ_IO_CHANNEL              (2133571342L)
+#define EXT2_ET_MAGIC_EXT2_FILE                  (2133571343L)
+#define EXT2_ET_MAGIC_E2IMAGE                    (2133571344L)
+#define EXT2_ET_MAGIC_INODE_IO_CHANNEL           (2133571345L)
+#define EXT2_ET_MAGIC_RESERVED_9                 (2133571346L)
+#define EXT2_ET_BAD_MAGIC                        (2133571347L)
+#define EXT2_ET_REV_TOO_HIGH                     (2133571348L)
+#define EXT2_ET_RO_FILSYS                        (2133571349L)
+#define EXT2_ET_GDESC_READ                       (2133571350L)
+#define EXT2_ET_GDESC_WRITE                      (2133571351L)
+#define EXT2_ET_GDESC_BAD_BLOCK_MAP              (2133571352L)
+#define EXT2_ET_GDESC_BAD_INODE_MAP              (2133571353L)
+#define EXT2_ET_GDESC_BAD_INODE_TABLE            (2133571354L)
+#define EXT2_ET_INODE_BITMAP_WRITE               (2133571355L)
+#define EXT2_ET_INODE_BITMAP_READ                (2133571356L)
+#define EXT2_ET_BLOCK_BITMAP_WRITE               (2133571357L)
+#define EXT2_ET_BLOCK_BITMAP_READ                (2133571358L)
+#define EXT2_ET_INODE_TABLE_WRITE                (2133571359L)
+#define EXT2_ET_INODE_TABLE_READ                 (2133571360L)
+#define EXT2_ET_NEXT_INODE_READ                  (2133571361L)
+#define EXT2_ET_UNEXPECTED_BLOCK_SIZE            (2133571362L)
+#define EXT2_ET_DIR_CORRUPTED                    (2133571363L)
+#define EXT2_ET_SHORT_READ                       (2133571364L)
+#define EXT2_ET_SHORT_WRITE                      (2133571365L)
+#define EXT2_ET_DIR_NO_SPACE                     (2133571366L)
+#define EXT2_ET_NO_INODE_BITMAP                  (2133571367L)
+#define EXT2_ET_NO_BLOCK_BITMAP                  (2133571368L)
+#define EXT2_ET_BAD_INODE_NUM                    (2133571369L)
+#define EXT2_ET_BAD_BLOCK_NUM                    (2133571370L)
+#define EXT2_ET_EXPAND_DIR_ERR                   (2133571371L)
+#define EXT2_ET_TOOSMALL                         (2133571372L)
+#define EXT2_ET_BAD_BLOCK_MARK                   (2133571373L)
+#define EXT2_ET_BAD_BLOCK_UNMARK                 (2133571374L)
+#define EXT2_ET_BAD_BLOCK_TEST                   (2133571375L)
+#define EXT2_ET_BAD_INODE_MARK                   (2133571376L)
+#define EXT2_ET_BAD_INODE_UNMARK                 (2133571377L)
+#define EXT2_ET_BAD_INODE_TEST                   (2133571378L)
+#define EXT2_ET_FUDGE_BLOCK_BITMAP_END           (2133571379L)
+#define EXT2_ET_FUDGE_INODE_BITMAP_END           (2133571380L)
+#define EXT2_ET_BAD_IND_BLOCK                    (2133571381L)
+#define EXT2_ET_BAD_DIND_BLOCK                   (2133571382L)
+#define EXT2_ET_BAD_TIND_BLOCK                   (2133571383L)
+#define EXT2_ET_NEQ_BLOCK_BITMAP                 (2133571384L)
+#define EXT2_ET_NEQ_INODE_BITMAP                 (2133571385L)
+#define EXT2_ET_BAD_DEVICE_NAME                  (2133571386L)
+#define EXT2_ET_MISSING_INODE_TABLE              (2133571387L)
+#define EXT2_ET_CORRUPT_SUPERBLOCK               (2133571388L)
+#define EXT2_ET_BAD_GENERIC_MARK                 (2133571389L)
+#define EXT2_ET_BAD_GENERIC_UNMARK               (2133571390L)
+#define EXT2_ET_BAD_GENERIC_TEST                 (2133571391L)
+#define EXT2_ET_SYMLINK_LOOP                     (2133571392L)
+#define EXT2_ET_CALLBACK_NOTHANDLED              (2133571393L)
+#define EXT2_ET_BAD_BLOCK_IN_INODE_TABLE         (2133571394L)
+#define EXT2_ET_UNSUPP_FEATURE                   (2133571395L)
+#define EXT2_ET_RO_UNSUPP_FEATURE                (2133571396L)
+#define EXT2_ET_LLSEEK_FAILED                    (2133571397L)
+#define EXT2_ET_NO_MEMORY                        (2133571398L)
+#define EXT2_ET_INVALID_ARGUMENT                 (2133571399L)
+#define EXT2_ET_BLOCK_ALLOC_FAIL                 (2133571400L)
+#define EXT2_ET_INODE_ALLOC_FAIL                 (2133571401L)
+#define EXT2_ET_NO_DIRECTORY                     (2133571402L)
+#define EXT2_ET_TOO_MANY_REFS                    (2133571403L)
+#define EXT2_ET_FILE_NOT_FOUND                   (2133571404L)
+#define EXT2_ET_FILE_RO                          (2133571405L)
+#define EXT2_ET_DB_NOT_FOUND                     (2133571406L)
+#define EXT2_ET_DIR_EXISTS                       (2133571407L)
+#define EXT2_ET_UNIMPLEMENTED                    (2133571408L)
+#define EXT2_ET_CANCEL_REQUESTED                 (2133571409L)
+#define EXT2_ET_FILE_TOO_BIG                     (2133571410L)
+#define EXT2_ET_JOURNAL_NOT_BLOCK                (2133571411L)
+#define EXT2_ET_NO_JOURNAL_SB                    (2133571412L)
+#define EXT2_ET_JOURNAL_TOO_SMALL                (2133571413L)
+#define EXT2_ET_JOURNAL_UNSUPP_VERSION           (2133571414L)
+#define EXT2_ET_LOAD_EXT_JOURNAL                 (2133571415L)
+#define EXT2_ET_NO_JOURNAL                       (2133571416L)
+#define EXT2_ET_DIRHASH_UNSUPP                   (2133571417L)
+#define EXT2_ET_BAD_EA_BLOCK_NUM                 (2133571418L)
+#define EXT2_ET_TOO_MANY_INODES                  (2133571419L)
+#define EXT2_ET_NOT_IMAGE_FILE                   (2133571420L)
+#define EXT2_ET_RES_GDT_BLOCKS                   (2133571421L)
+#define EXT2_ET_RESIZE_INODE_CORRUPT             (2133571422L)
+#define EXT2_ET_SET_BMAP_NO_IND                  (2133571423L)
+
+#if 0
+extern const struct error_table et_ext2_error_table;
+extern void initialize_ext2_error_table(void);
+
+/* For compatibility with Heimdal */
+extern void initialize_ext2_error_table_r(struct et_list **list);
+
+#define ERROR_TABLE_BASE_ext2 (2133571328L)
+
+/* for compatibility with older versions... */
+#define init_ext2_err_tbl initialize_ext2_error_table
+#define ext2_err_base ERROR_TABLE_BASE_ext2
+#endif
diff --git a/e2fsprogs/ext2fs/ext2_ext_attr.h b/e2fsprogs/ext2fs/ext2_ext_attr.h
new file mode 100644 (file)
index 0000000..23444c5
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+  File: linux/ext2_ext_attr.h
+
+  On-disk format of extended attributes for the ext2 filesystem.
+
+  (C) 2000 Andreas Gruenbacher, <a.gruenbacher@computer.org>
+*/
+
+/* Magic value in attribute blocks */
+#define EXT2_EXT_ATTR_MAGIC_v1         0xEA010000
+#define EXT2_EXT_ATTR_MAGIC            0xEA020000
+
+/* Maximum number of references to one attribute block */
+#define EXT2_EXT_ATTR_REFCOUNT_MAX     1024
+
+struct ext2_ext_attr_header {
+       __u32   h_magic;        /* magic number for identification */
+       __u32   h_refcount;     /* reference count */
+       __u32   h_blocks;       /* number of disk blocks used */
+       __u32   h_hash;         /* hash value of all attributes */
+       __u32   h_reserved[4];  /* zero right now */
+};
+
+struct ext2_ext_attr_entry {
+       __u8    e_name_len;     /* length of name */
+       __u8    e_name_index;   /* attribute name index */
+       __u16   e_value_offs;   /* offset in disk block of value */
+       __u32   e_value_block;  /* disk block attribute is stored on (n/i) */
+       __u32   e_value_size;   /* size of attribute value */
+       __u32   e_hash;         /* hash value of name and value */
+#if 0
+       char    e_name[0];      /* attribute name */
+#endif
+};
+
+#define EXT2_EXT_ATTR_PAD_BITS         2
+#define EXT2_EXT_ATTR_PAD              (1<<EXT2_EXT_ATTR_PAD_BITS)
+#define EXT2_EXT_ATTR_ROUND            (EXT2_EXT_ATTR_PAD-1)
+#define EXT2_EXT_ATTR_LEN(name_len) \
+       (((name_len) + EXT2_EXT_ATTR_ROUND + \
+       sizeof(struct ext2_ext_attr_entry)) & ~EXT2_EXT_ATTR_ROUND)
+#define EXT2_EXT_ATTR_NEXT(entry) \
+       ( (struct ext2_ext_attr_entry *)( \
+         (char *)(entry) + EXT2_EXT_ATTR_LEN((entry)->e_name_len)) )
+#define EXT2_EXT_ATTR_SIZE(size) \
+       (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND)
+#define EXT2_EXT_IS_LAST_ENTRY(entry) (*((__u32 *)(entry)) == 0UL)
+#define EXT2_EXT_ATTR_NAME(entry) \
+       (((char *) (entry)) + sizeof(struct ext2_ext_attr_entry))
+#define EXT2_XATTR_LEN(name_len) \
+       (((name_len) + EXT2_EXT_ATTR_ROUND + \
+       sizeof(struct ext2_xattr_entry)) & ~EXT2_EXT_ATTR_ROUND)
+#define EXT2_XATTR_SIZE(size) \
+       (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND)
+
+#ifdef __KERNEL__
+# ifdef CONFIG_EXT2_FS_EXT_ATTR
+extern int ext2_get_ext_attr(struct inode *, const char *, char *, size_t, int);
+extern int ext2_set_ext_attr(struct inode *, const char *, char *, size_t, int);
+extern void ext2_ext_attr_free_inode(struct inode *inode);
+extern void ext2_ext_attr_put_super(struct super_block *sb);
+extern int ext2_ext_attr_init(void);
+extern void ext2_ext_attr_done(void);
+# else
+#  define ext2_get_ext_attr NULL
+#  define ext2_set_ext_attr NULL
+# endif
+#endif  /* __KERNEL__ */
+
diff --git a/e2fsprogs/ext2fs/ext2_fs.h b/e2fsprogs/ext2fs/ext2_fs.h
new file mode 100644 (file)
index 0000000..ff615c4
--- /dev/null
@@ -0,0 +1,644 @@
+/*
+ *  linux/include/linux/ext2_fs.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ *  from
+ *
+ *  linux/include/linux/minix_fs.h
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#ifndef _LINUX_EXT2_FS_H
+#define _LINUX_EXT2_FS_H
+
+#include <ext2fs/ext2_types.h>         /* Changed from linux/types.h */
+
+/*
+ * The second extended filesystem constants/structures
+ */
+
+/*
+ * Define EXT2FS_DEBUG to produce debug messages
+ */
+#undef EXT2FS_DEBUG
+
+/*
+ * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files
+ */
+#define EXT2_PREALLOCATE
+#define EXT2_DEFAULT_PREALLOC_BLOCKS   8
+
+/*
+ * The second extended file system version
+ */
+#define EXT2FS_DATE            "95/08/09"
+#define EXT2FS_VERSION         "0.5b"
+
+/*
+ * Special inode numbers
+ */
+#define EXT2_BAD_INO            1      /* Bad blocks inode */
+#define EXT2_ROOT_INO           2      /* Root inode */
+#define EXT2_ACL_IDX_INO        3      /* ACL inode */
+#define EXT2_ACL_DATA_INO       4      /* ACL inode */
+#define EXT2_BOOT_LOADER_INO    5      /* Boot loader inode */
+#define EXT2_UNDEL_DIR_INO      6      /* Undelete directory inode */
+#define EXT2_RESIZE_INO                 7      /* Reserved group descriptors inode */
+#define EXT2_JOURNAL_INO        8      /* Journal inode */
+
+/* First non-reserved inode for old ext2 filesystems */
+#define EXT2_GOOD_OLD_FIRST_INO        11
+
+/*
+ * The second extended file system magic number
+ */
+#define EXT2_SUPER_MAGIC       0xEF53
+
+#ifdef __KERNEL__
+#define EXT2_SB(sb)    (&((sb)->u.ext2_sb))
+#else
+/* Assume that user mode programs are passing in an ext2fs superblock, not
+ * a kernel struct super_block.  This will allow us to call the feature-test
+ * macros from user land. */
+#define EXT2_SB(sb)    (sb)
+#endif
+
+/*
+ * Maximal count of links to a file
+ */
+#define EXT2_LINK_MAX          32000
+
+/*
+ * Macro-instructions used to manage several block sizes
+ */
+#define EXT2_MIN_BLOCK_LOG_SIZE                10      /* 1024 */
+#define EXT2_MAX_BLOCK_LOG_SIZE                16      /* 65536 */
+#define EXT2_MIN_BLOCK_SIZE    (1 << EXT2_MIN_BLOCK_LOG_SIZE)
+#define EXT2_MAX_BLOCK_SIZE    (1 << EXT2_MAX_BLOCK_LOG_SIZE)
+#ifdef __KERNEL__
+#define EXT2_BLOCK_SIZE(s)     ((s)->s_blocksize)
+#define EXT2_BLOCK_SIZE_BITS(s)        ((s)->s_blocksize_bits)
+#define EXT2_ADDR_PER_BLOCK_BITS(s)    (EXT2_SB(s)->addr_per_block_bits)
+#define EXT2_INODE_SIZE(s)     (EXT2_SB(s)->s_inode_size)
+#define EXT2_FIRST_INO(s)      (EXT2_SB(s)->s_first_ino)
+#else
+#define EXT2_BLOCK_SIZE(s)     (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
+#define EXT2_BLOCK_SIZE_BITS(s)        ((s)->s_log_block_size + 10)
+#define EXT2_INODE_SIZE(s)     (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+                                EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size)
+#define EXT2_FIRST_INO(s)      (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \
+                                EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino)
+#endif
+#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32))
+
+/*
+ * Macro-instructions used to manage fragments
+ */
+#define EXT2_MIN_FRAG_SIZE             EXT2_MIN_BLOCK_SIZE
+#define EXT2_MAX_FRAG_SIZE             EXT2_MAX_BLOCK_SIZE
+#define EXT2_MIN_FRAG_LOG_SIZE         EXT2_MIN_BLOCK_LOG_SIZE
+#ifdef __KERNEL__
+# define EXT2_FRAG_SIZE(s)             (EXT2_SB(s)->s_frag_size)
+# define EXT2_FRAGS_PER_BLOCK(s)       (EXT2_SB(s)->s_frags_per_block)
+#else
+# define EXT2_FRAG_SIZE(s)             (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size)
+# define EXT2_FRAGS_PER_BLOCK(s)       (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
+#endif
+
+/*
+ * ACL structures
+ */
+struct ext2_acl_header /* Header of Access Control Lists */
+{
+       __u32   aclh_size;
+       __u32   aclh_file_count;
+       __u32   aclh_acle_count;
+       __u32   aclh_first_acle;
+};
+
+struct ext2_acl_entry  /* Access Control List Entry */
+{
+       __u32   acle_size;
+       __u16   acle_perms;     /* Access permissions */
+       __u16   acle_type;      /* Type of entry */
+       __u16   acle_tag;       /* User or group identity */
+       __u16   acle_pad1;
+       __u32   acle_next;      /* Pointer on next entry for the */
+                                       /* same inode or on next free entry */
+};
+
+/*
+ * Structure of a blocks group descriptor
+ */
+struct ext2_group_desc
+{
+       __u32   bg_block_bitmap;                /* Blocks bitmap block */
+       __u32   bg_inode_bitmap;                /* Inodes bitmap block */
+       __u32   bg_inode_table;         /* Inodes table block */
+       __u16   bg_free_blocks_count;   /* Free blocks count */
+       __u16   bg_free_inodes_count;   /* Free inodes count */
+       __u16   bg_used_dirs_count;     /* Directories count */
+       __u16   bg_pad;
+       __u32   bg_reserved[3];
+};
+
+/*
+ * Data structures used by the directory indexing feature
+ *
+ * Note: all of the multibyte integer fields are little endian.
+ */
+
+/*
+ * Note: dx_root_info is laid out so that if it should somehow get
+ * overlaid by a dirent the two low bits of the hash version will be
+ * zero.  Therefore, the hash version mod 4 should never be 0.
+ * Sincerely, the paranoia department.
+ */
+struct ext2_dx_root_info {
+       __u32 reserved_zero;
+       __u8 hash_version; /* 0 now, 1 at release */
+       __u8 info_length; /* 8 */
+       __u8 indirect_levels;
+       __u8 unused_flags;
+};
+
+#define EXT2_HASH_LEGACY       0
+#define EXT2_HASH_HALF_MD4     1
+#define EXT2_HASH_TEA          2
+
+#define EXT2_HASH_FLAG_INCOMPAT        0x1
+
+struct ext2_dx_entry {
+       __u32 hash;
+       __u32 block;
+};
+
+struct ext2_dx_countlimit {
+       __u16 limit;
+       __u16 count;
+};
+
+
+/*
+ * Macro-instructions used to manage group descriptors
+ */
+#define EXT2_BLOCKS_PER_GROUP(s)       (EXT2_SB(s)->s_blocks_per_group)
+#define EXT2_INODES_PER_GROUP(s)       (EXT2_SB(s)->s_inodes_per_group)
+#define EXT2_INODES_PER_BLOCK(s)       (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s))
+/* limits imposed by 16-bit value gd_free_{blocks,inode}_count */
+#define EXT2_MAX_BLOCKS_PER_GROUP(s)   ((1 << 16) - 8)
+#define EXT2_MAX_INODES_PER_GROUP(s)   ((1 << 16) - EXT2_INODES_PER_BLOCK(s))
+#ifdef __KERNEL__
+#define EXT2_DESC_PER_BLOCK(s)         (EXT2_SB(s)->s_desc_per_block)
+#define EXT2_DESC_PER_BLOCK_BITS(s)    (EXT2_SB(s)->s_desc_per_block_bits)
+#else
+#define EXT2_DESC_PER_BLOCK(s)         (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc))
+#endif
+
+/*
+ * Constants relative to the data blocks
+ */
+#define EXT2_NDIR_BLOCKS               12
+#define EXT2_IND_BLOCK                 EXT2_NDIR_BLOCKS
+#define EXT2_DIND_BLOCK                        (EXT2_IND_BLOCK + 1)
+#define EXT2_TIND_BLOCK                        (EXT2_DIND_BLOCK + 1)
+#define EXT2_N_BLOCKS                  (EXT2_TIND_BLOCK + 1)
+
+/*
+ * Inode flags
+ */
+#define EXT2_SECRM_FL                  0x00000001 /* Secure deletion */
+#define EXT2_UNRM_FL                   0x00000002 /* Undelete */
+#define EXT2_COMPR_FL                  0x00000004 /* Compress file */
+#define EXT2_SYNC_FL                   0x00000008 /* Synchronous updates */
+#define EXT2_IMMUTABLE_FL              0x00000010 /* Immutable file */
+#define EXT2_APPEND_FL                 0x00000020 /* writes to file may only append */
+#define EXT2_NODUMP_FL                 0x00000040 /* do not dump file */
+#define EXT2_NOATIME_FL                        0x00000080 /* do not update atime */
+/* Reserved for compression usage... */
+#define EXT2_DIRTY_FL                  0x00000100
+#define EXT2_COMPRBLK_FL               0x00000200 /* One or more compressed clusters */
+#define EXT2_NOCOMPR_FL                        0x00000400 /* Access raw compressed data */
+#define EXT2_ECOMPR_FL                 0x00000800 /* Compression error */
+/* End compression flags --- maybe not all used */
+#define EXT2_BTREE_FL                  0x00001000 /* btree format dir */
+#define EXT2_INDEX_FL                  0x00001000 /* hash-indexed directory */
+#define EXT2_IMAGIC_FL                 0x00002000
+#define EXT3_JOURNAL_DATA_FL           0x00004000 /* file data should be journaled */
+#define EXT2_NOTAIL_FL                 0x00008000 /* file tail should not be merged */
+#define EXT2_DIRSYNC_FL                0x00010000 /* Synchronous directory modifications */
+#define EXT2_TOPDIR_FL                 0x00020000 /* Top of directory hierarchies*/
+#define EXT3_EXTENTS_FL                0x00080000 /* Inode uses extents */
+#define EXT2_RESERVED_FL               0x80000000 /* reserved for ext2 lib */
+
+#define EXT2_FL_USER_VISIBLE           0x0003DFFF /* User visible flags */
+#define EXT2_FL_USER_MODIFIABLE                0x000080FF /* User modifiable flags */
+
+/*
+ * ioctl commands
+ */
+#define EXT2_IOC_GETFLAGS              _IOR('f', 1, long)
+#define EXT2_IOC_SETFLAGS              _IOW('f', 2, long)
+#define EXT2_IOC_GETVERSION            _IOR('v', 1, long)
+#define EXT2_IOC_SETVERSION            _IOW('v', 2, long)
+
+/*
+ * Structure of an inode on the disk
+ */
+struct ext2_inode {
+       __u16   i_mode;         /* File mode */
+       __u16   i_uid;          /* Low 16 bits of Owner Uid */
+       __u32   i_size;         /* Size in bytes */
+       __u32   i_atime;        /* Access time */
+       __u32   i_ctime;        /* Creation time */
+       __u32   i_mtime;        /* Modification time */
+       __u32   i_dtime;        /* Deletion Time */
+       __u16   i_gid;          /* Low 16 bits of Group Id */
+       __u16   i_links_count;  /* Links count */
+       __u32   i_blocks;       /* Blocks count */
+       __u32   i_flags;        /* File flags */
+       union {
+               struct {
+                       __u32  l_i_reserved1;
+               } linux1;
+               struct {
+                       __u32  h_i_translator;
+               } hurd1;
+               struct {
+                       __u32  m_i_reserved1;
+               } masix1;
+       } osd1;                         /* OS dependent 1 */
+       __u32   i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+       __u32   i_generation;   /* File version (for NFS) */
+       __u32   i_file_acl;     /* File ACL */
+       __u32   i_dir_acl;      /* Directory ACL */
+       __u32   i_faddr;        /* Fragment address */
+       union {
+               struct {
+                       __u8    l_i_frag;       /* Fragment number */
+                       __u8    l_i_fsize;      /* Fragment size */
+                       __u16   i_pad1;
+                       __u16   l_i_uid_high;   /* these 2 fields    */
+                       __u16   l_i_gid_high;   /* were reserved2[0] */
+                       __u32   l_i_reserved2;
+               } linux2;
+               struct {
+                       __u8    h_i_frag;       /* Fragment number */
+                       __u8    h_i_fsize;      /* Fragment size */
+                       __u16   h_i_mode_high;
+                       __u16   h_i_uid_high;
+                       __u16   h_i_gid_high;
+                       __u32   h_i_author;
+               } hurd2;
+               struct {
+                       __u8    m_i_frag;       /* Fragment number */
+                       __u8    m_i_fsize;      /* Fragment size */
+                       __u16   m_pad1;
+                       __u32   m_i_reserved2[2];
+               } masix2;
+       } osd2;                         /* OS dependent 2 */
+};
+
+/*
+ * Permanent part of an large inode on the disk
+ */
+struct ext2_inode_large {
+       __u16   i_mode;         /* File mode */
+       __u16   i_uid;          /* Low 16 bits of Owner Uid */
+       __u32   i_size;         /* Size in bytes */
+       __u32   i_atime;        /* Access time */
+       __u32   i_ctime;        /* Creation time */
+       __u32   i_mtime;        /* Modification time */
+       __u32   i_dtime;        /* Deletion Time */
+       __u16   i_gid;          /* Low 16 bits of Group Id */
+       __u16   i_links_count;  /* Links count */
+       __u32   i_blocks;       /* Blocks count */
+       __u32   i_flags;        /* File flags */
+       union {
+               struct {
+                       __u32  l_i_reserved1;
+               } linux1;
+               struct {
+                       __u32  h_i_translator;
+               } hurd1;
+               struct {
+                       __u32  m_i_reserved1;
+               } masix1;
+       } osd1;                         /* OS dependent 1 */
+       __u32   i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+       __u32   i_generation;   /* File version (for NFS) */
+       __u32   i_file_acl;     /* File ACL */
+       __u32   i_dir_acl;      /* Directory ACL */
+       __u32   i_faddr;        /* Fragment address */
+       union {
+               struct {
+                       __u8    l_i_frag;       /* Fragment number */
+                       __u8    l_i_fsize;      /* Fragment size */
+                       __u16   i_pad1;
+                       __u16   l_i_uid_high;   /* these 2 fields    */
+                       __u16   l_i_gid_high;   /* were reserved2[0] */
+                       __u32   l_i_reserved2;
+               } linux2;
+               struct {
+                       __u8    h_i_frag;       /* Fragment number */
+                       __u8    h_i_fsize;      /* Fragment size */
+                       __u16   h_i_mode_high;
+                       __u16   h_i_uid_high;
+                       __u16   h_i_gid_high;
+                       __u32   h_i_author;
+               } hurd2;
+               struct {
+                       __u8    m_i_frag;       /* Fragment number */
+                       __u8    m_i_fsize;      /* Fragment size */
+                       __u16   m_pad1;
+                       __u32   m_i_reserved2[2];
+               } masix2;
+       } osd2;                         /* OS dependent 2 */
+       __u16   i_extra_isize;
+       __u16   i_pad1;
+};
+
+#define i_size_high    i_dir_acl
+
+#if defined(__KERNEL__) || defined(__linux__)
+#define i_reserved1    osd1.linux1.l_i_reserved1
+#define i_frag         osd2.linux2.l_i_frag
+#define i_fsize                osd2.linux2.l_i_fsize
+#define i_uid_low      i_uid
+#define i_gid_low      i_gid
+#define i_uid_high     osd2.linux2.l_i_uid_high
+#define i_gid_high     osd2.linux2.l_i_gid_high
+#define i_reserved2    osd2.linux2.l_i_reserved2
+
+#else
+#if defined(__GNU__)
+
+#define i_translator   osd1.hurd1.h_i_translator
+#define i_frag         osd2.hurd2.h_i_frag;
+#define i_fsize                osd2.hurd2.h_i_fsize;
+#define i_uid_high     osd2.hurd2.h_i_uid_high
+#define i_gid_high     osd2.hurd2.h_i_gid_high
+#define i_author       osd2.hurd2.h_i_author
+
+#else
+#if defined(__masix__)
+
+#define i_reserved1    osd1.masix1.m_i_reserved1
+#define i_frag         osd2.masix2.m_i_frag
+#define i_fsize                osd2.masix2.m_i_fsize
+#define i_reserved2    osd2.masix2.m_i_reserved2
+
+#endif  /* __masix__ */
+#endif  /* __GNU__ */
+#endif /* defined(__KERNEL__) || defined(__linux__) */
+
+/*
+ * File system states
+ */
+#define EXT2_VALID_FS                  0x0001  /* Unmounted cleanly */
+#define EXT2_ERROR_FS                  0x0002  /* Errors detected */
+
+/*
+ * Mount flags
+ */
+#define EXT2_MOUNT_CHECK               0x0001  /* Do mount-time checks */
+#define EXT2_MOUNT_GRPID               0x0004  /* Create files with directory's group */
+#define EXT2_MOUNT_DEBUG               0x0008  /* Some debugging messages */
+#define EXT2_MOUNT_ERRORS_CONT         0x0010  /* Continue on errors */
+#define EXT2_MOUNT_ERRORS_RO           0x0020  /* Remount fs ro on errors */
+#define EXT2_MOUNT_ERRORS_PANIC                0x0040  /* Panic on errors */
+#define EXT2_MOUNT_MINIX_DF            0x0080  /* Mimics the Minix statfs */
+#define EXT2_MOUNT_NO_UID32            0x0200  /* Disable 32-bit UIDs */
+
+#define clear_opt(o, opt)              o &= ~EXT2_MOUNT_##opt
+#define set_opt(o, opt)                        o |= EXT2_MOUNT_##opt
+#define test_opt(sb, opt)              (EXT2_SB(sb)->s_mount_opt & \
+                                        EXT2_MOUNT_##opt)
+/*
+ * Maximal mount counts between two filesystem checks
+ */
+#define EXT2_DFL_MAX_MNT_COUNT         20      /* Allow 20 mounts */
+#define EXT2_DFL_CHECKINTERVAL         0       /* Don't use interval check */
+
+/*
+ * Behaviour when detecting errors
+ */
+#define EXT2_ERRORS_CONTINUE           1       /* Continue execution */
+#define EXT2_ERRORS_RO                 2       /* Remount fs read-only */
+#define EXT2_ERRORS_PANIC              3       /* Panic */
+#define EXT2_ERRORS_DEFAULT            EXT2_ERRORS_CONTINUE
+
+/*
+ * Structure of the super block
+ */
+struct ext2_super_block {
+       __u32   s_inodes_count;         /* Inodes count */
+       __u32   s_blocks_count;         /* Blocks count */
+       __u32   s_r_blocks_count;       /* Reserved blocks count */
+       __u32   s_free_blocks_count;    /* Free blocks count */
+       __u32   s_free_inodes_count;    /* Free inodes count */
+       __u32   s_first_data_block;     /* First Data Block */
+       __u32   s_log_block_size;       /* Block size */
+       __s32   s_log_frag_size;        /* Fragment size */
+       __u32   s_blocks_per_group;     /* # Blocks per group */
+       __u32   s_frags_per_group;      /* # Fragments per group */
+       __u32   s_inodes_per_group;     /* # Inodes per group */
+       __u32   s_mtime;                /* Mount time */
+       __u32   s_wtime;                /* Write time */
+       __u16   s_mnt_count;            /* Mount count */
+       __s16   s_max_mnt_count;        /* Maximal mount count */
+       __u16   s_magic;                /* Magic signature */
+       __u16   s_state;                /* File system state */
+       __u16   s_errors;               /* Behaviour when detecting errors */
+       __u16   s_minor_rev_level;      /* minor revision level */
+       __u32   s_lastcheck;            /* time of last check */
+       __u32   s_checkinterval;        /* max. time between checks */
+       __u32   s_creator_os;           /* OS */
+       __u32   s_rev_level;            /* Revision level */
+       __u16   s_def_resuid;           /* Default uid for reserved blocks */
+       __u16   s_def_resgid;           /* Default gid for reserved blocks */
+       /*
+        * These fields are for EXT2_DYNAMIC_REV superblocks only.
+        *
+        * Note: the difference between the compatible feature set and
+        * the incompatible feature set is that if there is a bit set
+        * in the incompatible feature set that the kernel doesn't
+        * know about, it should refuse to mount the filesystem.
+        *
+        * e2fsck's requirements are more strict; if it doesn't know
+        * about a feature in either the compatible or incompatible
+        * feature set, it must abort and not try to meddle with
+        * things it doesn't understand...
+        */
+       __u32   s_first_ino;            /* First non-reserved inode */
+       __u16   s_inode_size;           /* size of inode structure */
+       __u16   s_block_group_nr;       /* block group # of this superblock */
+       __u32   s_feature_compat;       /* compatible feature set */
+       __u32   s_feature_incompat;     /* incompatible feature set */
+       __u32   s_feature_ro_compat;    /* readonly-compatible feature set */
+       __u8    s_uuid[16];             /* 128-bit uuid for volume */
+       char    s_volume_name[16];      /* volume name */
+       char    s_last_mounted[64];     /* directory where last mounted */
+       __u32   s_algorithm_usage_bitmap; /* For compression */
+       /*
+        * Performance hints.  Directory preallocation should only
+        * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on.
+        */
+       __u8    s_prealloc_blocks;      /* Nr of blocks to try to preallocate*/
+       __u8    s_prealloc_dir_blocks;  /* Nr to preallocate for dirs */
+       __u16   s_reserved_gdt_blocks;  /* Per group table for online growth */
+       /*
+        * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set.
+        */
+       __u8    s_journal_uuid[16];     /* uuid of journal superblock */
+       __u32   s_journal_inum;         /* inode number of journal file */
+       __u32   s_journal_dev;          /* device number of journal file */
+       __u32   s_last_orphan;          /* start of list of inodes to delete */
+       __u32   s_hash_seed[4];         /* HTREE hash seed */
+       __u8    s_def_hash_version;     /* Default hash version to use */
+       __u8    s_jnl_backup_type;      /* Default type of journal backup */
+       __u16   s_reserved_word_pad;
+       __u32   s_default_mount_opts;
+       __u32   s_first_meta_bg;        /* First metablock group */
+       __u32   s_mkfs_time;            /* When the filesystem was created */
+       __u32   s_jnl_blocks[17];       /* Backup of the journal inode */
+       __u32   s_reserved[172];        /* Padding to the end of the block */
+};
+
+/*
+ * Codes for operating systems
+ */
+#define EXT2_OS_LINUX          0
+#define EXT2_OS_HURD           1
+#define EXT2_OS_MASIX          2
+#define EXT2_OS_FREEBSD                3
+#define EXT2_OS_LITES          4
+
+/*
+ * Revision levels
+ */
+#define EXT2_GOOD_OLD_REV      0       /* The good old (original) format */
+#define EXT2_DYNAMIC_REV       1       /* V2 format w/ dynamic inode sizes */
+
+#define EXT2_CURRENT_REV       EXT2_GOOD_OLD_REV
+#define EXT2_MAX_SUPP_REV      EXT2_DYNAMIC_REV
+
+#define EXT2_GOOD_OLD_INODE_SIZE 128
+
+/*
+ * Journal inode backup types
+ */
+#define EXT3_JNL_BACKUP_BLOCKS 1
+
+/*
+ * Feature set definitions
+ */
+
+#define EXT2_HAS_COMPAT_FEATURE(sb,mask)                       \
+       ( EXT2_SB(sb)->s_feature_compat & (mask) )
+#define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask)                    \
+       ( EXT2_SB(sb)->s_feature_ro_compat & (mask) )
+#define EXT2_HAS_INCOMPAT_FEATURE(sb,mask)                     \
+       ( EXT2_SB(sb)->s_feature_incompat & (mask) )
+
+#define EXT2_FEATURE_COMPAT_DIR_PREALLOC       0x0001
+#define EXT2_FEATURE_COMPAT_IMAGIC_INODES      0x0002
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL                0x0004
+#define EXT2_FEATURE_COMPAT_EXT_ATTR           0x0008
+#define EXT2_FEATURE_COMPAT_RESIZE_INODE       0x0010
+#define EXT2_FEATURE_COMPAT_DIR_INDEX          0x0020
+
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER    0x0001
+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE      0x0002
+/* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR    0x0004 not used */
+
+#define EXT2_FEATURE_INCOMPAT_COMPRESSION      0x0001
+#define EXT2_FEATURE_INCOMPAT_FILETYPE         0x0002
+#define EXT3_FEATURE_INCOMPAT_RECOVER          0x0004 /* Needs recovery */
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV      0x0008 /* Journal device */
+#define EXT2_FEATURE_INCOMPAT_META_BG          0x0010
+#define EXT3_FEATURE_INCOMPAT_EXTENTS          0x0040
+
+
+#define EXT2_FEATURE_COMPAT_SUPP       0
+#define EXT2_FEATURE_INCOMPAT_SUPP     (EXT2_FEATURE_INCOMPAT_FILETYPE)
+#define EXT2_FEATURE_RO_COMPAT_SUPP    (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+                                        EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+                                        EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+
+/*
+ * Default values for user and/or group using reserved blocks
+ */
+#define EXT2_DEF_RESUID                0
+#define EXT2_DEF_RESGID                0
+
+/*
+ * Default mount options
+ */
+#define EXT2_DEFM_DEBUG                0x0001
+#define EXT2_DEFM_BSDGROUPS    0x0002
+#define EXT2_DEFM_XATTR_USER   0x0004
+#define EXT2_DEFM_ACL          0x0008
+#define EXT2_DEFM_UID16                0x0010
+#define EXT3_DEFM_JMODE                0x0060 
+#define EXT3_DEFM_JMODE_DATA   0x0020
+#define EXT3_DEFM_JMODE_ORDERED        0x0040
+#define EXT3_DEFM_JMODE_WBACK  0x0060
+
+/*
+ * Structure of a directory entry
+ */
+#define EXT2_NAME_LEN 255
+
+struct ext2_dir_entry {
+       __u32   inode;                  /* Inode number */
+       __u16   rec_len;                /* Directory entry length */
+       __u16   name_len;               /* Name length */
+       char    name[EXT2_NAME_LEN];    /* File name */
+};
+
+/*
+ * The new version of the directory entry.  Since EXT2 structures are
+ * stored in intel byte order, and the name_len field could never be
+ * bigger than 255 chars, it's safe to reclaim the extra byte for the
+ * file_type field.
+ */
+struct ext2_dir_entry_2 {
+       __u32   inode;                  /* Inode number */
+       __u16   rec_len;                /* Directory entry length */
+       __u8    name_len;               /* Name length */
+       __u8    file_type;
+       char    name[EXT2_NAME_LEN];    /* File name */
+};
+
+/*
+ * Ext2 directory file types.  Only the low 3 bits are used.  The
+ * other bits are reserved for now.
+ */
+#define EXT2_FT_UNKNOWN                0
+#define EXT2_FT_REG_FILE       1
+#define EXT2_FT_DIR            2
+#define EXT2_FT_CHRDEV         3
+#define EXT2_FT_BLKDEV         4
+#define EXT2_FT_FIFO           5
+#define EXT2_FT_SOCK           6
+#define EXT2_FT_SYMLINK                7
+
+#define EXT2_FT_MAX            8
+
+/*
+ * EXT2_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a multiple of 4
+ */
+#define EXT2_DIR_PAD                   4
+#define EXT2_DIR_ROUND                 (EXT2_DIR_PAD - 1)
+#define EXT2_DIR_REC_LEN(name_len)     (((name_len) + 8 + EXT2_DIR_ROUND) & \
+                                        ~EXT2_DIR_ROUND)
+
+#endif /* _LINUX_EXT2_FS_H */
diff --git a/e2fsprogs/ext2fs/ext2_io.h b/e2fsprogs/ext2fs/ext2_io.h
new file mode 100644 (file)
index 0000000..e17886c
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * io.h --- the I/O manager abstraction
+ * 
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#ifndef _EXT2FS_EXT2_IO_H
+#define _EXT2FS_EXT2_IO_H
+
+/*
+ * ext2_loff_t is defined here since unix_io.c needs it.
+ */
+#if defined(__GNUC__) || defined(HAS_LONG_LONG)
+typedef long long      ext2_loff_t;
+#else
+typedef long           ext2_loff_t;
+#endif
+
+/* llseek.c */
+ext2_loff_t ext2fs_llseek (int, ext2_loff_t, int);
+
+typedef struct struct_io_manager *io_manager;
+typedef struct struct_io_channel *io_channel;
+
+#define CHANNEL_FLAGS_WRITETHROUGH     0x01
+
+struct struct_io_channel {
+       errcode_t       magic;
+       io_manager      manager;
+       char            *name;
+       int             block_size;
+       errcode_t       (*read_error)(io_channel channel,
+                                     unsigned long block,
+                                     int count,
+                                     void *data,
+                                     size_t size,
+                                     int actual_bytes_read,
+                                     errcode_t error);
+       errcode_t       (*write_error)(io_channel channel,
+                                      unsigned long block,
+                                      int count,
+                                      const void *data,
+                                      size_t size,
+                                      int actual_bytes_written,
+                                      errcode_t error);
+       int             refcount;
+       int             flags;
+       int             reserved[14];
+       void            *private_data;
+       void            *app_data;
+};
+
+struct struct_io_manager {
+       errcode_t magic;
+       const char *name;
+       errcode_t (*open)(const char *name, int flags, io_channel *channel);
+       errcode_t (*close)(io_channel channel);
+       errcode_t (*set_blksize)(io_channel channel, int blksize);
+       errcode_t (*read_blk)(io_channel channel, unsigned long block,
+                             int count, void *data);
+       errcode_t (*write_blk)(io_channel channel, unsigned long block,
+                              int count, const void *data);
+       errcode_t (*flush)(io_channel channel);
+       errcode_t (*write_byte)(io_channel channel, unsigned long offset,
+                               int count, const void *data);
+       errcode_t (*set_option)(io_channel channel, const char *option, 
+                               const char *arg);
+       int             reserved[14];
+};
+
+#define IO_FLAG_RW     1
+
+/*
+ * Convenience functions....
+ */
+#define io_channel_close(c)            ((c)->manager->close((c)))
+#define io_channel_set_blksize(c,s)    ((c)->manager->set_blksize((c),s))
+#define io_channel_read_blk(c,b,n,d)   ((c)->manager->read_blk((c),b,n,d))
+#define io_channel_write_blk(c,b,n,d)  ((c)->manager->write_blk((c),b,n,d))
+#define io_channel_flush(c)            ((c)->manager->flush((c)))
+#define io_channel_bumpcount(c)                ((c)->refcount++)
+       
+/* io_manager.c */
+extern errcode_t io_channel_set_options(io_channel channel, 
+                                       const char *options);
+extern errcode_t io_channel_write_byte(io_channel channel, 
+                                      unsigned long offset,
+                                      int count, const void *data);
+
+/* unix_io.c */
+extern io_manager unix_io_manager;
+
+/* test_io.c */
+extern io_manager test_io_manager, test_io_backing_manager;
+extern void (*test_io_cb_read_blk)
+       (unsigned long block, int count, errcode_t err);
+extern void (*test_io_cb_write_blk)
+       (unsigned long block, int count, errcode_t err);
+extern void (*test_io_cb_set_blksize)
+       (int blksize, errcode_t err);
+
+#endif /* _EXT2FS_EXT2_IO_H */
+       
diff --git a/e2fsprogs/ext2fs/ext2_types.h b/e2fsprogs/ext2fs/ext2_types.h
new file mode 100644 (file)
index 0000000..ee9b980
--- /dev/null
@@ -0,0 +1 @@
+#include <linux/types.h>
diff --git a/e2fsprogs/ext2fs/ext2fs.h b/e2fsprogs/ext2fs/ext2fs.h
new file mode 100644 (file)
index 0000000..0832bc2
--- /dev/null
@@ -0,0 +1,1137 @@
+/*
+ * ext2fs.h --- ext2fs
+ * 
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#ifndef _EXT2FS_EXT2FS_H
+#define _EXT2FS_EXT2FS_H
+
+#ifdef __GNUC__
+#define EXT2FS_ATTR(x) __attribute__(x)
+#else
+#define EXT2FS_ATTR(x)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Non-GNU C compilers won't necessarily understand inline
+ */
+#if (!defined(__GNUC__) && !defined(__WATCOMC__))
+#define NO_INLINE_FUNCS
+#endif
+
+/*
+ * Build in support for byte-swapping filesystems if we the feature
+ * has been configured or if we're being built on a CPU architecture
+ * with a non-native byte order.
+ */
+#if defined(ENABLE_SWAPFS) || defined(WORDS_BIGENDIAN)
+#define EXT2FS_ENABLE_SWAPFS
+#endif
+
+/*
+ * Where the master copy of the superblock is located, and how big
+ * superblocks are supposed to be.  We define SUPERBLOCK_SIZE because
+ * the size of the superblock structure is not necessarily trustworthy
+ * (some versions have the padding set up so that the superblock is
+ * 1032 bytes long).
+ */
+#define SUPERBLOCK_OFFSET      1024
+#define SUPERBLOCK_SIZE        1024
+
+/*
+ * The last ext2fs revision level that this version of the library is
+ * able to support.
+ */
+#define EXT2_LIB_CURRENT_REV   EXT2_DYNAMIC_REV
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if EXT2_FLAT_INCLUDES
+#include "e2_types.h"
+#include "ext2_fs.h"
+#else
+#include <ext2fs/ext2_types.h>
+#include <ext2fs/ext2_fs.h>
+#endif /* EXT2_FLAT_INCLUDES */
+
+typedef __u32          ext2_ino_t;
+typedef __u32          blk_t;
+typedef __u32          dgrp_t;
+typedef __u32          ext2_off_t;
+typedef __s64          e2_blkcnt_t;
+typedef __u32          ext2_dirhash_t;
+
+#if EXT2_FLAT_INCLUDES
+#include "com_err.h"
+#include "ext2_io.h"
+#include "ext2_err.h"
+#else
+#include <et/com_err.h>
+#include <ext2fs/ext2_io.h>
+#include <ext2fs/ext2_err.h>
+#endif
+
+/*
+ * Portability help for Microsoft Visual C++
+ */
+#ifdef _MSC_VER
+#define EXT2_QSORT_TYPE int __cdecl
+#else
+#define EXT2_QSORT_TYPE int
+#endif
+
+typedef struct struct_ext2_filsys *ext2_filsys;
+
+struct ext2fs_struct_generic_bitmap {
+       errcode_t       magic;
+       ext2_filsys     fs;
+       __u32           start, end;
+       __u32           real_end;
+       char    *       description;
+       char    *       bitmap;
+       errcode_t       base_error_code;
+       __u32           reserved[7];
+};
+
+#define EXT2FS_MARK_ERROR      0
+#define EXT2FS_UNMARK_ERROR    1
+#define EXT2FS_TEST_ERROR      2
+
+typedef struct ext2fs_struct_generic_bitmap *ext2fs_generic_bitmap;
+typedef struct ext2fs_struct_generic_bitmap *ext2fs_inode_bitmap;
+typedef struct ext2fs_struct_generic_bitmap *ext2fs_block_bitmap;
+
+#ifdef EXT2_DYNAMIC_REV
+#define EXT2_FIRST_INODE(s)    EXT2_FIRST_INO(s)
+#else
+#define EXT2_FIRST_INODE(s)    EXT2_FIRST_INO
+#define EXT2_INODE_SIZE(s)     sizeof(struct ext2_inode)
+#endif
+
+/*
+ * badblocks list definitions
+ */
+
+typedef struct ext2_struct_u32_list *ext2_badblocks_list;
+typedef struct ext2_struct_u32_iterate *ext2_badblocks_iterate;
+
+typedef struct ext2_struct_u32_list *ext2_u32_list;
+typedef struct ext2_struct_u32_iterate *ext2_u32_iterate;
+
+/* old */
+typedef struct ext2_struct_u32_list *badblocks_list;
+typedef struct ext2_struct_u32_iterate *badblocks_iterate;
+
+#define BADBLOCKS_FLAG_DIRTY   1
+
+/*
+ * ext2_dblist structure and abstractions (see dblist.c)
+ */
+struct ext2_db_entry {
+       ext2_ino_t      ino;
+       blk_t   blk;
+       int     blockcnt;
+};
+
+typedef struct ext2_struct_dblist *ext2_dblist;
+
+#define DBLIST_ABORT   1
+
+/*
+ * ext2_fileio definitions
+ */
+
+#define EXT2_FILE_WRITE                0x0001
+#define EXT2_FILE_CREATE       0x0002
+
+#define EXT2_FILE_MASK         0x00FF
+
+#define EXT2_FILE_BUF_DIRTY    0x4000
+#define EXT2_FILE_BUF_VALID    0x2000
+
+typedef struct ext2_file *ext2_file_t;
+
+#define EXT2_SEEK_SET  0
+#define EXT2_SEEK_CUR  1
+#define EXT2_SEEK_END  2
+
+/*
+ * Flags for the ext2_filsys structure and for ext2fs_open()
+ */
+#define EXT2_FLAG_RW                   0x01
+#define EXT2_FLAG_CHANGED              0x02
+#define EXT2_FLAG_DIRTY                        0x04
+#define EXT2_FLAG_VALID                        0x08
+#define EXT2_FLAG_IB_DIRTY             0x10
+#define EXT2_FLAG_BB_DIRTY             0x20
+#define EXT2_FLAG_SWAP_BYTES           0x40
+#define EXT2_FLAG_SWAP_BYTES_READ      0x80
+#define EXT2_FLAG_SWAP_BYTES_WRITE     0x100
+#define EXT2_FLAG_MASTER_SB_ONLY       0x200
+#define EXT2_FLAG_FORCE                        0x400
+#define EXT2_FLAG_SUPER_ONLY           0x800
+#define EXT2_FLAG_JOURNAL_DEV_OK       0x1000
+#define EXT2_FLAG_IMAGE_FILE           0x2000
+
+/*
+ * Special flag in the ext2 inode i_flag field that means that this is
+ * a new inode.  (So that ext2_write_inode() can clear extra fields.)
+ */
+#define EXT2_NEW_INODE_FL      0x80000000
+
+/*
+ * Flags for mkjournal
+ *
+ * EXT2_MKJOURNAL_V1_SUPER     Make a (deprecated) V1 journal superblock
+ */
+#define EXT2_MKJOURNAL_V1_SUPER        0x0000001
+
+struct struct_ext2_filsys {
+       errcode_t                       magic;
+       io_channel                      io;
+       int                             flags;
+       char *                          device_name;
+       struct ext2_super_block *       super;
+       unsigned int                    blocksize;
+       int                             fragsize;
+       dgrp_t                          group_desc_count;
+       unsigned long                   desc_blocks;
+       struct ext2_group_desc *        group_desc;
+       int                             inode_blocks_per_group;
+       ext2fs_inode_bitmap             inode_map;
+       ext2fs_block_bitmap             block_map;
+       errcode_t (*get_blocks)(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks);
+       errcode_t (*check_directory)(ext2_filsys fs, ext2_ino_t ino);
+       errcode_t (*write_bitmaps)(ext2_filsys fs);
+       errcode_t (*read_inode)(ext2_filsys fs, ext2_ino_t ino,
+                               struct ext2_inode *inode);
+       errcode_t (*write_inode)(ext2_filsys fs, ext2_ino_t ino,
+                               struct ext2_inode *inode);
+       ext2_badblocks_list             badblocks;
+       ext2_dblist                     dblist;
+       __u32                           stride; /* for mke2fs */
+       struct ext2_super_block *       orig_super;
+       struct ext2_image_hdr *         image_header;
+       __u32                           umask;
+       /*
+        * Reserved for future expansion
+        */
+       __u32                           reserved[8];
+
+       /*
+        * Reserved for the use of the calling application.
+        */
+       void *                          priv_data;
+
+       /*
+        * Inode cache
+        */
+       struct ext2_inode_cache         *icache;
+       io_channel                      image_io;
+};
+
+#if EXT2_FLAT_INCLUDES
+#include "e2_bitops.h"
+#else
+#include <ext2fs/bitops.h>
+#endif
+
+/*
+ * Return flags for the block iterator functions
+ */
+#define BLOCK_CHANGED  1
+#define BLOCK_ABORT    2
+#define BLOCK_ERROR    4
+
+/*
+ * Block interate flags
+ *
+ * BLOCK_FLAG_APPEND, or BLOCK_FLAG_HOLE, indicates that the interator
+ * function should be called on blocks where the block number is zero.
+ * This is used by ext2fs_expand_dir() to be able to add a new block
+ * to an inode.  It can also be used for programs that want to be able
+ * to deal with files that contain "holes".
+ * 
+ * BLOCK_FLAG_TRAVERSE indicates that the iterator function for the
+ * indirect, doubly indirect, etc. blocks should be called after all
+ * of the blocks containined in the indirect blocks are processed.
+ * This is useful if you are going to be deallocating blocks from an
+ * inode.
+ *
+ * BLOCK_FLAG_DATA_ONLY indicates that the iterator function should be
+ * called for data blocks only.
+ *
+ * BLOCK_FLAG_NO_LARGE is for internal use only.  It informs
+ * ext2fs_block_iterate2 that large files won't be accepted.
+ */
+#define BLOCK_FLAG_APPEND      1
+#define BLOCK_FLAG_HOLE                1
+#define BLOCK_FLAG_DEPTH_TRAVERSE      2
+#define BLOCK_FLAG_DATA_ONLY   4
+
+#define BLOCK_FLAG_NO_LARGE    0x1000
+
+/*
+ * Magic "block count" return values for the block iterator function.
+ */
+#define BLOCK_COUNT_IND                (-1)
+#define BLOCK_COUNT_DIND       (-2)
+#define BLOCK_COUNT_TIND       (-3)
+#define BLOCK_COUNT_TRANSLATOR (-4)
+
+#if 0
+/*
+ * Flags for ext2fs_move_blocks
+ */
+#define EXT2_BMOVE_GET_DBLIST  0x0001  
+#define EXT2_BMOVE_DEBUG       0x0002
+#endif
+
+/*
+ * Flags for directory block reading and writing functions
+ */
+#define EXT2_DIRBLOCK_V2_STRUCT        0x0001
+
+/*
+ * Return flags for the directory iterator functions
+ */
+#define DIRENT_CHANGED 1
+#define DIRENT_ABORT   2
+#define DIRENT_ERROR   3
+
+/*
+ * Directory iterator flags
+ */
+
+#define DIRENT_FLAG_INCLUDE_EMPTY      1
+#define DIRENT_FLAG_INCLUDE_REMOVED    2
+
+#define DIRENT_DOT_FILE                1
+#define DIRENT_DOT_DOT_FILE    2
+#define DIRENT_OTHER_FILE      3
+#define DIRENT_DELETED_FILE    4
+
+/*
+ * Inode scan definitions
+ */
+typedef struct ext2_struct_inode_scan *ext2_inode_scan;
+
+/*
+ * ext2fs_scan flags
+ */
+#define EXT2_SF_CHK_BADBLOCKS  0x0001
+#define EXT2_SF_BAD_INODE_BLK  0x0002
+#define EXT2_SF_BAD_EXTRA_BYTES        0x0004
+#define EXT2_SF_SKIP_MISSING_ITABLE    0x0008
+
+/*
+ * ext2fs_check_if_mounted flags
+ */
+#define EXT2_MF_MOUNTED                1
+#define EXT2_MF_ISROOT         2
+#define EXT2_MF_READONLY       4
+#define EXT2_MF_SWAP           8
+
+/*
+ * Ext2/linux mode flags.  We define them here so that we don't need
+ * to depend on the OS's sys/stat.h, since we may be compiling on a
+ * non-Linux system.
+ */
+#define LINUX_S_IFMT  00170000
+#define LINUX_S_IFSOCK 0140000
+#define LINUX_S_IFLNK   0120000
+#define LINUX_S_IFREG  0100000
+#define LINUX_S_IFBLK  0060000
+#define LINUX_S_IFDIR  0040000
+#define LINUX_S_IFCHR  0020000
+#define LINUX_S_IFIFO  0010000
+#define LINUX_S_ISUID  0004000
+#define LINUX_S_ISGID  0002000
+#define LINUX_S_ISVTX  0001000
+
+#define LINUX_S_IRWXU 00700
+#define LINUX_S_IRUSR 00400
+#define LINUX_S_IWUSR 00200
+#define LINUX_S_IXUSR 00100
+
+#define LINUX_S_IRWXG 00070
+#define LINUX_S_IRGRP 00040
+#define LINUX_S_IWGRP 00020
+#define LINUX_S_IXGRP 00010
+
+#define LINUX_S_IRWXO 00007
+#define LINUX_S_IROTH 00004
+#define LINUX_S_IWOTH 00002
+#define LINUX_S_IXOTH 00001
+
+#define LINUX_S_ISLNK(m)       (((m) & LINUX_S_IFMT) == LINUX_S_IFLNK)
+#define LINUX_S_ISREG(m)       (((m) & LINUX_S_IFMT) == LINUX_S_IFREG)
+#define LINUX_S_ISDIR(m)       (((m) & LINUX_S_IFMT) == LINUX_S_IFDIR)
+#define LINUX_S_ISCHR(m)       (((m) & LINUX_S_IFMT) == LINUX_S_IFCHR)
+#define LINUX_S_ISBLK(m)       (((m) & LINUX_S_IFMT) == LINUX_S_IFBLK)
+#define LINUX_S_ISFIFO(m)      (((m) & LINUX_S_IFMT) == LINUX_S_IFIFO)
+#define LINUX_S_ISSOCK(m)      (((m) & LINUX_S_IFMT) == LINUX_S_IFSOCK)
+
+/*
+ * ext2 size of an inode
+ */
+#define EXT2_I_SIZE(i) ((i)->i_size | ((__u64) (i)->i_size_high << 32))
+
+/*
+ * ext2_icount_t abstraction
+ */
+#define EXT2_ICOUNT_OPT_INCREMENT      0x01
+
+typedef struct ext2_icount *ext2_icount_t;
+
+/*
+ * Flags for ext2fs_bmap
+ */
+#define BMAP_ALLOC     0x0001
+#define BMAP_SET       0x0002
+
+/*
+ * Flags for imager.c functions
+ */
+#define IMAGER_FLAG_INODEMAP   1
+#define IMAGER_FLAG_SPARSEWRITE        2
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+         if ((struct)->magic != (code)) return (code)
+
+
+/*
+ * For ext2 compression support
+ */
+#define EXT2FS_COMPRESSED_BLKADDR ((blk_t) 0xffffffff)
+#define HOLE_BLKADDR(_b) ((_b) == 0 || (_b) == EXT2FS_COMPRESSED_BLKADDR)
+
+/*
+ * Features supported by this version of the library
+ */
+#define EXT2_LIB_FEATURE_COMPAT_SUPP   (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\
+                                        EXT2_FEATURE_COMPAT_IMAGIC_INODES|\
+                                        EXT3_FEATURE_COMPAT_HAS_JOURNAL|\
+                                        EXT2_FEATURE_COMPAT_RESIZE_INODE|\
+                                        EXT2_FEATURE_COMPAT_DIR_INDEX|\
+                                        EXT2_FEATURE_COMPAT_EXT_ATTR)
+
+/* This #ifdef is temporary until compression is fully supported */
+#ifdef ENABLE_COMPRESSION
+#ifndef I_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL
+/* If the below warning bugs you, then have
+   `CPPFLAGS=-DI_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL' in your
+   environment at configure time. */
+ #warning "Compression support is experimental"
+#endif
+#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\
+                                        EXT2_FEATURE_INCOMPAT_COMPRESSION|\
+                                        EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
+                                        EXT2_FEATURE_INCOMPAT_META_BG|\
+                                        EXT3_FEATURE_INCOMPAT_RECOVER)
+#else
+#define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\
+                                        EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\
+                                        EXT2_FEATURE_INCOMPAT_META_BG|\
+                                        EXT3_FEATURE_INCOMPAT_RECOVER)
+#endif
+#define EXT2_LIB_FEATURE_RO_COMPAT_SUPP        (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\
+                                        EXT2_FEATURE_RO_COMPAT_LARGE_FILE)
+/*
+ * function prototypes
+ */
+
+/* alloc.c */
+extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode,
+                                 ext2fs_inode_bitmap map, ext2_ino_t *ret);
+extern errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal,
+                                 ext2fs_block_bitmap map, blk_t *ret);
+extern errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start,
+                                       blk_t finish, int num,
+                                       ext2fs_block_bitmap map,
+                                       blk_t *ret);
+extern errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal,
+                                   char *block_buf, blk_t *ret);
+
+/* alloc_sb.c */
+extern int ext2fs_reserve_super_and_bgd(ext2_filsys fs, 
+                                       dgrp_t group,
+                                       ext2fs_block_bitmap bmap);
+
+/* alloc_stats.c */
+void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse);
+void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino,
+                              int inuse, int isdir);
+void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse);
+
+/* alloc_tables.c */
+extern errcode_t ext2fs_allocate_tables(ext2_filsys fs);
+extern errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group,
+                                            ext2fs_block_bitmap bmap);
+
+/* badblocks.c */
+extern errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size);
+extern errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk);
+extern int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk);
+extern int ext2fs_u32_list_test(ext2_u32_list bb, blk_t blk);
+extern errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb,
+                                              ext2_u32_iterate *ret);
+extern int ext2fs_u32_list_iterate(ext2_u32_iterate iter, blk_t *blk);
+extern void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter);
+extern errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest);
+extern int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2);
+
+extern errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret,
+                                           int size);
+extern errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb,
+                                          blk_t blk);
+extern int ext2fs_badblocks_list_test(ext2_badblocks_list bb,
+                                   blk_t blk);
+extern int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk);
+extern void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk);
+extern errcode_t
+       ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb,
+                                           ext2_badblocks_iterate *ret);
+extern int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter,
+                                        blk_t *blk);
+extern void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter);
+extern errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src,
+                                      ext2_badblocks_list *dest);
+extern int ext2fs_badblocks_equal(ext2_badblocks_list bb1,
+                                 ext2_badblocks_list bb2);
+extern int ext2fs_u32_list_count(ext2_u32_list bb);
+
+/* bb_compat */
+extern errcode_t badblocks_list_create(badblocks_list *ret, int size);
+extern errcode_t badblocks_list_add(badblocks_list bb, blk_t blk);
+extern int badblocks_list_test(badblocks_list bb, blk_t blk);
+extern errcode_t badblocks_list_iterate_begin(badblocks_list bb,
+                                             badblocks_iterate *ret);
+extern int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk);
+extern void badblocks_list_iterate_end(badblocks_iterate iter);
+extern void badblocks_list_free(badblocks_list bb);
+
+/* bb_inode.c */
+extern errcode_t ext2fs_update_bb_inode(ext2_filsys fs,
+                                       ext2_badblocks_list bb_list);
+
+/* bitmaps.c */
+extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs);
+extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs);
+extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs);
+extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs);
+extern errcode_t ext2fs_allocate_generic_bitmap(__u32 start,
+                                               __u32 end,
+                                               __u32 real_end,
+                                               const char *descr,
+                                               ext2fs_generic_bitmap *ret);
+extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs,
+                                             const char *descr,
+                                             ext2fs_block_bitmap *ret);
+extern errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs,
+                                             const char *descr,
+                                             ext2fs_inode_bitmap *ret);
+extern errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap,
+                                              ext2_ino_t end, ext2_ino_t *oend);
+extern errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap,
+                                              blk_t end, blk_t *oend);
+extern void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap);
+extern void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap);
+extern errcode_t ext2fs_read_bitmaps(ext2_filsys fs);
+extern errcode_t ext2fs_write_bitmaps(ext2_filsys fs);
+
+/* block.c */
+extern errcode_t ext2fs_block_iterate(ext2_filsys fs,
+                                     ext2_ino_t        ino,
+                                     int       flags,
+                                     char *block_buf,
+                                     int (*func)(ext2_filsys fs,
+                                                 blk_t *blocknr,
+                                                 int   blockcnt,
+                                                 void  *priv_data),
+                                     void *priv_data);
+errcode_t ext2fs_block_iterate2(ext2_filsys fs,
+                               ext2_ino_t      ino,
+                               int     flags,
+                               char *block_buf,
+                               int (*func)(ext2_filsys fs,
+                                           blk_t       *blocknr,
+                                           e2_blkcnt_t blockcnt,
+                                           blk_t       ref_blk,
+                                           int         ref_offset,
+                                           void        *priv_data),
+                               void *priv_data);
+
+/* bmap.c */
+extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino,
+                            struct ext2_inode *inode, 
+                            char *block_buf, int bmap_flags,
+                            blk_t block, blk_t *phys_blk);
+
+
+#if 0
+/* bmove.c */
+extern errcode_t ext2fs_move_blocks(ext2_filsys fs,
+                                   ext2fs_block_bitmap reserve,
+                                   ext2fs_block_bitmap alloc_map,
+                                   int flags);
+#endif
+
+/* check_desc.c */
+extern errcode_t ext2fs_check_desc(ext2_filsys fs);
+
+/* closefs.c */
+extern errcode_t ext2fs_close(ext2_filsys fs);
+extern errcode_t ext2fs_flush(ext2_filsys fs);
+extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block);
+extern int ext2fs_super_and_bgd_loc(ext2_filsys fs, 
+                                   dgrp_t group,
+                                   blk_t *ret_super_blk,
+                                   blk_t *ret_old_desc_blk,
+                                   blk_t *ret_new_desc_blk,
+                                   int *ret_meta_bg);
+extern void ext2fs_update_dynamic_rev(ext2_filsys fs);
+
+/* cmp_bitmaps.c */
+extern errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1,
+                                            ext2fs_block_bitmap bm2);
+extern errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1,
+                                            ext2fs_inode_bitmap bm2);
+
+/* dblist.c */
+
+extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs);
+extern errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist);
+extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino,
+                                     blk_t blk, int blockcnt);
+extern void ext2fs_dblist_sort(ext2_dblist dblist,
+                              EXT2_QSORT_TYPE (*sortfunc)(const void *,
+                                                          const void *));
+extern errcode_t ext2fs_dblist_iterate(ext2_dblist dblist,
+       int (*func)(ext2_filsys fs, struct ext2_db_entry *db_info,
+                   void        *priv_data),
+       void *priv_data);
+extern errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino,
+                                     blk_t blk, int blockcnt);
+extern errcode_t ext2fs_copy_dblist(ext2_dblist src,
+                                   ext2_dblist *dest);
+extern int ext2fs_dblist_count(ext2_dblist dblist);
+
+/* dblist_dir.c */
+extern errcode_t
+       ext2fs_dblist_dir_iterate(ext2_dblist dblist,
+                                 int   flags,
+                                 char  *block_buf,
+                                 int (*func)(ext2_ino_t        dir,
+                                             int               entry,
+                                             struct ext2_dir_entry *dirent,
+                                             int       offset,
+                                             int       blocksize,
+                                             char      *buf,
+                                             void      *priv_data),
+                                 void *priv_data);
+
+/* dirblock.c */
+extern errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block,
+                                      void *buf);
+extern errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block,
+                                       void *buf, int flags);
+extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block,
+                                       void *buf);
+extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block,
+                                        void *buf, int flags);
+
+/* dirhash.c */
+extern errcode_t ext2fs_dirhash(int version, const char *name, int len,
+                               const __u32 *seed,
+                               ext2_dirhash_t *ret_hash,
+                               ext2_dirhash_t *ret_minor_hash);
+
+
+/* dir_iterate.c */
+extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, 
+                             ext2_ino_t dir,
+                             int flags,
+                             char *block_buf,
+                             int (*func)(struct ext2_dir_entry *dirent,
+                                         int   offset,
+                                         int   blocksize,
+                                         char  *buf,
+                                         void  *priv_data),
+                             void *priv_data);
+extern errcode_t ext2fs_dir_iterate2(ext2_filsys fs, 
+                             ext2_ino_t dir,
+                             int flags,
+                             char *block_buf,
+                             int (*func)(ext2_ino_t    dir,
+                                         int   entry,
+                                         struct ext2_dir_entry *dirent,
+                                         int   offset,
+                                         int   blocksize,
+                                         char  *buf,
+                                         void  *priv_data),
+                             void *priv_data);
+
+/* dupfs.c */
+extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest);
+
+/* expanddir.c */
+extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir);
+
+/* ext_attr.c */
+extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf);
+extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block,
+                                      void *buf);
+extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
+                                          char *block_buf,
+                                          int adjust, __u32 *newcount);
+
+/* fileio.c */
+extern errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
+                                  struct ext2_inode *inode,
+                                  int flags, ext2_file_t *ret);
+extern errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
+                                 int flags, ext2_file_t *ret);
+extern ext2_filsys ext2fs_file_get_fs(ext2_file_t file);
+extern errcode_t ext2fs_file_close(ext2_file_t file);
+extern errcode_t ext2fs_file_flush(ext2_file_t file);
+extern errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
+                                 unsigned int wanted, unsigned int *got);
+extern errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
+                                  unsigned int nbytes, unsigned int *written);
+extern errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
+                                  int whence, __u64 *ret_pos);
+extern errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
+                                  int whence, ext2_off_t *ret_pos);
+errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size);
+extern ext2_off_t ext2fs_file_get_size(ext2_file_t file);
+extern errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size);
+
+/* finddev.c */
+extern char *ext2fs_find_block_device(dev_t device);
+
+/* flushb.c */
+extern errcode_t ext2fs_sync_device(int fd, int flushb);
+
+/* freefs.c */
+extern void ext2fs_free(ext2_filsys fs);
+extern void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap);
+extern void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap);
+extern void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap);
+extern void ext2fs_free_dblist(ext2_dblist dblist);
+extern void ext2fs_badblocks_list_free(ext2_badblocks_list bb);
+extern void ext2fs_u32_list_free(ext2_u32_list bb);
+
+/* getsize.c */
+extern errcode_t ext2fs_get_device_size(const char *file, int blocksize,
+                                       blk_t *retblocks);
+
+/* getsectsize.c */
+errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize);
+
+/* imager.c */
+extern errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags);
+extern errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags);
+
+/* ind_block.c */
+errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf);
+errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf);
+
+/* initialize.c */
+extern errcode_t ext2fs_initialize(const char *name, int flags,
+                                  struct ext2_super_block *param,
+                                  io_manager manager, ext2_filsys *ret_fs);
+
+/* icount.c */
+extern void ext2fs_free_icount(ext2_icount_t icount);
+extern errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, 
+                                      unsigned int size,
+                                      ext2_icount_t hint, ext2_icount_t *ret);
+extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, 
+                                     unsigned int size,
+                                     ext2_icount_t *ret);
+extern errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino,
+                                    __u16 *ret);
+extern errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
+                                        __u16 *ret);
+extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
+                                        __u16 *ret);
+extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
+                                    __u16 count);
+extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount);
+errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *);
+
+/* inode.c */
+extern errcode_t ext2fs_flush_icache(ext2_filsys fs);
+extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, 
+                                           ext2_ino_t *ino,
+                                           struct ext2_inode *inode, 
+                                           int bufsize);
+extern errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
+                                 ext2_inode_scan *ret_scan);
+extern void ext2fs_close_inode_scan(ext2_inode_scan scan);
+extern errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino,
+                              struct ext2_inode *inode);
+extern errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
+                                                  int  group);
+extern void ext2fs_set_inode_callback
+       (ext2_inode_scan scan,
+        errcode_t (*done_group)(ext2_filsys fs,
+                                ext2_inode_scan scan,
+                                dgrp_t group,
+                                void * priv_data),
+        void *done_group_data);
+extern int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
+                                  int clear_flags);
+extern errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
+                                       struct ext2_inode * inode, 
+                                       int bufsize);
+extern errcode_t ext2fs_read_inode (ext2_filsys fs, ext2_ino_t ino,
+                           struct ext2_inode * inode);
+extern errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
+                                        struct ext2_inode * inode, 
+                                        int bufsize);
+extern errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
+                           struct ext2_inode * inode);
+extern errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino,
+                           struct ext2_inode * inode);
+extern errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks);
+extern errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino);
+
+/* inode_io.c */
+extern io_manager inode_io_manager;
+extern errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino, 
+                                       char **name);
+extern errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino,
+                                        struct ext2_inode *inode,
+                                        char **name);
+       
+/* ismounted.c */
+extern errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags);
+extern errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags,
+                                         char *mtpt, int mtlen);
+
+/* namei.c */
+extern errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name,
+                        int namelen, char *buf, ext2_ino_t *inode);
+extern errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+                       const char *name, ext2_ino_t *inode);
+errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+                             const char *name, ext2_ino_t *inode);
+extern errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+                       ext2_ino_t inode, ext2_ino_t *res_inode);
+
+/* native.c */
+int ext2fs_native_flag(void);
+
+/* newdir.c */
+extern errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
+                               ext2_ino_t parent_ino, char **block);
+
+/* mkdir.c */
+extern errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
+                             const char *name);
+
+/* mkjournal.c */
+extern errcode_t ext2fs_create_journal_superblock(ext2_filsys fs,
+                                                 __u32 size, int flags,
+                                                 char  **ret_jsb);
+extern errcode_t ext2fs_add_journal_device(ext2_filsys fs,
+                                          ext2_filsys journal_dev);
+extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size,
+                                         int flags);
+
+/* openfs.c */
+extern errcode_t ext2fs_open(const char *name, int flags, int superblock,
+                            unsigned int block_size, io_manager manager,
+                            ext2_filsys *ret_fs);
+extern errcode_t ext2fs_open2(const char *name, const char *io_options, 
+                             int flags, int superblock, 
+                             unsigned int block_size, io_manager manager,
+                             ext2_filsys *ret_fs);
+extern blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, 
+                                        dgrp_t i);
+errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io);
+errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io);
+errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io);
+
+/* get_pathname.c */
+extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino,
+                              char **name);
+
+/* link.c */
+errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
+                     ext2_ino_t ino, int flags);
+errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name,
+                       ext2_ino_t ino, int flags);
+
+/* read_bb.c */
+extern errcode_t ext2fs_read_bb_inode(ext2_filsys fs,
+                                     ext2_badblocks_list *bb_list);
+
+/* read_bb_file.c */
+extern errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, 
+                                     ext2_badblocks_list *bb_list,
+                                     void *priv_data,
+                                     void (*invalid)(ext2_filsys fs,
+                                                     blk_t blk,
+                                                     char *badstr,
+                                                     void *priv_data));
+extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, 
+                                    ext2_badblocks_list *bb_list,
+                                    void (*invalid)(ext2_filsys fs,
+                                                    blk_t blk));
+
+/* res_gdt.c */
+extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs);
+
+/* rs_bitmap.c */
+extern errcode_t ext2fs_resize_generic_bitmap(__u32 new_end,
+                                             __u32 new_real_end,
+                                             ext2fs_generic_bitmap bmap);
+extern errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end,
+                                           ext2fs_inode_bitmap bmap);
+extern errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end,
+                                           ext2fs_block_bitmap bmap);
+extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src,
+                                   ext2fs_generic_bitmap *dest);
+
+/* swapfs.c */
+extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, 
+                                int has_header);
+extern void ext2fs_swap_super(struct ext2_super_block * super);
+extern void ext2fs_swap_group_desc(struct ext2_group_desc *gdp);
+extern void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
+                                  struct ext2_inode_large *f, int hostorder,
+                                  int bufsize);
+extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t,
+                             struct ext2_inode *f, int hostorder);
+
+/* valid_blk.c */
+extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode);
+
+/* version.c */
+extern int ext2fs_parse_version_string(const char *ver_string);
+extern int ext2fs_get_library_version(const char **ver_string,
+                                     const char **date_string);
+
+/* write_bb_file.c */
+extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list,
+                                     unsigned int flags,
+                                     FILE *f);
+
+
+/* inline functions */
+extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr);
+extern errcode_t ext2fs_free_mem(void *ptr);
+extern errcode_t ext2fs_resize_mem(unsigned long old_size,
+                                  unsigned long size, void *ptr);
+extern void ext2fs_mark_super_dirty(ext2_filsys fs);
+extern void ext2fs_mark_changed(ext2_filsys fs);
+extern int ext2fs_test_changed(ext2_filsys fs);
+extern void ext2fs_mark_valid(ext2_filsys fs);
+extern void ext2fs_unmark_valid(ext2_filsys fs);
+extern int ext2fs_test_valid(ext2_filsys fs);
+extern void ext2fs_mark_ib_dirty(ext2_filsys fs);
+extern void ext2fs_mark_bb_dirty(ext2_filsys fs);
+extern int ext2fs_test_ib_dirty(ext2_filsys fs);
+extern int ext2fs_test_bb_dirty(ext2_filsys fs);
+extern int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk);
+extern int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino);
+extern blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
+                                     struct ext2_inode *inode);
+
+/*
+ * The actual inlined functions definitions themselves...
+ *
+ * If NO_INLINE_FUNCS is defined, then we won't try to do inline
+ * functions at all!
+ */
+#if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS))
+#ifdef INCLUDE_INLINE_FUNCS
+#define _INLINE_ extern
+#else
+#ifdef __GNUC__
+#define _INLINE_ extern __inline__
+#else                          /* For Watcom C */
+#define _INLINE_ extern inline
+#endif
+#endif
+
+#ifndef EXT2_CUSTOM_MEMORY_ROUTINES
+/*
+ *  Allocate memory
+ */
+_INLINE_ errcode_t ext2fs_get_mem(unsigned long size, void *ptr)
+{
+       void **pp = (void **)ptr;
+
+       *pp = malloc(size);
+       if (!*pp)
+               return EXT2_ET_NO_MEMORY;
+       return 0;
+}
+
+/*
+ * Free memory
+ */
+_INLINE_ errcode_t ext2fs_free_mem(void *ptr)
+{
+       void **pp = (void **)ptr;
+
+       free(*pp);
+       *pp = 0;
+       return 0;
+}
+       
+/*
+ *  Resize memory
+ */
+_INLINE_ errcode_t ext2fs_resize_mem(unsigned long EXT2FS_ATTR((unused)) old_size,
+                                    unsigned long size, void *ptr)
+{
+       void *p;
+       void **pp = (void **)ptr;
+
+       p = realloc(*pp, size);
+       if (!p)
+               return EXT2_ET_NO_MEMORY;
+       *pp = p;
+       return 0;
+}
+#endif /* Custom memory routines */
+
+/*
+ * Mark a filesystem superblock as dirty
+ */
+_INLINE_ void ext2fs_mark_super_dirty(ext2_filsys fs)
+{
+       fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_CHANGED;
+}
+
+/*
+ * Mark a filesystem as changed
+ */
+_INLINE_ void ext2fs_mark_changed(ext2_filsys fs)
+{
+       fs->flags |= EXT2_FLAG_CHANGED;
+}
+
+/*
+ * Check to see if a filesystem has changed
+ */
+_INLINE_ int ext2fs_test_changed(ext2_filsys fs)
+{
+       return (fs->flags & EXT2_FLAG_CHANGED);
+}
+
+/*
+ * Mark a filesystem as valid
+ */
+_INLINE_ void ext2fs_mark_valid(ext2_filsys fs)
+{
+       fs->flags |= EXT2_FLAG_VALID;
+}
+
+/*
+ * Mark a filesystem as NOT valid
+ */
+_INLINE_ void ext2fs_unmark_valid(ext2_filsys fs)
+{
+       fs->flags &= ~EXT2_FLAG_VALID;
+}
+
+/*
+ * Check to see if a filesystem is valid
+ */
+_INLINE_ int ext2fs_test_valid(ext2_filsys fs)
+{
+       return (fs->flags & EXT2_FLAG_VALID);
+}
+
+/*
+ * Mark the inode bitmap as dirty
+ */
+_INLINE_ void ext2fs_mark_ib_dirty(ext2_filsys fs)
+{
+       fs->flags |= EXT2_FLAG_IB_DIRTY | EXT2_FLAG_CHANGED;
+}
+
+/*
+ * Mark the block bitmap as dirty
+ */
+_INLINE_ void ext2fs_mark_bb_dirty(ext2_filsys fs)
+{
+       fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_CHANGED;
+}
+
+/*
+ * Check to see if a filesystem's inode bitmap is dirty
+ */
+_INLINE_ int ext2fs_test_ib_dirty(ext2_filsys fs)
+{
+       return (fs->flags & EXT2_FLAG_IB_DIRTY);
+}
+
+/*
+ * Check to see if a filesystem's block bitmap is dirty
+ */
+_INLINE_ int ext2fs_test_bb_dirty(ext2_filsys fs)
+{
+       return (fs->flags & EXT2_FLAG_BB_DIRTY);
+}
+
+/*
+ * Return the group # of a block
+ */
+_INLINE_ int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk)
+{
+       return (blk - fs->super->s_first_data_block) /
+               fs->super->s_blocks_per_group;
+}
+
+/*
+ * Return the group # of an inode number
+ */
+_INLINE_ int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino)
+{
+       return (ino - 1) / fs->super->s_inodes_per_group;
+}
+
+_INLINE_ blk_t ext2fs_inode_data_blocks(ext2_filsys fs,
+                                       struct ext2_inode *inode)
+{
+       return inode->i_blocks -
+              (inode->i_file_acl ? fs->blocksize >> 9 : 0);
+}
+#undef _INLINE_
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _EXT2FS_EXT2FS_H */
diff --git a/e2fsprogs/ext2fs/ext2fsP.h b/e2fsprogs/ext2fs/ext2fsP.h
new file mode 100644 (file)
index 0000000..eea32d6
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * ext2fsP.h --- private header file for ext2 library
+ * 
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include "ext2fs.h"
+
+/*
+ * Badblocks list
+ */
+struct ext2_struct_u32_list {
+       int     magic;
+       int     num;
+       int     size;
+       __u32   *list;
+       int     badblocks_flags;
+};
+
+struct ext2_struct_u32_iterate {
+       int                     magic;
+       ext2_u32_list           bb;
+       int                     ptr;
+};
+
+
+/*
+ * Directory block iterator definition
+ */
+struct ext2_struct_dblist {
+       int                     magic;
+       ext2_filsys             fs;
+       ext2_ino_t              size;
+       ext2_ino_t              count;
+       int                     sorted;
+       struct ext2_db_entry *  list;
+};
+
+/*
+ * For directory iterators
+ */
+struct dir_context {
+       ext2_ino_t              dir;
+       int             flags;
+       char            *buf;
+       int (*func)(ext2_ino_t  dir,
+                   int entry,
+                   struct ext2_dir_entry *dirent,
+                   int offset,
+                   int blocksize,
+                   char        *buf,
+                   void        *priv_data);
+       void            *priv_data;
+       errcode_t       errcode;
+};
+
+/*
+ * Inode cache structure
+ */
+struct ext2_inode_cache {
+       void *                          buffer;
+       blk_t                           buffer_blk;
+       int                             cache_last;
+       int                             cache_size;
+       int                             refcount;
+       struct ext2_inode_cache_ent     *cache;
+};
+
+struct ext2_inode_cache_ent {
+       ext2_ino_t              ino;
+       struct ext2_inode       inode;
+};
+
+/* Function prototypes */
+
+extern int ext2fs_process_dir_block(ext2_filsys        fs,
+                                   blk_t               *blocknr,
+                                   e2_blkcnt_t         blockcnt,
+                                   blk_t               ref_block,
+                                   int                 ref_offset,
+                                   void                *priv_data);
+
+
diff --git a/e2fsprogs/ext2fs/ext_attr.c b/e2fsprogs/ext2fs/ext_attr.c
new file mode 100644 (file)
index 0000000..08211c3
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * ext_attr.c --- extended attribute blocks
+ * 
+ * Copyright (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
+ *
+ * Copyright (C) 2002 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2_ext_attr.h"
+
+#include "ext2fs.h"
+
+errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf)
+{
+       errcode_t       retval;
+
+       retval = io_channel_read_blk(fs->io, block, 1, buf);
+       if (retval)
+               return retval;
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if ((fs->flags & (EXT2_FLAG_SWAP_BYTES|
+                         EXT2_FLAG_SWAP_BYTES_READ)) != 0)
+               ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1);
+#endif
+       return 0;
+}
+
+errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf)
+{
+       errcode_t       retval;
+       char            *write_buf;
+       char            *buf = NULL;
+
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+           (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) {
+               retval = ext2fs_get_mem(fs->blocksize, &buf);
+               if (retval)
+                       return retval;
+               write_buf = buf;
+               ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1);
+       } else
+#endif
+               write_buf = (char *) inbuf;
+       retval = io_channel_write_blk(fs->io, block, 1, write_buf);
+       if (buf)
+               ext2fs_free_mem(&buf);
+       if (!retval)
+               ext2fs_mark_changed(fs);
+       return retval;
+}
+
+/*
+ * This function adjusts the reference count of the EA block.
+ */
+errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk,
+                                   char *block_buf, int adjust,
+                                   __u32 *newcount)
+{
+       errcode_t       retval;
+       struct ext2_ext_attr_header *header;
+       char    *buf = 0;
+
+       if ((blk >= fs->super->s_blocks_count) ||
+           (blk < fs->super->s_first_data_block))
+               return EXT2_ET_BAD_EA_BLOCK_NUM;
+
+       if (!block_buf) {
+               retval = ext2fs_get_mem(fs->blocksize, &buf);
+               if (retval)
+                       return retval;
+               block_buf = buf;
+       }
+
+       retval = ext2fs_read_ext_attr(fs, blk, block_buf);
+       if (retval)
+               goto errout;
+
+       header = (struct ext2_ext_attr_header *) block_buf;
+       header->h_refcount += adjust;
+       if (newcount)
+               *newcount = header->h_refcount;
+
+       retval = ext2fs_write_ext_attr(fs, blk, block_buf);
+       if (retval)
+               goto errout;
+
+errout:
+       if (buf)
+               ext2fs_free_mem(&buf);
+       return retval;
+}
diff --git a/e2fsprogs/ext2fs/fileio.c b/e2fsprogs/ext2fs/fileio.c
new file mode 100644 (file)
index 0000000..3e42cbc
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * fileio.c --- Simple file I/O routines
+ * 
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct ext2_file {
+       errcode_t               magic;
+       ext2_filsys             fs;
+       ext2_ino_t              ino;
+       struct ext2_inode       inode;
+       int                     flags;
+       __u64                   pos;
+       blk_t                   blockno;
+       blk_t                   physblock;
+       char                    *buf;
+};
+
+#define BMAP_BUFFER (file->buf + fs->blocksize)
+
+errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino,
+                           struct ext2_inode *inode,
+                           int flags, ext2_file_t *ret)
+{
+       ext2_file_t     file;
+       errcode_t       retval;
+
+       /*
+        * Don't let caller create or open a file for writing if the
+        * filesystem is read-only.
+        */
+       if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) &&
+           !(fs->flags & EXT2_FLAG_RW))
+               return EXT2_ET_RO_FILSYS;
+
+       retval = ext2fs_get_mem(sizeof(struct ext2_file), &file);
+       if (retval)
+               return retval;
+       
+       memset(file, 0, sizeof(struct ext2_file));
+       file->magic = EXT2_ET_MAGIC_EXT2_FILE;
+       file->fs = fs;
+       file->ino = ino;
+       file->flags = flags & EXT2_FILE_MASK;
+
+       if (inode) {
+               memcpy(&file->inode, inode, sizeof(struct ext2_inode));
+       } else {
+               retval = ext2fs_read_inode(fs, ino, &file->inode);
+               if (retval)
+                       goto fail;
+       }
+       
+       retval = ext2fs_get_mem(fs->blocksize * 3, &file->buf);
+       if (retval)
+               goto fail;
+
+       *ret = file;
+       return 0;
+       
+fail:
+       if (file->buf)
+               ext2fs_free_mem(&file->buf);
+       ext2fs_free_mem(&file);
+       return retval;
+}
+
+errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino,
+                          int flags, ext2_file_t *ret)
+{
+       return ext2fs_file_open2(fs, ino, NULL, flags, ret);
+}
+
+/*
+ * This function returns the filesystem handle of a file from the structure
+ */
+ext2_filsys ext2fs_file_get_fs(ext2_file_t file)
+{
+       if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
+               return 0;
+       return file->fs;
+}
+
+/*
+ * This function flushes the dirty block buffer out to disk if
+ * necessary.
+ */
+errcode_t ext2fs_file_flush(ext2_file_t file)
+{
+       errcode_t       retval;
+       ext2_filsys fs;
+       
+       EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+       fs = file->fs;
+
+       if (!(file->flags & EXT2_FILE_BUF_VALID) ||
+           !(file->flags & EXT2_FILE_BUF_DIRTY))
+               return 0;
+
+       /*
+        * OK, the physical block hasn't been allocated yet.
+        * Allocate it.
+        */
+       if (!file->physblock) {
+               retval = ext2fs_bmap(fs, file->ino, &file->inode,
+                                    BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0,
+                                    file->blockno, &file->physblock);
+               if (retval)
+                       return retval;
+       }
+
+       retval = io_channel_write_blk(fs->io, file->physblock,
+                                     1, file->buf);
+       if (retval)
+               return retval;
+
+       file->flags &= ~EXT2_FILE_BUF_DIRTY;
+
+       return retval;
+}
+
+/*
+ * This function synchronizes the file's block buffer and the current
+ * file position, possibly invalidating block buffer if necessary
+ */
+static errcode_t sync_buffer_position(ext2_file_t file)
+{
+       blk_t   b;
+       errcode_t       retval;
+
+       b = file->pos / file->fs->blocksize;
+       if (b != file->blockno) {
+               retval = ext2fs_file_flush(file);
+               if (retval)
+                       return retval;
+               file->flags &= ~EXT2_FILE_BUF_VALID;
+       }
+       file->blockno = b;
+       return 0;
+}
+
+/*
+ * This function loads the file's block buffer with valid data from
+ * the disk as necessary.
+ *
+ * If dontfill is true, then skip initializing the buffer since we're
+ * going to be replacing its entire contents anyway.  If set, then the
+ * function basically only sets file->physblock and EXT2_FILE_BUF_VALID
+ */
+#define DONTFILL 1
+static errcode_t load_buffer(ext2_file_t file, int dontfill)
+{
+       ext2_filsys     fs = file->fs;
+       errcode_t       retval;
+
+       if (!(file->flags & EXT2_FILE_BUF_VALID)) {
+               retval = ext2fs_bmap(fs, file->ino, &file->inode,
+                                    BMAP_BUFFER, 0, file->blockno,
+                                    &file->physblock);
+               if (retval)
+                       return retval;
+               if (!dontfill) {
+                       if (file->physblock) {
+                               retval = io_channel_read_blk(fs->io,
+                                                            file->physblock, 
+                                                            1, file->buf);
+                               if (retval)
+                                       return retval;
+                       } else
+                               memset(file->buf, 0, fs->blocksize);
+               }
+               file->flags |= EXT2_FILE_BUF_VALID;
+       }
+       return 0;
+}
+       
+
+errcode_t ext2fs_file_close(ext2_file_t file)
+{
+       errcode_t       retval;
+       
+       EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+
+       retval = ext2fs_file_flush(file);
+       
+       if (file->buf)
+               ext2fs_free_mem(&file->buf);
+       ext2fs_free_mem(&file);
+
+       return retval;
+}
+
+
+errcode_t ext2fs_file_read(ext2_file_t file, void *buf,
+                          unsigned int wanted, unsigned int *got)
+{
+       ext2_filsys     fs;
+       errcode_t       retval = 0;
+       unsigned int    start, c, count = 0;
+       __u64           left;
+       char            *ptr = (char *) buf;
+
+       EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+       fs = file->fs;
+
+       while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) {
+               retval = sync_buffer_position(file);
+               if (retval)
+                       goto fail;
+               retval = load_buffer(file, 0);
+               if (retval)
+                       goto fail;
+
+               start = file->pos % fs->blocksize;
+               c = fs->blocksize - start;
+               if (c > wanted)
+                       c = wanted;
+               left = EXT2_I_SIZE(&file->inode) - file->pos ;
+               if (c > left)
+                       c = left;
+       
+               memcpy(ptr, file->buf+start, c);
+               file->pos += c;
+               ptr += c;
+               count += c;
+               wanted -= c;
+       }
+       
+fail:
+       if (got)
+               *got = count;
+       return retval;
+}
+
+
+errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
+                           unsigned int nbytes, unsigned int *written)
+{
+       ext2_filsys     fs;
+       errcode_t       retval = 0;
+       unsigned int    start, c, count = 0;
+       const char      *ptr = (const char *) buf;
+
+       EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+       fs = file->fs;
+
+       if (!(file->flags & EXT2_FILE_WRITE))
+               return EXT2_ET_FILE_RO;
+
+       while (nbytes > 0) {
+               retval = sync_buffer_position(file);
+               if (retval)
+                       goto fail;
+               
+               start = file->pos % fs->blocksize;
+               c = fs->blocksize - start;
+               if (c > nbytes)
+                       c = nbytes;
+
+               /*
+                * We only need to do a read-modify-update cycle if
+                * we're doing a partial write.
+                */
+               retval = load_buffer(file, (c == fs->blocksize));
+               if (retval)
+                       goto fail;
+
+               file->flags |= EXT2_FILE_BUF_DIRTY;
+               memcpy(file->buf+start, ptr, c);
+               file->pos += c;
+               ptr += c;
+               count += c;
+               nbytes -= c;
+       }
+       
+fail:
+       if (written)
+               *written = count;
+       return retval;
+}
+
+errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset,
+                           int whence, __u64 *ret_pos)
+{
+       EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+
+       if (whence == EXT2_SEEK_SET)
+               file->pos = offset;
+       else if (whence == EXT2_SEEK_CUR)
+               file->pos += offset;
+       else if (whence == EXT2_SEEK_END)
+               file->pos = EXT2_I_SIZE(&file->inode) + offset;
+       else
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       if (ret_pos)
+               *ret_pos = file->pos;
+
+       return 0;
+}
+
+errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
+                           int whence, ext2_off_t *ret_pos)
+{
+       __u64           loffset, ret_loffset;
+       errcode_t       retval;
+       
+       loffset = offset;
+       retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset);
+       if (ret_pos)
+               *ret_pos = (ext2_off_t) ret_loffset;
+       return retval;
+}
+
+
+/*
+ * This function returns the size of the file, according to the inode
+ */
+errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size)
+{
+       if (file->magic != EXT2_ET_MAGIC_EXT2_FILE)
+               return EXT2_ET_MAGIC_EXT2_FILE;
+       *ret_size = EXT2_I_SIZE(&file->inode);
+       return 0;
+}
+
+/*
+ * This function returns the size of the file, according to the inode
+ */
+ext2_off_t ext2fs_file_get_size(ext2_file_t file)
+{
+       __u64   size;
+
+       if (ext2fs_file_get_lsize(file, &size))
+               return 0;
+       if ((size >> 32) != 0)
+               return 0;
+       return size;
+}
+
+/*
+ * This function sets the size of the file, truncating it if necessary
+ * 
+ * XXX still need to call truncate
+ */
+errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size)
+{
+       errcode_t       retval;
+       EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE);
+       
+       file->inode.i_size = size;
+       file->inode.i_size_high = 0;
+       if (file->ino) {
+               retval = ext2fs_write_inode(file->fs, file->ino, &file->inode);
+               if (retval)
+                       return retval;
+       }
+
+       /* 
+        * XXX truncate inode if necessary
+        */
+
+       return 0;
+}
diff --git a/e2fsprogs/ext2fs/finddev.c b/e2fsprogs/ext2fs/finddev.c
new file mode 100644 (file)
index 0000000..fa2cadd
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * finddev.c -- this routine attempts to find a particular device in
+ *     /dev
+ * 
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <dirent.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct dir_list {
+       char    *name;
+       struct dir_list *next;
+};
+
+/*
+ * This function adds an entry to the directory list
+ */
+static void add_to_dirlist(const char *name, struct dir_list **list)
+{
+       struct dir_list *dp;
+
+       dp = malloc(sizeof(struct dir_list));
+       if (!dp)
+               return;
+       dp->name = malloc(strlen(name)+1);
+       if (!dp->name) {
+               free(dp);
+               return;
+       }
+       strcpy(dp->name, name);
+       dp->next = *list;
+       *list = dp;
+}
+
+/*
+ * This function frees a directory list
+ */
+static void free_dirlist(struct dir_list **list)
+{
+       struct dir_list *dp, *next;
+
+       for (dp = *list; dp; dp = next) {
+               next = dp->next;
+               free(dp->name);
+               free(dp);
+       }
+       *list = 0;
+}
+
+static int scan_dir(char *dir_name, dev_t device, struct dir_list **list,
+                   char **ret_path)
+{
+       DIR     *dir;
+       struct dirent *dp;
+       char    path[1024], *cp;
+       int     dirlen;
+       struct stat st;
+
+       dirlen = strlen(dir_name);
+       if ((dir = opendir(dir_name)) == NULL)
+               return errno;
+       dp = readdir(dir);
+       while (dp) {
+               if (dirlen + strlen(dp->d_name) + 2 >= sizeof(path))
+                       goto skip_to_next;
+               if (dp->d_name[0] == '.' &&
+                   ((dp->d_name[1] == 0) ||
+                    ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
+                       goto skip_to_next;
+               sprintf(path, "%s/%s", dir_name, dp->d_name);
+               if (stat(path, &st) < 0)
+                       goto skip_to_next;
+               if (S_ISDIR(st.st_mode))
+                       add_to_dirlist(path, list);
+               if (S_ISBLK(st.st_mode) && st.st_rdev == device) {
+                       cp = malloc(strlen(path)+1);
+                       if (!cp) {
+                               closedir(dir);
+                               return ENOMEM;
+                       }
+                       strcpy(cp, path);
+                       *ret_path = cp;
+                       goto success;
+               }
+       skip_to_next:
+               dp = readdir(dir);
+       }
+success:
+       closedir(dir);
+       return 0;
+}
+
+/*
+ * This function finds the pathname to a block device with a given
+ * device number.  It returns a pointer to allocated memory to the
+ * pathname on success, and NULL on failure.
+ */
+char *ext2fs_find_block_device(dev_t device)
+{
+       struct dir_list *list = 0, *new_list = 0;
+       struct dir_list *current;
+       char    *ret_path = 0;
+
+       /*
+        * Add the starting directories to search...
+        */
+       add_to_dirlist("/devices", &list);
+       add_to_dirlist("/devfs", &list);
+       add_to_dirlist("/dev", &list);
+       
+       while (list) {
+               current = list;
+               list = list->next;
+#ifdef DEBUG
+               printf("Scanning directory %s\n", current->name);
+#endif
+               scan_dir(current->name, device, &new_list, &ret_path);
+               free(current->name);
+               free(current);
+               if (ret_path)
+                       break;
+               /*
+                * If we're done checking at this level, descend to
+                * the next level of subdirectories. (breadth-first)
+                */
+               if (list == 0) {
+                       list = new_list;
+                       new_list = 0;
+               }
+       }
+       free_dirlist(&list);
+       free_dirlist(&new_list);
+       return ret_path;
+}
+
+       
+#ifdef DEBUG
+int main(int argc, char** argv)
+{
+       char    *devname, *tmp;
+       int     major, minor;
+       dev_t   device;
+       const char *errmsg = "Couldn't parse %s: %s\n";
+
+       if ((argc != 2) && (argc != 3)) {
+               fprintf(stderr, "Usage: %s device_number\n", argv[0]);
+               fprintf(stderr, "\t: %s major minor\n", argv[0]);
+               exit(1);
+       }
+       if (argc == 2) {
+               device = strtoul(argv[1], &tmp, 0);
+               if (*tmp) {
+                       fprintf(stderr, errmsg, "device number", argv[1]);
+                       exit(1);
+               }
+       } else {
+               major = strtoul(argv[1], &tmp, 0);
+               if (*tmp) {
+                       fprintf(stderr, errmsg, "major number", argv[1]);
+                       exit(1);
+               }
+               minor = strtoul(argv[2], &tmp, 0);
+               if (*tmp) {
+                       fprintf(stderr, errmsg, "minor number", argv[2]);
+                       exit(1);
+               }
+               device = makedev(major, minor);
+               printf("Looking for device 0x%04x (%d:%d)\n", device,
+                      major, minor);
+       }
+       devname = ext2fs_find_block_device(device);
+       if (devname) {
+               printf("Found device!  %s\n", devname);
+               free(devname);
+       } else {
+               printf("Couldn't find device.\n");
+       }
+       return 0;
+}
+       
+#endif
diff --git a/e2fsprogs/ext2fs/flushb.c b/e2fsprogs/ext2fs/flushb.c
new file mode 100644 (file)
index 0000000..1882795
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * flushb.c --- Hides system-dependent information for both syncing a
+ *     device to disk and to flush any buffers from disk cache.
+ * 
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#if HAVE_SYS_MOUNT_H
+#include <sys/param.h>
+#include <sys/mount.h>         /* This may define BLKFLSBUF */
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * For Linux, define BLKFLSBUF and FDFLUSH if necessary, since 
+ * not all portable header file does so for us.  This really should be
+ * fixed in the glibc header files.  (Recent glibcs appear to define
+ * BLKFLSBUF in sys/mount.h, but FDFLUSH still doesn't seem to be
+ * defined anywhere portable.)  Until then....
+ */
+#ifdef __linux__
+#ifndef BLKFLSBUF
+#define BLKFLSBUF      _IO(0x12,97)    /* flush buffer cache */
+#endif
+#ifndef FDFLUSH
+#define FDFLUSH                _IO(2,0x4b)     /* flush floppy disk */
+#endif
+#endif
+
+/*
+ * This function will sync a device/file, and optionally attempt to
+ * flush the buffer cache.  The latter is basically only useful for
+ * system benchmarks and for torturing systems in burn-in tests.  :)
+ */
+errcode_t ext2fs_sync_device(int fd, int flushb)
+{
+       /*
+        * We always sync the device in case we're running on old
+        * kernels for which we can lose data if we don't.  (There
+        * still is a race condition for those kernels, but this
+        * reduces it greatly.)
+        */
+       if (fsync (fd) == -1)
+               return errno;
+
+       if (flushb) {
+
+#ifdef BLKFLSBUF
+               if (ioctl (fd, BLKFLSBUF, 0) == 0)
+                       return 0;
+#else
+#ifdef __GNUC__
+ #warning BLKFLSBUF not defined
+#endif /* __GNUC__ */
+#endif
+#ifdef FDFLUSH
+               ioctl (fd, FDFLUSH, 0);   /* In case this is a floppy */
+#else
+#ifdef __GNUC__
+ #warning FDFLUSH not defined
+#endif /* __GNUC__ */
+#endif
+       }
+       return 0;
+}
diff --git a/e2fsprogs/ext2fs/freefs.c b/e2fsprogs/ext2fs/freefs.c
new file mode 100644 (file)
index 0000000..029ffaa
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * freefs.c --- free an ext2 filesystem
+ * 
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache);
+
+void ext2fs_free(ext2_filsys fs)
+{
+       if (!fs || (fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS))
+               return;
+       if (fs->image_io != fs->io) {
+               if (fs->image_io)
+                       io_channel_close(fs->image_io);
+       }
+       if (fs->io) {
+               io_channel_close(fs->io);
+       }
+       if (fs->device_name)
+               ext2fs_free_mem(&fs->device_name);
+       if (fs->super)
+               ext2fs_free_mem(&fs->super);
+       if (fs->orig_super)
+               ext2fs_free_mem(&fs->orig_super);
+       if (fs->group_desc)
+               ext2fs_free_mem(&fs->group_desc);
+       if (fs->block_map)
+               ext2fs_free_block_bitmap(fs->block_map);
+       if (fs->inode_map)
+               ext2fs_free_inode_bitmap(fs->inode_map);
+
+       if (fs->badblocks)
+               ext2fs_badblocks_list_free(fs->badblocks);
+       fs->badblocks = 0;
+
+       if (fs->dblist)
+               ext2fs_free_dblist(fs->dblist);
+
+       if (fs->icache)
+               ext2fs_free_inode_cache(fs->icache);
+       
+       fs->magic = 0;
+
+       ext2fs_free_mem(&fs);
+}
+
+void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap)
+{
+       if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_GENERIC_BITMAP))
+               return;
+
+       bitmap->magic = 0;
+       if (bitmap->description) {
+               ext2fs_free_mem(&bitmap->description);
+               bitmap->description = 0;
+       }
+       if (bitmap->bitmap) {
+               ext2fs_free_mem(&bitmap->bitmap);
+               bitmap->bitmap = 0;
+       }
+       ext2fs_free_mem(&bitmap);
+}
+
+void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap)
+{
+       if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_INODE_BITMAP))
+               return;
+
+       bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+       ext2fs_free_generic_bitmap(bitmap);
+}
+
+void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap)
+{
+       if (!bitmap || (bitmap->magic != EXT2_ET_MAGIC_BLOCK_BITMAP))
+               return;
+
+       bitmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+       ext2fs_free_generic_bitmap(bitmap);
+}
+
+/*
+ * Free the inode cache structure
+ */
+static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache)
+{
+       if (--icache->refcount)
+               return;
+       if (icache->buffer)
+               ext2fs_free_mem(&icache->buffer);
+       if (icache->cache)
+               ext2fs_free_mem(&icache->cache);
+       icache->buffer_blk = 0;
+       ext2fs_free_mem(&icache);
+}
+
+/*
+ * This procedure frees a badblocks list.
+ */
+void ext2fs_u32_list_free(ext2_u32_list bb)
+{
+       if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST)
+               return;
+
+       if (bb->list)
+               ext2fs_free_mem(&bb->list);
+       bb->list = 0;
+       ext2fs_free_mem(&bb);
+}
+
+void ext2fs_badblocks_list_free(ext2_badblocks_list bb)
+{
+       ext2fs_u32_list_free((ext2_u32_list) bb);
+}
+
+
+/*
+ * Free a directory block list
+ */
+void ext2fs_free_dblist(ext2_dblist dblist)
+{
+       if (!dblist || (dblist->magic != EXT2_ET_MAGIC_DBLIST))
+               return;
+
+       if (dblist->list)
+               ext2fs_free_mem(&dblist->list);
+       dblist->list = 0;
+       if (dblist->fs && dblist->fs->dblist == dblist)
+               dblist->fs->dblist = 0;
+       dblist->magic = 0;
+       ext2fs_free_mem(&dblist);
+}
+
diff --git a/e2fsprogs/ext2fs/gen_bitmap.c b/e2fsprogs/ext2fs/gen_bitmap.c
new file mode 100644 (file)
index 0000000..700affa
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * gen_bitmap.c --- Generic bitmap routines that used to be inlined.
+ * 
+ * Copyright (C) 2001 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+                                        __u32 bitno)
+{
+       if ((bitno < bitmap->start) || (bitno > bitmap->end)) {
+               ext2fs_warn_bitmap2(bitmap, EXT2FS_MARK_ERROR, bitno);
+               return 0;
+       }
+       return ext2fs_set_bit(bitno - bitmap->start, bitmap->bitmap);
+}
+
+int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap,
+                                          blk_t bitno)
+{
+       if ((bitno < bitmap->start) || (bitno > bitmap->end)) {
+               ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno);
+               return 0;
+       }
+       return ext2fs_clear_bit(bitno - bitmap->start, bitmap->bitmap);
+}
diff --git a/e2fsprogs/ext2fs/get_pathname.c b/e2fsprogs/ext2fs/get_pathname.c
new file mode 100644 (file)
index 0000000..23f593f
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * get_pathname.c --- do directry/inode -> name translation
+ * 
+ * Copyright (C) 1993, 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ *
+ *     ext2fs_get_pathname(fs, dir, ino, name)
+ *
+ *     This function translates takes two inode numbers into a
+ *     string, placing the result in <name>.  <dir> is the containing
+ *     directory inode, and <ino> is the inode number itself.  If
+ *     <ino> is zero, then ext2fs_get_pathname will return pathname
+ *     of the the directory <dir>.
+ * 
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct get_pathname_struct {
+       ext2_ino_t      search_ino;
+       ext2_ino_t      parent;
+       char            *name;
+       errcode_t       errcode;
+};
+
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+static int get_pathname_proc(struct ext2_dir_entry *dirent,
+                            int        offset EXT2FS_ATTR((unused)),
+                            int        blocksize EXT2FS_ATTR((unused)),
+                            char       *buf EXT2FS_ATTR((unused)),
+                            void       *priv_data)
+{
+       struct get_pathname_struct      *gp;
+       errcode_t                       retval;
+
+       gp = (struct get_pathname_struct *) priv_data;
+
+       if (((dirent->name_len & 0xFF) == 2) &&
+           !strncmp(dirent->name, "..", 2))
+               gp->parent = dirent->inode;
+       if (dirent->inode == gp->search_ino) {
+               retval = ext2fs_get_mem((dirent->name_len & 0xFF) + 1,
+                                       &gp->name);
+               if (retval) {
+                       gp->errcode = retval;
+                       return DIRENT_ABORT;
+               }
+               strncpy(gp->name, dirent->name, (dirent->name_len & 0xFF));
+               gp->name[dirent->name_len & 0xFF] = '\0';
+               return DIRENT_ABORT;
+       }
+       return 0;
+}
+
+static errcode_t ext2fs_get_pathname_int(ext2_filsys fs, ext2_ino_t dir, 
+                                        ext2_ino_t ino, int maxdepth, 
+                                        char *buf, char **name)
+{
+       struct get_pathname_struct gp;
+       char    *parent_name, *ret;
+       errcode_t       retval;
+
+       if (dir == ino) {
+               retval = ext2fs_get_mem(2, name);
+               if (retval)
+                       return retval;
+               strcpy(*name, (dir == EXT2_ROOT_INO) ? "/" : ".");
+               return 0;
+       }
+
+       if (!dir || (maxdepth < 0)) {
+               retval = ext2fs_get_mem(4, name);
+               if (retval)
+                       return retval;
+               strcpy(*name, "...");
+               return 0;
+       }
+
+       gp.search_ino = ino;
+       gp.parent = 0;
+       gp.name = 0;
+       gp.errcode = 0;
+       
+       retval = ext2fs_dir_iterate(fs, dir, 0, buf, get_pathname_proc, &gp);
+       if (retval)
+               goto cleanup;
+       if (gp.errcode) {
+               retval = gp.errcode;
+               goto cleanup;
+       }
+
+       retval = ext2fs_get_pathname_int(fs, gp.parent, dir, maxdepth-1,
+                                        buf, &parent_name);
+       if (retval)
+               goto cleanup;
+       if (!ino) {
+               *name = parent_name;
+               return 0;
+       }
+       
+       if (gp.name) 
+               retval = ext2fs_get_mem(strlen(parent_name)+strlen(gp.name)+2,
+                                       &ret);
+       else
+               retval = ext2fs_get_mem(strlen(parent_name)+5, &ret);
+       if (retval)
+               goto cleanup;
+       
+       ret[0] = 0;
+       if (parent_name[1])
+               strcat(ret, parent_name);
+       strcat(ret, "/");
+       if (gp.name)
+               strcat(ret, gp.name);
+       else
+               strcat(ret, "???");
+       *name = ret;
+       ext2fs_free_mem(&parent_name);
+       retval = 0;
+       
+cleanup:
+       if (gp.name)
+               ext2fs_free_mem(&gp.name);
+       return retval;
+}
+
+errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino,
+                             char **name)
+{
+       char    *buf;
+       errcode_t       retval;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       retval = ext2fs_get_mem(fs->blocksize, &buf);
+       if (retval)
+               return retval;
+       if (dir == ino)
+               ino = 0;
+       retval = ext2fs_get_pathname_int(fs, dir, ino, 32, buf, name);
+       ext2fs_free_mem(&buf);
+       return retval;
+       
+}
diff --git a/e2fsprogs/ext2fs/getsectsize.c b/e2fsprogs/ext2fs/getsectsize.c
new file mode 100644 (file)
index 0000000..77a9e3d
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * getsectsize.c --- get the sector size of a device.
+ * 
+ * Copyright (C) 1995, 1995 Theodore Ts'o.
+ * Copyright (C) 2003 VMware, Inc.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_LINUX_FD_H
+#include <sys/ioctl.h>
+#include <linux/fd.h>
+#endif
+
+#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
+#define BLKSSZGET  _IO(0x12,104)/* get block device sector size */
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Returns the number of blocks in a partition
+ */
+errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize)
+{
+       int     fd;
+
+#ifdef CONFIG_LFS
+       fd = open64(file, O_RDONLY);
+#else
+       fd = open(file, O_RDONLY);
+#endif
+       if (fd < 0)
+               return errno;
+
+#ifdef BLKSSZGET
+       if (ioctl(fd, BLKSSZGET, sectsize) >= 0) {
+               close(fd);
+               return 0;
+       }
+#endif
+       *sectsize = 0;
+       close(fd);
+       return 0;
+}
diff --git a/e2fsprogs/ext2fs/getsize.c b/e2fsprogs/ext2fs/getsize.c
new file mode 100644 (file)
index 0000000..036d926
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * getsize.c --- get the size of a partition.
+ * 
+ * Copyright (C) 1995, 1995 Theodore Ts'o.
+ * Copyright (C) 2003 VMware, Inc.
+ *
+ * Windows version of ext2fs_get_device_size by Chris Li, VMware.
+ * 
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+#include <sys/disklabel.h>
+#endif
+#ifdef HAVE_SYS_DISK_H
+#ifdef HAVE_SYS_QUEUE_H
+#include <sys/queue.h> /* for LIST_HEAD */
+#endif
+#include <sys/disk.h>
+#endif
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+
+#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
+#define BLKGETSIZE _IO(0x12,96)        /* return device size */
+#endif
+
+#if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64)
+#define BLKGETSIZE64 _IOR(0x12,114,size_t)     /* return device size in bytes (u64 *arg) */
+#endif
+
+#ifdef APPLE_DARWIN
+#define BLKGETSIZE DKIOCGETBLOCKCOUNT32
+#endif /* APPLE_DARWIN */
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#if defined(__CYGWIN__) || defined (WIN32)
+#include "windows.h"
+#include "winioctl.h"
+
+#if (_WIN32_WINNT >= 0x0500)
+#define HAVE_GET_FILE_SIZE_EX 1
+#endif
+
+errcode_t ext2fs_get_device_size(const char *file, int blocksize,
+                                blk_t *retblocks)
+{
+       HANDLE dev;
+       PARTITION_INFORMATION pi;
+       DISK_GEOMETRY gi;
+       DWORD retbytes;
+#ifdef HAVE_GET_FILE_SIZE_EX
+       LARGE_INTEGER filesize;
+#else
+       DWORD filesize;
+#endif /* HAVE_GET_FILE_SIZE_EX */
+
+       dev = CreateFile(file, GENERIC_READ, 
+                        FILE_SHARE_READ | FILE_SHARE_WRITE ,
+                        NULL,  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,  NULL); 
+       if (dev == INVALID_HANDLE_VALUE)
+               return EBADF;
+       if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO,
+                           &pi, sizeof(PARTITION_INFORMATION),
+                           &pi, sizeof(PARTITION_INFORMATION),
+                           &retbytes, NULL)) {
+
+               *retblocks = pi.PartitionLength.QuadPart / blocksize;
+       
+       } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY,
+                               &gi, sizeof(DISK_GEOMETRY),
+                               &gi, sizeof(DISK_GEOMETRY),
+                               &retbytes, NULL)) {
+
+               *retblocks = gi.BytesPerSector *
+                            gi.SectorsPerTrack *
+                            gi.TracksPerCylinder *
+                            gi.Cylinders.QuadPart / blocksize;
+
+#ifdef HAVE_GET_FILE_SIZE_EX
+       } else if (GetFileSizeEx(dev, &filesize)) {
+               *retblocks = filesize.QuadPart / blocksize;
+       }
+#else
+       } else {
+               filesize = GetFileSize(dev, NULL);
+               if (INVALID_FILE_SIZE != filesize) {
+                       *retblocks = filesize / blocksize;
+               }
+       }
+#endif /* HAVE_GET_FILE_SIZE_EX */
+
+       CloseHandle(dev);
+       return 0;
+}
+
+#else
+
+static int valid_offset (int fd, ext2_loff_t offset)
+{
+       char ch;
+
+       if (ext2fs_llseek (fd, offset, 0) < 0)
+               return 0;
+       if (read (fd, &ch, 1) < 1)
+               return 0;
+       return 1;
+}
+
+/*
+ * Returns the number of blocks in a partition
+ */
+errcode_t ext2fs_get_device_size(const char *file, int blocksize,
+                                blk_t *retblocks)
+{
+       int     fd;
+       int valid_blkgetsize64 = 1;
+#ifdef __linux__
+       struct          utsname ut;
+#endif
+       unsigned long long size64;
+       unsigned long   size;
+       ext2_loff_t high, low;
+#ifdef FDGETPRM
+       struct floppy_struct this_floppy;
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+       int part;
+       struct disklabel lab;
+       struct partition *pp;
+       char ch;
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+#ifdef CONFIG_LFS
+       fd = open64(file, O_RDONLY);
+#else
+       fd = open(file, O_RDONLY);
+#endif
+       if (fd < 0)
+               return errno;
+
+#ifdef DKIOCGETBLOCKCOUNT      /* For Apple Darwin */
+       if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) {
+               if ((sizeof(*retblocks) < sizeof(unsigned long long))
+                   && ((size64 / (blocksize / 512)) > 0xFFFFFFFF))
+                       return EFBIG;
+               close(fd);
+               *retblocks = size64 / (blocksize / 512);
+               return 0;
+       }
+#endif
+
+#ifdef BLKGETSIZE64
+#ifdef __linux__
+       if ((uname(&ut) == 0) &&
+           ((ut.release[0] == '2') && (ut.release[1] == '.') &&
+            (ut.release[2] < '6') && (ut.release[3] == '.')))
+               valid_blkgetsize64 = 0;
+#endif
+       if (valid_blkgetsize64 &&
+           ioctl(fd, BLKGETSIZE64, &size64) >= 0) {
+               if ((sizeof(*retblocks) < sizeof(unsigned long long))
+                   && ((size64 / blocksize) > 0xFFFFFFFF))
+                       return EFBIG;
+               close(fd);
+               *retblocks = size64 / blocksize;
+               return 0;
+       }
+#endif
+
+#ifdef BLKGETSIZE
+       if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+               close(fd);
+               *retblocks = size / (blocksize / 512);
+               return 0;
+       }
+#endif
+
+#ifdef FDGETPRM
+       if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
+               close(fd);
+               *retblocks = this_floppy.size / (blocksize / 512);
+               return 0;
+       }
+#endif
+
+#ifdef HAVE_SYS_DISKLABEL_H
+#if defined(DIOCGMEDIASIZE)
+       {
+           off_t ms;
+           u_int bs;
+           if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) {
+               *retblocks = ms / blocksize;
+               return 0;
+           }
+       }
+#elif defined(DIOCGDINFO)
+       /* old disklabel interface */
+       part = strlen(file) - 1;
+       if (part >= 0) {
+               ch = file[part];
+               if (isdigit(ch))
+                       part = 0;
+               else if (ch >= 'a' && ch <= 'h')
+                       part = ch - 'a';
+               else
+                       part = -1;
+       }
+       if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+               pp = &lab.d_partitions[part];
+               if (pp->p_size) {
+                       close(fd);
+                       *retblocks = pp->p_size / (blocksize / 512);
+                       return 0;
+               }
+       }
+#endif /* defined(DIOCG*) */
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+       /*
+        * OK, we couldn't figure it out by using a specialized ioctl,
+        * which is generally the best way.  So do binary search to
+        * find the size of the partition.
+        */
+       low = 0;
+       for (high = 1024; valid_offset (fd, high); high *= 2)
+               low = high;
+       while (low < high - 1)
+       {
+               const ext2_loff_t mid = (low + high) / 2;
+
+               if (valid_offset (fd, mid))
+                       low = mid;
+               else
+                       high = mid;
+       }
+       valid_offset (fd, 0);
+       close(fd);
+       size64 = low + 1;
+       if ((sizeof(*retblocks) < sizeof(unsigned long long))
+           && ((size64 / blocksize) > 0xFFFFFFFF))
+               return EFBIG;
+       *retblocks = size64 / blocksize;
+       return 0;
+}
+
+#endif /* WIN32 */
+
+#ifdef DEBUG
+int main(int argc, char **argv)
+{
+       blk_t   blocks;
+       int     retval;
+       
+       if (argc < 2) {
+               fprintf(stderr, "Usage: %s device\n", argv[0]);
+               exit(1);
+       }
+
+       retval = ext2fs_get_device_size(argv[1], 1024, &blocks);
+       if (retval) {
+               com_err(argv[0], retval,
+                       "while calling ext2fs_get_device_size");
+               exit(1);
+       }
+       printf("Device %s has %d 1k blocks.\n", argv[1], blocks);
+       exit(0);
+}
+#endif
diff --git a/e2fsprogs/ext2fs/icount.c b/e2fsprogs/ext2fs/icount.c
new file mode 100644 (file)
index 0000000..5997792
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+ * icount.c --- an efficient inode count abstraction
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * The data storage strategy used by icount relies on the observation
+ * that most inode counts are either zero (for non-allocated inodes),
+ * one (for most files), and only a few that are two or more
+ * (directories and files that are linked to more than one directory).
+ *
+ * Also, e2fsck tends to load the icount data sequentially.
+ *
+ * So, we use an inode bitmap to indicate which inodes have a count of
+ * one, and then use a sorted list to store the counts for inodes
+ * which are greater than one.
+ *
+ * We also use an optional bitmap to indicate which inodes are already
+ * in the sorted list, to speed up the use of this abstraction by
+ * e2fsck's pass 2.  Pass 2 increments inode counts as it finds them,
+ * so this extra bitmap avoids searching the sorted list to see if a
+ * particular inode is on the sorted list already.
+ */
+
+struct ext2_icount_el {
+       ext2_ino_t      ino;
+       __u16   count;
+};
+
+struct ext2_icount {
+       errcode_t               magic;
+       ext2fs_inode_bitmap     single;
+       ext2fs_inode_bitmap     multiple;
+       ext2_ino_t              count;
+       ext2_ino_t              size;
+       ext2_ino_t              num_inodes;
+       ext2_ino_t              cursor;
+       struct ext2_icount_el   *list;
+};
+
+void ext2fs_free_icount(ext2_icount_t icount)
+{
+       if (!icount)
+               return;
+
+       icount->magic = 0;
+       if (icount->list)
+               ext2fs_free_mem(&icount->list);
+       if (icount->single)
+               ext2fs_free_inode_bitmap(icount->single);
+       if (icount->multiple)
+               ext2fs_free_inode_bitmap(icount->multiple);
+       ext2fs_free_mem(&icount);
+}
+
+errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size,
+                               ext2_icount_t hint, ext2_icount_t *ret)
+{
+       ext2_icount_t   icount;
+       errcode_t       retval;
+       size_t          bytes;
+       ext2_ino_t      i;
+
+       if (hint) {
+               EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT);
+               if (hint->size > size)
+                       size = (size_t) hint->size;
+       }
+       
+       retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount);
+       if (retval)
+               return retval;
+       memset(icount, 0, sizeof(struct ext2_icount));
+
+       retval = ext2fs_allocate_inode_bitmap(fs, 0, 
+                                             &icount->single);
+       if (retval)
+               goto errout;
+
+       if (flags & EXT2_ICOUNT_OPT_INCREMENT) {
+               retval = ext2fs_allocate_inode_bitmap(fs, 0, 
+                                                     &icount->multiple);
+               if (retval)
+                       goto errout;
+       } else
+               icount->multiple = 0;
+
+       if (size) {
+               icount->size = size;
+       } else {
+               /*
+                * Figure out how many special case inode counts we will
+                * have.  We know we will need one for each directory;
+                * we also need to reserve some extra room for file links
+                */
+               retval = ext2fs_get_num_dirs(fs, &icount->size);
+               if (retval)
+                       goto errout;
+               icount->size += fs->super->s_inodes_count / 50;
+       }
+       
+       bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el));
+#if 0
+       printf("Icount allocated %d entries, %d bytes.\n",
+              icount->size, bytes);
+#endif
+       retval = ext2fs_get_mem(bytes, &icount->list);
+       if (retval)
+               goto errout;
+       memset(icount->list, 0, bytes);
+
+       icount->magic = EXT2_ET_MAGIC_ICOUNT;
+       icount->count = 0;
+       icount->cursor = 0;
+       icount->num_inodes = fs->super->s_inodes_count;
+
+       /*
+        * Populate the sorted list with those entries which were
+        * found in the hint icount (since those are ones which will
+        * likely need to be in the sorted list this time around).
+        */
+       if (hint) {
+               for (i=0; i < hint->count; i++)
+                       icount->list[i].ino = hint->list[i].ino;
+               icount->count = hint->count;
+       }
+
+       *ret = icount;
+       return 0;
+
+errout:
+       ext2fs_free_icount(icount);
+       return(retval);
+}
+
+errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, 
+                              unsigned int size,
+                              ext2_icount_t *ret)
+{
+       return ext2fs_create_icount2(fs, flags, size, 0, ret);
+}
+
+/*
+ * insert_icount_el() --- Insert a new entry into the sorted list at a
+ *     specified position.
+ */
+static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
+                                           ext2_ino_t ino, int pos)
+{
+       struct ext2_icount_el   *el;
+       errcode_t               retval;
+       ext2_ino_t                      new_size = 0;
+       int                     num;
+
+       if (icount->count >= icount->size) {
+               if (icount->count) {
+                       new_size = icount->list[(unsigned)icount->count-1].ino;
+                       new_size = (ext2_ino_t) (icount->count * 
+                               ((float) icount->num_inodes / new_size));
+               }
+               if (new_size < (icount->size + 100))
+                       new_size = icount->size + 100;
+#if 0
+               printf("Reallocating icount %d entries...\n", new_size);
+#endif 
+               retval = ext2fs_resize_mem((size_t) icount->size *
+                                          sizeof(struct ext2_icount_el),
+                                          (size_t) new_size *
+                                          sizeof(struct ext2_icount_el),
+                                          &icount->list);
+               if (retval)
+                       return 0;
+               icount->size = new_size;
+       }
+       num = (int) icount->count - pos;
+       if (num < 0)
+               return 0;       /* should never happen */
+       if (num) {
+               memmove(&icount->list[pos+1], &icount->list[pos],
+                       sizeof(struct ext2_icount_el) * num);
+       }
+       icount->count++;
+       el = &icount->list[pos];
+       el->count = 0;
+       el->ino = ino;
+       return el;
+}
+
+/*
+ * get_icount_el() --- given an inode number, try to find icount
+ *     information in the sorted list.  If the create flag is set,
+ *     and we can't find an entry, create one in the sorted list.
+ */
+static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
+                                           ext2_ino_t ino, int create)
+{
+       float   range;
+       int     low, high, mid;
+       ext2_ino_t      lowval, highval;
+
+       if (!icount || !icount->list)
+               return 0;
+
+       if (create && ((icount->count == 0) ||
+                      (ino > icount->list[(unsigned)icount->count-1].ino))) {
+               return insert_icount_el(icount, ino, (unsigned) icount->count);
+       }
+       if (icount->count == 0)
+               return 0;
+       
+       if (icount->cursor >= icount->count)
+               icount->cursor = 0;
+       if (ino == icount->list[icount->cursor].ino)
+               return &icount->list[icount->cursor++];
+#if 0
+       printf("Non-cursor get_icount_el: %u\n", ino);
+#endif
+       low = 0;
+       high = (int) icount->count-1;
+       while (low <= high) {
+#if 0
+               mid = (low+high)/2;
+#else
+               if (low == high)
+                       mid = low;
+               else {
+                       /* Interpolate for efficiency */
+                       lowval = icount->list[low].ino;
+                       highval = icount->list[high].ino;
+
+                       if (ino < lowval)
+                               range = 0;
+                       else if (ino > highval)
+                               range = 1;
+                       else 
+                               range = ((float) (ino - lowval)) /
+                                       (highval - lowval);
+                       mid = low + ((int) (range * (high-low)));
+               }
+#endif
+               if (ino == icount->list[mid].ino) {
+                       icount->cursor = mid+1;
+                       return &icount->list[mid];
+               }
+               if (ino < icount->list[mid].ino)
+                       high = mid-1;
+               else
+                       low = mid+1;
+       }
+       /*
+        * If we need to create a new entry, it should be right at
+        * low (where high will be left at low-1).
+        */
+       if (create)
+               return insert_icount_el(icount, ino, low);
+       return 0;
+}
+
+errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out)
+{
+       errcode_t       ret = 0;
+       unsigned int    i;
+       const char *bad = "bad icount";
+       
+       EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+       if (icount->count > icount->size) {
+               fprintf(out, "%s: count > size\n", bad);
+               return EXT2_ET_INVALID_ARGUMENT;
+       }
+       for (i=1; i < icount->count; i++) {
+               if (icount->list[i-1].ino >= icount->list[i].ino) {
+                       fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n",
+                               bad, i-1, icount->list[i-1].ino,
+                               i, icount->list[i].ino);
+                       ret = EXT2_ET_INVALID_ARGUMENT;
+               }
+       }
+       return ret;
+}
+
+errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret)
+{
+       struct ext2_icount_el   *el;
+       
+       EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+       if (!ino || (ino > icount->num_inodes))
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+               *ret = 1;
+               return 0;
+       }
+       if (icount->multiple &&
+           !ext2fs_test_inode_bitmap(icount->multiple, ino)) {
+               *ret = 0;
+               return 0;
+       }
+       el = get_icount_el(icount, ino, 0);
+       if (!el) {
+               *ret = 0;
+               return 0;
+       }
+       *ret = el->count;
+       return 0;
+}
+
+errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
+                                 __u16 *ret)
+{
+       struct ext2_icount_el   *el;
+
+       EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+       if (!ino || (ino > icount->num_inodes))
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+               /*
+                * If the existing count is 1, then we know there is
+                * no entry in the list.
+                */
+               el = get_icount_el(icount, ino, 1);
+               if (!el)
+                       return EXT2_ET_NO_MEMORY;
+               ext2fs_unmark_inode_bitmap(icount->single, ino);
+               el->count = 2;
+       } else if (icount->multiple) {
+               /*
+                * The count is either zero or greater than 1; if the
+                * inode is set in icount->multiple, then there should
+                * be an entry in the list, so find it using
+                * get_icount_el().
+                */
+               if (ext2fs_test_inode_bitmap(icount->multiple, ino)) {
+                       el = get_icount_el(icount, ino, 1);
+                       if (!el)
+                               return EXT2_ET_NO_MEMORY;
+                       el->count++;
+               } else {
+                       /*
+                        * The count was zero; mark the single bitmap
+                        * and return.
+                        */
+               zero_count:
+                       ext2fs_mark_inode_bitmap(icount->single, ino);
+                       if (ret)
+                               *ret = 1;
+                       return 0;
+               }
+       } else {
+               /*
+                * The count is either zero or greater than 1; try to
+                * find an entry in the list to determine which.
+                */
+               el = get_icount_el(icount, ino, 0);
+               if (!el) {
+                       /* No entry means the count was zero */
+                       goto zero_count;
+               }
+               el = get_icount_el(icount, ino, 1);
+               if (!el)
+                       return EXT2_ET_NO_MEMORY;
+               el->count++;
+       }
+       if (icount->multiple)
+               ext2fs_mark_inode_bitmap(icount->multiple, ino);
+       if (ret)
+               *ret = el->count;
+       return 0;
+}
+
+errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
+                                 __u16 *ret)
+{
+       struct ext2_icount_el   *el;
+
+       if (!ino || (ino > icount->num_inodes))
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+       if (ext2fs_test_inode_bitmap(icount->single, ino)) {
+               ext2fs_unmark_inode_bitmap(icount->single, ino);
+               if (icount->multiple)
+                       ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+               else {
+                       el = get_icount_el(icount, ino, 0);
+                       if (el)
+                               el->count = 0;
+               }
+               if (ret)
+                       *ret = 0;
+               return 0;
+       }
+
+       if (icount->multiple &&
+           !ext2fs_test_inode_bitmap(icount->multiple, ino))
+               return EXT2_ET_INVALID_ARGUMENT;
+       
+       el = get_icount_el(icount, ino, 0);
+       if (!el || el->count == 0)
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       el->count--;
+       if (el->count == 1)
+               ext2fs_mark_inode_bitmap(icount->single, ino);
+       if ((el->count == 0) && icount->multiple)
+               ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+
+       if (ret)
+               *ret = el->count;
+       return 0;
+}
+
+errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
+                             __u16 count)
+{
+       struct ext2_icount_el   *el;
+
+       if (!ino || (ino > icount->num_inodes))
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
+
+       if (count == 1) {
+               ext2fs_mark_inode_bitmap(icount->single, ino);
+               if (icount->multiple)
+                       ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+               return 0;
+       }
+       if (count == 0) {
+               ext2fs_unmark_inode_bitmap(icount->single, ino);
+               if (icount->multiple) {
+                       /*
+                        * If the icount->multiple bitmap is enabled,
+                        * we can just clear both bitmaps and we're done
+                        */
+                       ext2fs_unmark_inode_bitmap(icount->multiple, ino);
+               } else {
+                       el = get_icount_el(icount, ino, 0);
+                       if (el)
+                               el->count = 0;
+               }
+               return 0;
+       }
+
+       /*
+        * Get the icount element
+        */
+       el = get_icount_el(icount, ino, 1);
+       if (!el)
+               return EXT2_ET_NO_MEMORY;
+       el->count = count;
+       ext2fs_unmark_inode_bitmap(icount->single, ino);
+       if (icount->multiple)
+               ext2fs_mark_inode_bitmap(icount->multiple, ino);
+       return 0;
+}
+
+ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount)
+{
+       if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT)
+               return 0;
+
+       return icount->size;
+}
diff --git a/e2fsprogs/ext2fs/imager.c b/e2fsprogs/ext2fs/imager.c
new file mode 100644 (file)
index 0000000..596fbbe
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ * image.c --- writes out the critical parts of the filesystem as a
+ *     flat file.
+ *
+ * Copyright (C) 2000 Theodore Ts'o.
+ *
+ * Note: this uses the POSIX IO interfaces, unlike most of the other
+ * functions in this library.  So sue me.  
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef HAVE_TYPE_SSIZE_T
+typedef int ssize_t;
+#endif
+
+/*
+ * This function returns 1 if the specified block is all zeros
+ */
+static int check_zero_block(char *buf, int blocksize)
+{
+       char    *cp = buf;
+       int     left = blocksize;
+
+       while (left > 0) {
+               if (*cp++)
+                       return 0;
+               left--;
+       }
+       return 1;
+}
+
+/*
+ * Write the inode table out as a single block.
+ */
+#define BUF_BLOCKS     32
+
+errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags)
+{
+       unsigned int    group, left, c, d;
+       char            *buf, *cp;
+       blk_t           blk;
+       ssize_t         actual;
+       errcode_t       retval;
+
+       buf = malloc(fs->blocksize * BUF_BLOCKS);
+       if (!buf)
+               return ENOMEM;
+       
+       for (group = 0; group < fs->group_desc_count; group++) {
+               blk = fs->group_desc[(unsigned)group].bg_inode_table;
+               if (!blk)
+                       return EXT2_ET_MISSING_INODE_TABLE;
+               left = fs->inode_blocks_per_group;
+               while (left) {
+                       c = BUF_BLOCKS;
+                       if (c > left)
+                               c = left;
+                       retval = io_channel_read_blk(fs->io, blk, c, buf);
+                       if (retval)
+                               goto errout;
+                       cp = buf;
+                       while (c) {
+                               if (!(flags & IMAGER_FLAG_SPARSEWRITE)) {
+                                       d = c;
+                                       goto skip_sparse;
+                               }
+                               /* Skip zero blocks */
+                               if (check_zero_block(cp, fs->blocksize)) {
+                                       c--;
+                                       blk++;
+                                       left--;
+                                       cp += fs->blocksize;
+                                       lseek(fd, fs->blocksize, SEEK_CUR);
+                                       continue;
+                               }
+                               /* Find non-zero blocks */
+                               for (d=1; d < c; d++) {
+                                       if (check_zero_block(cp + d*fs->blocksize, fs->blocksize))
+                                               break;
+                               }
+                       skip_sparse:
+                               actual = write(fd, cp, fs->blocksize * d);
+                               if (actual == -1) {
+                                       retval = errno;
+                                       goto errout;
+                               }
+                               if (actual != (ssize_t) (fs->blocksize * d)) {
+                                       retval = EXT2_ET_SHORT_WRITE;
+                                       goto errout;
+                               }
+                               blk += d;
+                               left -= d;
+                               cp += fs->blocksize * d;
+                               c -= d;
+                       }
+               }
+       }
+       retval = 0;
+
+errout:
+       free(buf);
+       return retval;
+}
+
+/*
+ * Read in the inode table and stuff it into place
+ */
+errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, 
+                                 int flags EXT2FS_ATTR((unused)))
+{
+       unsigned int    group, c, left;
+       char            *buf;
+       blk_t           blk;
+       ssize_t         actual;
+       errcode_t       retval;
+
+       buf = malloc(fs->blocksize * BUF_BLOCKS);
+       if (!buf)
+               return ENOMEM;
+       
+       for (group = 0; group < fs->group_desc_count; group++) {
+               blk = fs->group_desc[(unsigned)group].bg_inode_table;
+               if (!blk) {
+                       retval = EXT2_ET_MISSING_INODE_TABLE;
+                       goto errout;
+               }
+               left = fs->inode_blocks_per_group;
+               while (left) {
+                       c = BUF_BLOCKS;
+                       if (c > left)
+                               c = left;
+                       actual = read(fd, buf, fs->blocksize * c);
+                       if (actual == -1) {
+                               retval = errno;
+                               goto errout;
+                       }
+                       if (actual != (ssize_t) (fs->blocksize * c)) {
+                               retval = EXT2_ET_SHORT_READ;
+                               goto errout;
+                       }
+                       retval = io_channel_write_blk(fs->io, blk, c, buf);
+                       if (retval)
+                               goto errout;
+                       
+                       blk += c;
+                       left -= c;
+               }
+       }
+       retval = ext2fs_flush_icache(fs);
+
+errout:
+       free(buf);
+       return retval;
+}
+
+/*
+ * Write out superblock and group descriptors
+ */
+errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, 
+                                  int flags EXT2FS_ATTR((unused)))
+{
+       char            *buf, *cp;
+       ssize_t         actual;
+       errcode_t       retval;
+
+       buf = malloc(fs->blocksize);
+       if (!buf)
+               return ENOMEM;
+
+       /*
+        * Write out the superblock
+        */
+       memset(buf, 0, fs->blocksize);
+       memcpy(buf, fs->super, SUPERBLOCK_SIZE);
+       actual = write(fd, buf, fs->blocksize);
+       if (actual == -1) {
+               retval = errno;
+               goto errout;
+       }
+       if (actual != (ssize_t) fs->blocksize) {
+               retval = EXT2_ET_SHORT_WRITE;
+               goto errout;
+       }
+
+       /*
+        * Now write out the block group descriptors
+        */
+       cp = (char *) fs->group_desc;
+       actual = write(fd, cp, fs->blocksize * fs->desc_blocks);
+       if (actual == -1) {
+               retval = errno;
+               goto errout;
+       }
+       if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) {
+               retval = EXT2_ET_SHORT_WRITE;
+               goto errout;
+       }
+       
+       retval = 0;
+
+errout:
+       free(buf);
+       return retval;
+}
+
+/*
+ * Read the superblock and group descriptors and overwrite them.
+ */
+errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, 
+                                 int flags EXT2FS_ATTR((unused)))
+{
+       char            *buf;
+       ssize_t         actual, size;
+       errcode_t       retval;
+
+       size = fs->blocksize * (fs->group_desc_count + 1);
+       buf = malloc(size);
+       if (!buf)
+               return ENOMEM;
+
+       /*
+        * Read it all in.
+        */
+       actual = read(fd, buf, size);
+       if (actual == -1) {
+               retval = errno;
+               goto errout;
+       }
+       if (actual != size) {
+               retval = EXT2_ET_SHORT_READ;
+               goto errout;
+       }
+
+       /*
+        * Now copy in the superblock and group descriptors
+        */
+       memcpy(fs->super, buf, SUPERBLOCK_SIZE);
+
+       memcpy(fs->group_desc, buf + fs->blocksize,
+              fs->blocksize * fs->group_desc_count);
+
+       retval = 0;
+
+errout:
+       free(buf);
+       return retval;
+}
+
+/*
+ * Write the block/inode bitmaps.
+ */
+errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags)
+{
+       char            *ptr;
+       int             c, size;
+       char            zero_buf[1024];
+       ssize_t         actual;
+       errcode_t       retval;
+
+       if (flags & IMAGER_FLAG_INODEMAP) {
+               if (!fs->inode_map) {
+                       retval = ext2fs_read_inode_bitmap(fs);
+                       if (retval)
+                               return retval;
+               }
+               ptr = fs->inode_map->bitmap;
+               size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
+       } else {
+               if (!fs->block_map) {
+                       retval = ext2fs_read_block_bitmap(fs);
+                       if (retval)
+                               return retval;
+               }
+               ptr = fs->block_map->bitmap;
+               size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+       }
+       size = size * fs->group_desc_count;
+
+       actual = write(fd, ptr, size);
+       if (actual == -1) {
+               retval = errno;
+               goto errout;
+       }
+       if (actual != size) {
+               retval = EXT2_ET_SHORT_WRITE;
+               goto errout;
+       }
+       size = size % fs->blocksize;
+       memset(zero_buf, 0, sizeof(zero_buf));
+       if (size) {
+               size = fs->blocksize - size;
+               while (size) {
+                       c = size;
+                       if (c > (int) sizeof(zero_buf))
+                               c = sizeof(zero_buf);
+                       actual = write(fd, zero_buf, c);
+                       if (actual == -1) {
+                               retval = errno;
+                               goto errout;
+                       }
+                       if (actual != c) {
+                               retval = EXT2_ET_SHORT_WRITE;
+                               goto errout;
+                       }
+                       size -= c;
+               }
+       }
+       retval = 0;
+errout:
+       return (retval);
+}
+
+
+/*
+ * Read the block/inode bitmaps.
+ */
+errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags)
+{
+       char            *ptr, *buf = 0;
+       int             size;
+       ssize_t         actual;
+       errcode_t       retval;
+
+       if (flags & IMAGER_FLAG_INODEMAP) {
+               if (!fs->inode_map) {
+                       retval = ext2fs_read_inode_bitmap(fs);
+                       if (retval)
+                               return retval;
+               }
+               ptr = fs->inode_map->bitmap;
+               size = (EXT2_INODES_PER_GROUP(fs->super) / 8);
+       } else {
+               if (!fs->block_map) {
+                       retval = ext2fs_read_block_bitmap(fs);
+                       if (retval)
+                               return retval;
+               }
+               ptr = fs->block_map->bitmap;
+               size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+       }
+       size = size * fs->group_desc_count;
+
+       buf = malloc(size);
+       if (!buf)
+               return ENOMEM;
+
+       actual = read(fd, buf, size);
+       if (actual == -1) {
+               retval = errno;
+               goto errout;
+       }
+       if (actual != size) {
+               retval = EXT2_ET_SHORT_WRITE;
+               goto errout;
+       }
+       memcpy(ptr, buf, size);
+       
+       retval = 0;
+errout:
+       if (buf)
+               free(buf);
+       return (retval);
+}
diff --git a/e2fsprogs/ext2fs/ind_block.c b/e2fsprogs/ext2fs/ind_block.c
new file mode 100644 (file)
index 0000000..3519048
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * ind_block.c --- indirect block I/O routines
+ * 
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 
+ *     2001, 2002, 2003, 2004, 2005 by  Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf)
+{
+       errcode_t       retval;
+       blk_t           *block_nr;
+       int             i;
+       int             limit = fs->blocksize >> 2;
+
+       if ((fs->flags & EXT2_FLAG_IMAGE_FILE) &&
+           (fs->io != fs->image_io))
+               memset(buf, 0, fs->blocksize);
+       else {
+               retval = io_channel_read_blk(fs->io, blk, 1, buf);
+               if (retval)
+                       return retval;
+       }
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_READ)) {
+               block_nr = (blk_t *) buf;
+               for (i = 0; i < limit; i++, block_nr++)
+                       *block_nr = ext2fs_swab32(*block_nr);
+       }
+#endif
+       return 0;
+}
+
+errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf)
+{
+       blk_t           *block_nr;
+       int             i;
+       int             limit = fs->blocksize >> 2;
+
+       if (fs->flags & EXT2_FLAG_IMAGE_FILE)
+               return 0;
+
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if (fs->flags & (EXT2_FLAG_SWAP_BYTES | EXT2_FLAG_SWAP_BYTES_WRITE)) {
+               block_nr = (blk_t *) buf;
+               for (i = 0; i < limit; i++, block_nr++)
+                       *block_nr = ext2fs_swab32(*block_nr);
+       }
+#endif
+       return io_channel_write_blk(fs->io, blk, 1, buf);
+}
+
+
diff --git a/e2fsprogs/ext2fs/initialize.c b/e2fsprogs/ext2fs/initialize.c
new file mode 100644 (file)
index 0000000..82cd9f1
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ * initialize.c --- initialize a filesystem handle given superblock
+ *     parameters.  Used by mke2fs when initializing a filesystem.
+ * 
+ * Copyright (C) 1994, 1995, 1996 Theodore Ts'o.
+ * 
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#if defined(__linux__)    &&   defined(EXT2_OS_LINUX)
+#define CREATOR_OS EXT2_OS_LINUX
+#else
+#if defined(__GNU__)     &&    defined(EXT2_OS_HURD)
+#define CREATOR_OS EXT2_OS_HURD
+#else
+#if defined(__FreeBSD__) &&    defined(EXT2_OS_FREEBSD)
+#define CREATOR_OS EXT2_OS_FREEBSD
+#else
+#if defined(LITES)        &&   defined(EXT2_OS_LITES)
+#define CREATOR_OS EXT2_OS_LITES
+#else
+#define CREATOR_OS EXT2_OS_LINUX /* by default */
+#endif /* defined(LITES) && defined(EXT2_OS_LITES) */
+#endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */
+#endif /* defined(__GNU__)     && defined(EXT2_OS_HURD) */
+#endif /* defined(__linux__)   && defined(EXT2_OS_LINUX) */
+       
+/*
+ * Note we override the kernel include file's idea of what the default
+ * check interval (never) should be.  It's a good idea to check at
+ * least *occasionally*, specially since servers will never rarely get
+ * to reboot, since Linux is so robust these days.  :-)
+ * 
+ * 180 days (six months) seems like a good value.
+ */
+#ifdef EXT2_DFL_CHECKINTERVAL
+#undef EXT2_DFL_CHECKINTERVAL
+#endif
+#define EXT2_DFL_CHECKINTERVAL (86400L * 180L)
+
+/*
+ * Calculate the number of GDT blocks to reserve for online filesystem growth.
+ * The absolute maximum number of GDT blocks we can reserve is determined by
+ * the number of block pointers that can fit into a single block.
+ */
+static int calc_reserved_gdt_blocks(ext2_filsys fs)
+{
+       struct ext2_super_block *sb = fs->super;
+       unsigned long bpg = sb->s_blocks_per_group;
+       unsigned int gdpb = fs->blocksize / sizeof(struct ext2_group_desc);
+       unsigned long max_blocks = 0xffffffff;
+       unsigned long rsv_groups;
+       int rsv_gdb;
+
+       /* We set it at 1024x the current filesystem size, or
+        * the upper block count limit (2^32), whichever is lower.
+        */
+       if (sb->s_blocks_count < max_blocks / 1024)
+               max_blocks = sb->s_blocks_count * 1024;
+       rsv_groups = (max_blocks - sb->s_first_data_block + bpg - 1) / bpg;
+       rsv_gdb = (rsv_groups + gdpb - 1) / gdpb - fs->desc_blocks;
+       if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb))
+               rsv_gdb = EXT2_ADDR_PER_BLOCK(sb);
+#ifdef RES_GDT_DEBUG
+       printf("max_blocks %lu, rsv_groups = %lu, rsv_gdb = %lu\n",
+              max_blocks, rsv_groups, rsv_gdb);
+#endif
+
+       return rsv_gdb;
+}
+
+errcode_t ext2fs_initialize(const char *name, int flags,
+                           struct ext2_super_block *param,
+                           io_manager manager, ext2_filsys *ret_fs)
+{
+       ext2_filsys     fs;
+       errcode_t       retval;
+       struct ext2_super_block *super;
+       int             frags_per_block;
+       unsigned int    rem;
+       unsigned int    overhead = 0;
+       blk_t           group_block;
+       unsigned int    ipg;
+       dgrp_t          i;
+       blk_t           numblocks;
+       int             rsv_gdt;
+       char            *buf;
+
+       if (!param || !param->s_blocks_count)
+               return EXT2_ET_INVALID_ARGUMENT;
+       
+       retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs);
+       if (retval)
+               return retval;
+       
+       memset(fs, 0, sizeof(struct struct_ext2_filsys));
+       fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS;
+       fs->flags = flags | EXT2_FLAG_RW;
+       fs->umask = 022;
+#ifdef WORDS_BIGENDIAN
+       fs->flags |= EXT2_FLAG_SWAP_BYTES;
+#endif
+       retval = manager->open(name, IO_FLAG_RW, &fs->io);
+       if (retval)
+               goto cleanup;
+       fs->image_io = fs->io;
+       fs->io->app_data = fs;
+       retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name);
+       if (retval)
+               goto cleanup;
+
+       strcpy(fs->device_name, name);
+       retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super);
+       if (retval)
+               goto cleanup;
+       fs->super = super;
+
+       memset(super, 0, SUPERBLOCK_SIZE);
+
+#define set_field(field, default) (super->field = param->field ? \
+                                  param->field : (default))
+
+       super->s_magic = EXT2_SUPER_MAGIC;
+       super->s_state = EXT2_VALID_FS;
+
+       set_field(s_log_block_size, 0); /* default blocksize: 1024 bytes */
+       set_field(s_log_frag_size, 0); /* default fragsize: 1024 bytes */
+       set_field(s_first_data_block, super->s_log_block_size ? 0 : 1);
+       set_field(s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT);
+       set_field(s_errors, EXT2_ERRORS_DEFAULT);
+       set_field(s_feature_compat, 0);
+       set_field(s_feature_incompat, 0);
+       set_field(s_feature_ro_compat, 0);
+       set_field(s_first_meta_bg, 0);
+       if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) {
+               retval = EXT2_ET_UNSUPP_FEATURE;
+               goto cleanup;
+       }
+       if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) {
+               retval = EXT2_ET_RO_UNSUPP_FEATURE;
+               goto cleanup;
+       }
+
+       set_field(s_rev_level, EXT2_GOOD_OLD_REV);
+       if (super->s_rev_level >= EXT2_DYNAMIC_REV) {
+               set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO);
+               set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE);
+       }
+
+       set_field(s_checkinterval, EXT2_DFL_CHECKINTERVAL);
+       super->s_mkfs_time = super->s_lastcheck = time(NULL);
+
+       super->s_creator_os = CREATOR_OS;
+
+       fs->blocksize = EXT2_BLOCK_SIZE(super);
+       fs->fragsize = EXT2_FRAG_SIZE(super);
+       frags_per_block = fs->blocksize / fs->fragsize;
+
+       /* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */
+       set_field(s_blocks_per_group, fs->blocksize * 8);
+       if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super))
+               super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super);
+       super->s_frags_per_group = super->s_blocks_per_group * frags_per_block;
+       
+       super->s_blocks_count = param->s_blocks_count;
+       super->s_r_blocks_count = param->s_r_blocks_count;
+       if (super->s_r_blocks_count >= param->s_blocks_count) {
+               retval = EXT2_ET_INVALID_ARGUMENT;
+               goto cleanup;
+       }
+
+       /*
+        * If we're creating an external journal device, we don't need
+        * to bother with the rest.
+        */
+       if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
+               fs->group_desc_count = 0;
+               ext2fs_mark_super_dirty(fs);
+               *ret_fs = fs;
+               return 0;
+       }
+
+retry:
+       fs->group_desc_count = (super->s_blocks_count -
+                               super->s_first_data_block +
+                               EXT2_BLOCKS_PER_GROUP(super) - 1)
+               / EXT2_BLOCKS_PER_GROUP(super);
+       if (fs->group_desc_count == 0) {
+               retval = EXT2_ET_TOOSMALL;
+               goto cleanup;
+       }
+       fs->desc_blocks = (fs->group_desc_count +
+                          EXT2_DESC_PER_BLOCK(super) - 1)
+               / EXT2_DESC_PER_BLOCK(super);
+
+       i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize;
+       set_field(s_inodes_count, super->s_blocks_count / i);
+
+       /*
+        * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so
+        * that we have enough inodes for the filesystem(!)
+        */
+       if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1)
+               super->s_inodes_count = EXT2_FIRST_INODE(super)+1;
+       
+       /*
+        * There should be at least as many inodes as the user
+        * requested.  Figure out how many inodes per group that
+        * should be.  But make sure that we don't allocate more than
+        * one bitmap's worth of inodes each group.
+        */
+       ipg = (super->s_inodes_count + fs->group_desc_count - 1) /
+               fs->group_desc_count;
+       if (ipg > fs->blocksize * 8) {
+               if (super->s_blocks_per_group >= 256) {
+                       /* Try again with slightly different parameters */
+                       super->s_blocks_per_group -= 8;
+                       super->s_blocks_count = param->s_blocks_count;
+                       super->s_frags_per_group = super->s_blocks_per_group *
+                               frags_per_block;
+                       goto retry;
+               } else
+                       return EXT2_ET_TOO_MANY_INODES;
+       }
+
+       if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super))
+               ipg = EXT2_MAX_INODES_PER_GROUP(super);
+
+       super->s_inodes_per_group = ipg;
+       if (super->s_inodes_count > ipg * fs->group_desc_count)
+               super->s_inodes_count = ipg * fs->group_desc_count;
+
+       /*
+        * Make sure the number of inodes per group completely fills
+        * the inode table blocks in the descriptor.  If not, add some
+        * additional inodes/group.  Waste not, want not...
+        */
+       fs->inode_blocks_per_group = (((super->s_inodes_per_group *
+                                       EXT2_INODE_SIZE(super)) +
+                                      EXT2_BLOCK_SIZE(super) - 1) /
+                                     EXT2_BLOCK_SIZE(super));
+       super->s_inodes_per_group = ((fs->inode_blocks_per_group *
+                                     EXT2_BLOCK_SIZE(super)) /
+                                    EXT2_INODE_SIZE(super));
+       /*
+        * Finally, make sure the number of inodes per group is a
+        * multiple of 8.  This is needed to simplify the bitmap
+        * splicing code.
+        */
+       super->s_inodes_per_group &= ~7;
+       fs->inode_blocks_per_group = (((super->s_inodes_per_group *
+                                       EXT2_INODE_SIZE(super)) +
+                                      EXT2_BLOCK_SIZE(super) - 1) /
+                                     EXT2_BLOCK_SIZE(super));
+
+       /*
+        * adjust inode count to reflect the adjusted inodes_per_group
+        */
+       super->s_inodes_count = super->s_inodes_per_group *
+               fs->group_desc_count;
+       super->s_free_inodes_count = super->s_inodes_count;
+
+       /*
+        * check the number of reserved group descriptor table blocks
+        */
+       if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE)
+               rsv_gdt = calc_reserved_gdt_blocks(fs);
+       else
+               rsv_gdt = 0;
+       set_field(s_reserved_gdt_blocks, rsv_gdt);
+       if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) {
+               retval = EXT2_ET_RES_GDT_BLOCKS;
+               goto cleanup;
+       }
+
+       /*
+        * Overhead is the number of bookkeeping blocks per group.  It
+        * includes the superblock backup, the group descriptor
+        * backups, the inode bitmap, the block bitmap, and the inode
+        * table.
+        */
+
+       overhead = (int) (2 + fs->inode_blocks_per_group);
+
+       if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1))
+               overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks;
+
+       /* This can only happen if the user requested too many inodes */
+       if (overhead > super->s_blocks_per_group)
+               return EXT2_ET_TOO_MANY_INODES;
+
+       /*
+        * See if the last group is big enough to support the
+        * necessary data structures.  If not, we need to get rid of
+        * it.
+        */
+       rem = ((super->s_blocks_count - super->s_first_data_block) %
+              super->s_blocks_per_group);
+       if ((fs->group_desc_count == 1) && rem && (rem < overhead))
+               return EXT2_ET_TOOSMALL;
+       if (rem && (rem < overhead+50)) {
+               super->s_blocks_count -= rem;
+               goto retry;
+       }
+
+       /*
+        * At this point we know how big the filesystem will be.  So
+        * we can do any and all allocations that depend on the block
+        * count.
+        */
+
+       retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf);
+       if (retval)
+               goto cleanup;
+       
+       sprintf(buf, "block bitmap for %s", fs->device_name);
+       retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map);
+       if (retval)
+               goto cleanup;
+       
+       sprintf(buf, "inode bitmap for %s", fs->device_name);
+       retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map);
+       if (retval)
+               goto cleanup;
+
+       ext2fs_free_mem(&buf);
+
+       retval = ext2fs_get_mem((size_t) fs->desc_blocks * fs->blocksize,
+                               &fs->group_desc);
+       if (retval)
+               goto cleanup;
+
+       memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize);
+
+       /*
+        * Reserve the superblock and group descriptors for each
+        * group, and fill in the correct group statistics for group.
+        * Note that although the block bitmap, inode bitmap, and
+        * inode table have not been allocated (and in fact won't be
+        * by this routine), they are accounted for nevertheless.
+        */
+       group_block = super->s_first_data_block;
+       super->s_free_blocks_count = 0;
+       for (i = 0; i < fs->group_desc_count; i++) {
+               numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map);
+
+               super->s_free_blocks_count += numblocks;
+               fs->group_desc[i].bg_free_blocks_count = numblocks;
+               fs->group_desc[i].bg_free_inodes_count =
+                       fs->super->s_inodes_per_group;
+               fs->group_desc[i].bg_used_dirs_count = 0;
+               
+               group_block += super->s_blocks_per_group;
+       }
+       
+       ext2fs_mark_super_dirty(fs);
+       ext2fs_mark_bb_dirty(fs);
+       ext2fs_mark_ib_dirty(fs);
+       
+       io_channel_set_blksize(fs->io, fs->blocksize);
+
+       *ret_fs = fs;
+       return 0;
+cleanup:
+       ext2fs_free(fs);
+       return retval;
+}
diff --git a/e2fsprogs/ext2fs/inline.c b/e2fsprogs/ext2fs/inline.c
new file mode 100644 (file)
index 0000000..5833b1d
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * inline.c --- Includes the inlined functions defined in the header
+ *     files as standalone functions, in case the application program
+ *     is compiled with inlining turned off.
+ * 
+ * Copyright (C) 1993, 1994 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#define INCLUDE_INLINE_FUNCS
+#include "ext2fs.h"
+
diff --git a/e2fsprogs/ext2fs/inode.c b/e2fsprogs/ext2fs/inode.c
new file mode 100644 (file)
index 0000000..222568e
--- /dev/null
@@ -0,0 +1,794 @@
+/*
+ * inode.c --- utility routines to read and write inodes
+ * 
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+#include "e2image.h"
+
+struct ext2_struct_inode_scan {
+       errcode_t               magic;
+       ext2_filsys             fs;
+       ext2_ino_t              current_inode;
+       blk_t                   current_block;
+       dgrp_t                  current_group;
+       ext2_ino_t              inodes_left;
+       blk_t                   blocks_left;
+       dgrp_t                  groups_left;
+       blk_t                   inode_buffer_blocks;
+       char *                  inode_buffer;
+       int                     inode_size;
+       char *                  ptr;
+       int                     bytes_left;
+       char                    *temp_buffer;
+       errcode_t               (*done_group)(ext2_filsys fs,
+                                             ext2_inode_scan scan,
+                                             dgrp_t group,
+                                             void * priv_data);
+       void *                  done_group_data;
+       int                     bad_block_ptr;
+       int                     scan_flags;
+       int                     reserved[6];
+};
+
+/*
+ * This routine flushes the icache, if it exists.
+ */
+errcode_t ext2fs_flush_icache(ext2_filsys fs)
+{
+       int     i;
+       
+       if (!fs->icache)
+               return 0;
+
+       for (i=0; i < fs->icache->cache_size; i++)
+               fs->icache->cache[i].ino = 0;
+
+       fs->icache->buffer_blk = 0;
+       return 0;
+}
+
+static errcode_t create_icache(ext2_filsys fs)
+{
+       errcode_t       retval;
+       
+       if (fs->icache)
+               return 0;
+       retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache);
+       if (retval)
+               return retval;
+
+       memset(fs->icache, 0, sizeof(struct ext2_inode_cache));
+       retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer);
+       if (retval) {
+               ext2fs_free_mem(&fs->icache);
+               return retval;
+       }
+       fs->icache->buffer_blk = 0;
+       fs->icache->cache_last = -1;
+       fs->icache->cache_size = 4;
+       fs->icache->refcount = 1;
+       retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache_ent)
+                               * fs->icache->cache_size,
+                               &fs->icache->cache);
+       if (retval) {
+               ext2fs_free_mem(&fs->icache->buffer);
+               ext2fs_free_mem(&fs->icache);
+               return retval;
+       }
+       ext2fs_flush_icache(fs);
+       return 0;
+}
+
+errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks,
+                                ext2_inode_scan *ret_scan)
+{
+       ext2_inode_scan scan;
+       errcode_t       retval;
+       errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks);
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       /*
+        * If fs->badblocks isn't set, then set it --- since the inode
+        * scanning functions require it.
+        */
+       if (fs->badblocks == 0) {
+               /*
+                * Temporarly save fs->get_blocks and set it to zero,
+                * for compatibility with old e2fsck's.
+                */
+               save_get_blocks = fs->get_blocks;
+               fs->get_blocks = 0;
+               retval = ext2fs_read_bb_inode(fs, &fs->badblocks);
+               if (retval && fs->badblocks) {
+                       ext2fs_badblocks_list_free(fs->badblocks);
+                       fs->badblocks = 0;
+               }
+               fs->get_blocks = save_get_blocks;
+       }
+
+       retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan);
+       if (retval)
+               return retval;
+       memset(scan, 0, sizeof(struct ext2_struct_inode_scan));
+
+       scan->magic = EXT2_ET_MAGIC_INODE_SCAN;
+       scan->fs = fs;
+       scan->inode_size = EXT2_INODE_SIZE(fs->super);
+       scan->bytes_left = 0;
+       scan->current_group = 0;
+       scan->groups_left = fs->group_desc_count - 1;
+       scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8;
+       scan->current_block = scan->fs->
+               group_desc[scan->current_group].bg_inode_table;
+       scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
+       scan->blocks_left = scan->fs->inode_blocks_per_group;
+       retval = ext2fs_get_mem((size_t) (scan->inode_buffer_blocks * 
+                                         fs->blocksize),
+                               &scan->inode_buffer);
+       scan->done_group = 0;
+       scan->done_group_data = 0;
+       scan->bad_block_ptr = 0;
+       if (retval) {
+               ext2fs_free_mem(&scan);
+               return retval;
+       }
+       retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer);
+       if (retval) {
+               ext2fs_free_mem(&scan->inode_buffer);
+               ext2fs_free_mem(&scan);
+               return retval;
+       }
+       if (scan->fs->badblocks && scan->fs->badblocks->num)
+               scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS;
+       *ret_scan = scan;
+       return 0;
+}
+
+void ext2fs_close_inode_scan(ext2_inode_scan scan)
+{
+       if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
+               return;
+       
+       ext2fs_free_mem(&scan->inode_buffer);
+       scan->inode_buffer = NULL;
+       ext2fs_free_mem(&scan->temp_buffer);
+       scan->temp_buffer = NULL;
+       ext2fs_free_mem(&scan);
+       return;
+}
+
+void ext2fs_set_inode_callback(ext2_inode_scan scan,
+                              errcode_t (*done_group)(ext2_filsys fs,
+                                                      ext2_inode_scan scan,
+                                                      dgrp_t group,
+                                                      void * priv_data),
+                              void *done_group_data)
+{
+       if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
+               return;
+       
+       scan->done_group = done_group;
+       scan->done_group_data = done_group_data;
+}
+
+int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags,
+                           int clear_flags)
+{
+       int     old_flags;
+
+       if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN))
+               return 0;
+
+       old_flags = scan->scan_flags;
+       scan->scan_flags &= ~clear_flags;
+       scan->scan_flags |= set_flags;
+       return old_flags;
+}
+
+/*
+ * This function is called by ext2fs_get_next_inode when it needs to
+ * get ready to read in a new blockgroup.
+ */
+static errcode_t get_next_blockgroup(ext2_inode_scan scan)
+{
+       scan->current_group++;
+       scan->groups_left--;
+                       
+       scan->current_block = scan->fs->
+               group_desc[scan->current_group].bg_inode_table;
+
+       scan->current_inode = scan->current_group *
+               EXT2_INODES_PER_GROUP(scan->fs->super);
+
+       scan->bytes_left = 0;
+       scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super);
+       scan->blocks_left = scan->fs->inode_blocks_per_group;
+       return 0;
+}
+
+errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan,
+                                           int group)
+{
+       scan->current_group = group - 1;
+       scan->groups_left = scan->fs->group_desc_count - group;
+       return get_next_blockgroup(scan);
+}
+
+/*
+ * This function is called by get_next_blocks() to check for bad
+ * blocks in the inode table.
+ *
+ * This function assumes that badblocks_list->list is sorted in
+ * increasing order.
+ */
+static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan,
+                                           blk_t *num_blocks)
+{
+       blk_t   blk = scan->current_block;
+       badblocks_list  bb = scan->fs->badblocks;
+
+       /*
+        * If the inode table is missing, then obviously there are no
+        * bad blocks.  :-)
+        */
+       if (blk == 0)
+               return 0;
+
+       /*
+        * If the current block is greater than the bad block listed
+        * in the bad block list, then advance the pointer until this
+        * is no longer the case.  If we run out of bad blocks, then
+        * we don't need to do any more checking!
+        */
+       while (blk > bb->list[scan->bad_block_ptr]) {
+               if (++scan->bad_block_ptr >= bb->num) {
+                       scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
+                       return 0;
+               }
+       }
+
+       /*
+        * If the current block is equal to the bad block listed in
+        * the bad block list, then handle that one block specially.
+        * (We could try to handle runs of bad blocks, but that
+        * only increases CPU efficiency by a small amount, at the
+        * expense of a huge expense of code complexity, and for an
+        * uncommon case at that.)
+        */
+       if (blk == bb->list[scan->bad_block_ptr]) {
+               scan->scan_flags |= EXT2_SF_BAD_INODE_BLK;
+               *num_blocks = 1;
+               if (++scan->bad_block_ptr >= bb->num)
+                       scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS;
+               return 0;
+       }
+
+       /*
+        * If there is a bad block in the range that we're about to
+        * read in, adjust the number of blocks to read so that we we
+        * don't read in the bad block.  (Then the next block to read
+        * will be the bad block, which is handled in the above case.)
+        */
+       if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr])
+               *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk);
+
+       return 0;
+}
+
+/*
+ * This function is called by ext2fs_get_next_inode when it needs to
+ * read in more blocks from the current blockgroup's inode table.
+ */
+static errcode_t get_next_blocks(ext2_inode_scan scan)
+{
+       blk_t           num_blocks;
+       errcode_t       retval;
+
+       /*
+        * Figure out how many blocks to read; we read at most
+        * inode_buffer_blocks, and perhaps less if there aren't that
+        * many blocks left to read.
+        */
+       num_blocks = scan->inode_buffer_blocks;
+       if (num_blocks > scan->blocks_left)
+               num_blocks = scan->blocks_left;
+
+       /*
+        * If the past block "read" was a bad block, then mark the
+        * left-over extra bytes as also being bad.
+        */
+       if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) {
+               if (scan->bytes_left)
+                       scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES;
+               scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK;
+       }
+
+       /*
+        * Do inode bad block processing, if necessary.
+        */
+       if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) {
+               retval = check_for_inode_bad_blocks(scan, &num_blocks);
+               if (retval)
+                       return retval;
+       }
+               
+       if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) ||
+           (scan->current_block == 0)) {
+               memset(scan->inode_buffer, 0,
+                      (size_t) num_blocks * scan->fs->blocksize);
+       } else {
+               retval = io_channel_read_blk(scan->fs->io,
+                                            scan->current_block,
+                                            (int) num_blocks,
+                                            scan->inode_buffer);
+               if (retval)
+                       return EXT2_ET_NEXT_INODE_READ;
+       }
+       scan->ptr = scan->inode_buffer;
+       scan->bytes_left = num_blocks * scan->fs->blocksize;
+
+       scan->blocks_left -= num_blocks;
+       if (scan->current_block)
+               scan->current_block += num_blocks;
+       return 0;
+}
+
+#if 0
+/*
+ * Returns 1 if the entire inode_buffer has a non-zero size and
+ * contains all zeros.  (Not just deleted inodes, since that means
+ * that part of the inode table was used at one point; we want all
+ * zeros, which means that the inode table is pristine.)
+ */
+static inline int is_empty_scan(ext2_inode_scan scan)
+{
+       int     i;
+       
+       if (scan->bytes_left == 0)
+               return 0;
+
+       for (i=0; i < scan->bytes_left; i++)
+               if (scan->ptr[i])
+                       return 0;
+       return 1;
+}
+#endif
+
+errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino,
+                                    struct ext2_inode *inode, int bufsize)
+{
+       errcode_t       retval;
+       int             extra_bytes = 0;
+       
+       EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN);
+
+       /*
+        * Do we need to start reading a new block group?
+        */
+       if (scan->inodes_left <= 0) {
+       force_new_group:
+               if (scan->done_group) {
+                       retval = (scan->done_group)
+                               (scan->fs, scan, scan->current_group,
+                                scan->done_group_data);
+                       if (retval)
+                               return retval;
+               }
+               if (scan->groups_left <= 0) {
+                       *ino = 0;
+                       return 0;
+               }
+               retval = get_next_blockgroup(scan);
+               if (retval)
+                       return retval;
+       }
+       /*
+        * This is done outside the above if statement so that the
+        * check can be done for block group #0.
+        */
+       if (scan->current_block == 0) {
+               if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) {
+                       goto force_new_group;
+               } else
+                       return EXT2_ET_MISSING_INODE_TABLE;
+       }
+       
+
+       /*
+        * Have we run out of space in the inode buffer?  If so, we
+        * need to read in more blocks.
+        */
+       if (scan->bytes_left < scan->inode_size) {
+               memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left);
+               extra_bytes = scan->bytes_left;
+
+               retval = get_next_blocks(scan);
+               if (retval)
+                       return retval;
+#if 0
+               /*
+                * XXX test  Need check for used inode somehow.
+                * (Note: this is hard.)
+                */
+               if (is_empty_scan(scan))
+                       goto force_new_group;
+#endif
+       }
+
+       retval = 0;
+       if (extra_bytes) {
+               memcpy(scan->temp_buffer+extra_bytes, scan->ptr,
+                      scan->inode_size - extra_bytes);
+               scan->ptr += scan->inode_size - extra_bytes;
+               scan->bytes_left -= scan->inode_size - extra_bytes;
+
+#ifdef EXT2FS_ENABLE_SWAPFS
+               if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+                   (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+                       ext2fs_swap_inode_full(scan->fs, 
+                               (struct ext2_inode_large *) inode,
+                               (struct ext2_inode_large *) scan->temp_buffer, 
+                               0, bufsize);
+               else
+#endif
+                       *inode = *((struct ext2_inode *) scan->temp_buffer);
+               if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES)
+                       retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
+               scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES;
+       } else {
+#ifdef EXT2FS_ENABLE_SWAPFS
+               if ((scan->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+                   (scan->fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+                       ext2fs_swap_inode_full(scan->fs, 
+                               (struct ext2_inode_large *) inode,
+                               (struct ext2_inode_large *) scan->ptr,
+                               0, bufsize);
+               else
+#endif
+                       memcpy(inode, scan->ptr, bufsize);
+               scan->ptr += scan->inode_size;
+               scan->bytes_left -= scan->inode_size;
+               if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK)
+                       retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE;
+       }
+
+       scan->inodes_left--;
+       scan->current_inode++;
+       *ino = scan->current_inode;
+       return retval;
+}
+
+errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino,
+                               struct ext2_inode *inode)
+{
+       return ext2fs_get_next_inode_full(scan, ino, inode,
+                                               sizeof(struct ext2_inode));
+}
+
+/*
+ * Functions to read and write a single inode.
+ */
+errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino,
+                                struct ext2_inode * inode, int bufsize)
+{
+       unsigned long   group, block, block_nr, offset;
+       char            *ptr;
+       errcode_t       retval;
+       int             clen, i, inodes_per_block, length;
+       io_channel      io;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       /* Check to see if user has an override function */
+       if (fs->read_inode) {
+               retval = (fs->read_inode)(fs, ino, inode);
+               if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
+                       return retval;
+       }
+       /* Create inode cache if not present */
+       if (!fs->icache) {
+               retval = create_icache(fs);
+               if (retval)
+                       return retval;
+       }
+       /* Check to see if it's in the inode cache */
+       if (bufsize == sizeof(struct ext2_inode)) {
+               /* only old good inode can be retrieve from the cache */
+               for (i=0; i < fs->icache->cache_size; i++) {
+                       if (fs->icache->cache[i].ino == ino) {
+                               *inode = fs->icache->cache[i].inode;
+                               return 0;
+                       }
+               }
+       }
+       if ((ino == 0) || (ino > fs->super->s_inodes_count))
+               return EXT2_ET_BAD_INODE_NUM;
+       if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
+               inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super);
+               block_nr = fs->image_header->offset_inode / fs->blocksize;
+               block_nr += (ino - 1) / inodes_per_block;
+               offset = ((ino - 1) % inodes_per_block) *
+                       EXT2_INODE_SIZE(fs->super);
+               io = fs->image_io;
+       } else {
+               group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
+               offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
+                       EXT2_INODE_SIZE(fs->super);
+               block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
+               if (!fs->group_desc[(unsigned)group].bg_inode_table)
+                       return EXT2_ET_MISSING_INODE_TABLE;
+               block_nr = fs->group_desc[(unsigned)group].bg_inode_table + 
+                       block;
+               io = fs->io;
+       }
+       offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
+
+       length = EXT2_INODE_SIZE(fs->super);
+       if (bufsize < length)
+               length = bufsize;
+
+       ptr = (char *) inode;
+       while (length) {
+               clen = length;
+               if ((offset + length) > fs->blocksize)
+                       clen = fs->blocksize - offset;
+
+               if (block_nr != fs->icache->buffer_blk) {
+                       retval = io_channel_read_blk(io, block_nr, 1,
+                                                    fs->icache->buffer);
+                       if (retval)
+                               return retval;
+                       fs->icache->buffer_blk = block_nr;
+               }
+
+               memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset,
+                      clen);
+
+               offset = 0;
+               length -= clen;
+               ptr += clen;
+               block_nr++;
+       }
+
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+           (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
+               ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode, 
+                                      (struct ext2_inode_large *) inode, 
+                                      0, length);
+#endif
+
+       /* Update the inode cache */
+       fs->icache->cache_last = (fs->icache->cache_last + 1) %
+               fs->icache->cache_size;
+       fs->icache->cache[fs->icache->cache_last].ino = ino;
+       fs->icache->cache[fs->icache->cache_last].inode = *inode;
+       
+       return 0;
+}
+
+errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino,
+                           struct ext2_inode * inode)
+{
+       return ext2fs_read_inode_full(fs, ino, inode,
+                                       sizeof(struct ext2_inode));
+}
+
+errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino,
+                                 struct ext2_inode * inode, int bufsize)
+{
+       unsigned long group, block, block_nr, offset;
+       errcode_t retval = 0;
+       struct ext2_inode_large temp_inode, *w_inode;
+       char *ptr;
+       int clen, i, length;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       /* Check to see if user provided an override function */
+       if (fs->write_inode) {
+               retval = (fs->write_inode)(fs, ino, inode);
+               if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
+                       return retval;
+       }
+
+       /* Check to see if the inode cache needs to be updated */
+       if (fs->icache) {
+               for (i=0; i < fs->icache->cache_size; i++) {
+                       if (fs->icache->cache[i].ino == ino) {
+                               fs->icache->cache[i].inode = *inode;
+                               break;
+                       }
+               }
+       } else {
+               retval = create_icache(fs);
+               if (retval)
+                       return retval;
+       }
+               
+       if (!(fs->flags & EXT2_FLAG_RW))
+               return EXT2_ET_RO_FILSYS;
+
+       if ((ino == 0) || (ino > fs->super->s_inodes_count))
+               return EXT2_ET_BAD_INODE_NUM;
+
+       length = bufsize;
+       if (length < EXT2_INODE_SIZE(fs->super))
+               length = EXT2_INODE_SIZE(fs->super);
+
+       if (length > (int) sizeof(struct ext2_inode_large)) {
+               w_inode = malloc(length);
+               if (!w_inode)
+                       return ENOMEM;
+       } else
+               w_inode = &temp_inode;
+       memset(w_inode, 0, length);
+
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+           (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
+               ext2fs_swap_inode_full(fs, w_inode, 
+                                      (struct ext2_inode_large *) inode, 
+                                      1, bufsize);
+       else
+#endif
+               memcpy(w_inode, inode, bufsize);
+       
+       group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super);
+       offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) *
+               EXT2_INODE_SIZE(fs->super);
+       block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super);
+       if (!fs->group_desc[(unsigned) group].bg_inode_table)
+               return EXT2_ET_MISSING_INODE_TABLE;
+       block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block;
+
+       offset &= (EXT2_BLOCK_SIZE(fs->super) - 1);
+
+       length = EXT2_INODE_SIZE(fs->super);
+       if (length > bufsize)
+               length = bufsize;
+
+       ptr = (char *) w_inode;
+
+       while (length) {
+               clen = length;
+               if ((offset + length) > fs->blocksize)
+                       clen = fs->blocksize - offset;
+
+               if (fs->icache->buffer_blk != block_nr) {
+                       retval = io_channel_read_blk(fs->io, block_nr, 1,
+                                                    fs->icache->buffer);
+                       if (retval)
+                               goto errout;
+                       fs->icache->buffer_blk = block_nr;
+               }
+
+       
+               memcpy((char *) fs->icache->buffer + (unsigned) offset, 
+                      ptr, clen);
+
+               retval = io_channel_write_blk(fs->io, block_nr, 1, 
+                                             fs->icache->buffer);
+               if (retval)
+                       goto errout;
+
+               offset = 0;
+               ptr += clen;
+               length -= clen;
+               block_nr++;
+       }
+               
+       fs->flags |= EXT2_FLAG_CHANGED;
+errout:
+       if (w_inode && w_inode != &temp_inode)
+               free(w_inode);
+       return retval;
+}
+
+errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino,
+                            struct ext2_inode *inode)
+{
+       return ext2fs_write_inode_full(fs, ino, inode,
+                                      sizeof(struct ext2_inode));
+}
+
+/* 
+ * This function should be called when writing a new inode.  It makes
+ * sure that extra part of large inodes is initialized properly.
+ */
+errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino,
+                                struct ext2_inode *inode)
+{
+       struct ext2_inode       *buf;
+       int                     size = EXT2_INODE_SIZE(fs->super);
+       struct ext2_inode_large *large_inode;
+
+       if (size == sizeof(struct ext2_inode))
+               return ext2fs_write_inode_full(fs, ino, inode,
+                                              sizeof(struct ext2_inode));
+
+       buf = malloc(size);
+       if (!buf)
+               return ENOMEM;
+
+       memset(buf, 0, size);
+       *buf = *inode;
+
+       large_inode = (struct ext2_inode_large *) buf;
+       large_inode->i_extra_isize = sizeof(struct ext2_inode_large) - 
+               EXT2_GOOD_OLD_INODE_SIZE;
+
+       return ext2fs_write_inode_full(fs, ino, buf, size);
+}
+
+errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks)
+{
+       struct ext2_inode       inode;
+       int                     i;
+       errcode_t               retval;
+       
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (ino > fs->super->s_inodes_count)
+               return EXT2_ET_BAD_INODE_NUM;
+
+       if (fs->get_blocks) {
+               if (!(*fs->get_blocks)(fs, ino, blocks))
+                       return 0;
+       }
+       retval = ext2fs_read_inode(fs, ino, &inode);
+       if (retval)
+               return retval;
+       for (i=0; i < EXT2_N_BLOCKS; i++)
+               blocks[i] = inode.i_block[i];
+       return 0;
+}
+
+errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino)
+{
+       struct  ext2_inode      inode;
+       errcode_t               retval;
+       
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (ino > fs->super->s_inodes_count)
+               return EXT2_ET_BAD_INODE_NUM;
+
+       if (fs->check_directory) {
+               retval = (fs->check_directory)(fs, ino);
+               if (retval != EXT2_ET_CALLBACK_NOTHANDLED)
+                       return retval;
+       }
+       retval = ext2fs_read_inode(fs, ino, &inode);
+       if (retval)
+               return retval;
+       if (!LINUX_S_ISDIR(inode.i_mode))
+               return EXT2_ET_NO_DIRECTORY;
+       return 0;
+}
+
diff --git a/e2fsprogs/ext2fs/inode_io.c b/e2fsprogs/ext2fs/inode_io.c
new file mode 100644 (file)
index 0000000..b5c08b9
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * inode_io.c --- This is allows an inode in an ext2 filesystem image
+ *     to be accessed via the I/O manager interface.
+ *
+ * Copyright (C) 2002 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+         if ((struct)->magic != (code)) return (code)
+
+struct inode_private_data {
+       int                             magic;
+       char                            name[32];
+       ext2_file_t                     file;
+       ext2_filsys                     fs;
+       ext2_ino_t                      ino;
+       struct ext2_inode               inode;
+       int                             flags;
+       struct inode_private_data       *next;
+};
+
+#define CHANNEL_HAS_INODE      0x8000
+
+static struct inode_private_data *top_intern;
+static int ino_unique = 0;
+
+static errcode_t inode_open(const char *name, int flags, io_channel *channel);
+static errcode_t inode_close(io_channel channel);
+static errcode_t inode_set_blksize(io_channel channel, int blksize);
+static errcode_t inode_read_blk(io_channel channel, unsigned long block,
+                              int count, void *data);
+static errcode_t inode_write_blk(io_channel channel, unsigned long block,
+                               int count, const void *data);
+static errcode_t inode_flush(io_channel channel);
+static errcode_t inode_write_byte(io_channel channel, unsigned long offset,
+                               int size, const void *data);
+
+static struct struct_io_manager struct_inode_manager = {
+       EXT2_ET_MAGIC_IO_MANAGER,
+       "Inode I/O Manager",
+       inode_open,
+       inode_close,
+       inode_set_blksize,
+       inode_read_blk,
+       inode_write_blk,
+       inode_flush,
+       inode_write_byte
+};
+
+io_manager inode_io_manager = &struct_inode_manager;
+
+errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino,
+                                 struct ext2_inode *inode,
+                                 char **name)
+{
+       struct inode_private_data       *data;
+       errcode_t                       retval;
+
+       if ((retval = ext2fs_get_mem(sizeof(struct inode_private_data),
+                                    &data)))
+               return retval;
+       data->magic = EXT2_ET_MAGIC_INODE_IO_CHANNEL;
+       sprintf(data->name, "%u:%d", ino, ino_unique++);
+       data->file = 0;
+       data->fs = fs;
+       data->ino = ino;
+       data->flags = 0;
+       if (inode) {
+               memcpy(&data->inode, inode, sizeof(struct ext2_inode));
+               data->flags |= CHANNEL_HAS_INODE;
+       }
+       data->next = top_intern;
+       top_intern = data;
+       *name = data->name;
+       return 0;
+}
+
+errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino,
+                                char **name)
+{
+       return ext2fs_inode_io_intern2(fs, ino, NULL, name);
+}
+
+
+static errcode_t inode_open(const char *name, int flags, io_channel *channel)
+{
+       io_channel      io = NULL;
+       struct inode_private_data *prev, *data = NULL;
+       errcode_t       retval;
+       int             open_flags;
+
+       if (name == 0)
+               return EXT2_ET_BAD_DEVICE_NAME;
+
+       for (data = top_intern, prev = NULL; data;
+            prev = data, data = data->next)
+               if (strcmp(name, data->name) == 0)
+                       break;
+       if (!data)
+               return ENOENT;
+       if (prev)
+               prev->next = data->next;
+       else
+               top_intern = data->next;
+
+       retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
+       if (retval)
+               goto cleanup;
+       memset(io, 0, sizeof(struct struct_io_channel));
+
+       io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+       io->manager = inode_io_manager;
+       retval = ext2fs_get_mem(strlen(name)+1, &io->name);
+       if (retval)
+               goto cleanup;
+
+       strcpy(io->name, name);
+       io->private_data = data;
+       io->block_size = 1024;
+       io->read_error = 0;
+       io->write_error = 0;
+       io->refcount = 1;
+
+       open_flags = (flags & IO_FLAG_RW) ? EXT2_FILE_WRITE : 0;
+       retval = ext2fs_file_open2(data->fs, data->ino,
+                                  (data->flags & CHANNEL_HAS_INODE) ?
+                                  &data->inode : 0, open_flags,
+                                  &data->file);
+       if (retval)
+               goto cleanup;
+               
+       *channel = io;
+       return 0;
+
+cleanup:
+       if (data) {
+               ext2fs_free_mem(&data);
+       }
+       if (io)
+               ext2fs_free_mem(&io);
+       return retval;
+}
+
+static errcode_t inode_close(io_channel channel)
+{
+       struct inode_private_data *data;
+       errcode_t       retval = 0;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct inode_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+       if (--channel->refcount > 0)
+               return 0;
+
+       retval = ext2fs_file_close(data->file);
+       
+       ext2fs_free_mem(&channel->private_data);
+       if (channel->name)
+               ext2fs_free_mem(&channel->name);
+       ext2fs_free_mem(&channel);
+       return retval;
+}
+
+static errcode_t inode_set_blksize(io_channel channel, int blksize)
+{
+       struct inode_private_data *data;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct inode_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+       channel->block_size = blksize;
+       return 0;
+}
+
+
+static errcode_t inode_read_blk(io_channel channel, unsigned long block,
+                              int count, void *buf)
+{
+       struct inode_private_data *data;
+       errcode_t       retval;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct inode_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+       if ((retval = ext2fs_file_lseek(data->file,
+                                       block * channel->block_size,
+                                       EXT2_SEEK_SET, 0)))
+               return retval;
+
+       count = (count < 0) ? -count : (count * channel->block_size);
+
+       return ext2fs_file_read(data->file, buf, count, 0);
+}
+
+static errcode_t inode_write_blk(io_channel channel, unsigned long block,
+                               int count, const void *buf)
+{
+       struct inode_private_data *data;
+       errcode_t       retval;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct inode_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+       if ((retval = ext2fs_file_lseek(data->file,
+                                       block * channel->block_size,
+                                       EXT2_SEEK_SET, 0)))
+               return retval;
+
+       count = (count < 0) ? -count : (count * channel->block_size);
+
+       return ext2fs_file_write(data->file, buf, count, 0);
+}
+
+static errcode_t inode_write_byte(io_channel channel, unsigned long offset,
+                                int size, const void *buf)
+{
+       struct inode_private_data *data;
+       errcode_t       retval = 0;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct inode_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+       if ((retval = ext2fs_file_lseek(data->file, offset,
+                                       EXT2_SEEK_SET, 0)))
+               return retval;
+
+       return ext2fs_file_write(data->file, buf, size, 0);
+}
+
+/*
+ * Flush data buffers to disk.  
+ */
+static errcode_t inode_flush(io_channel channel)
+{
+       struct inode_private_data *data;
+       
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct inode_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL);
+
+       return ext2fs_file_flush(data->file);
+}
+
diff --git a/e2fsprogs/ext2fs/io_manager.c b/e2fsprogs/ext2fs/io_manager.c
new file mode 100644 (file)
index 0000000..e50d7e4
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * io_manager.c --- the I/O manager abstraction
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t io_channel_set_options(io_channel channel, const char *opts)
+{
+       errcode_t retval = 0;
+       char *next, *ptr, *options, *arg;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+
+       if (!opts)
+               return 0;
+
+       if (!channel->manager->set_option)
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       options = malloc(strlen(opts)+1);
+       if (!options)
+               return EXT2_ET_NO_MEMORY;
+       strcpy(options, opts);
+       ptr = options;
+
+       while (ptr && *ptr) {
+               next = strchr(ptr, '&');
+               if (next)
+                       *next++ = 0;
+
+               arg = strchr(ptr, '=');
+               if (arg)
+                       *arg++ = 0;
+
+               retval = (channel->manager->set_option)(channel, ptr, arg);
+               if (retval)
+                       break;
+               ptr = next;
+       }
+       free(options);
+       return retval;
+}
+
+errcode_t io_channel_write_byte(io_channel channel, unsigned long offset,
+                               int count, const void *data)
+{
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+
+       if (channel->manager->write_byte) 
+               return channel->manager->write_byte(channel, offset, 
+                                                   count, data);
+
+       return EXT2_ET_UNIMPLEMENTED;
+}
diff --git a/e2fsprogs/ext2fs/irel.h b/e2fsprogs/ext2fs/irel.h
new file mode 100644 (file)
index 0000000..9b943ce
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * irel.h
+ * 
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+struct ext2_inode_reference {
+       blk_t   block;
+       __u16 offset;
+};
+
+struct ext2_inode_relocate_entry {
+       ext2_ino_t      new;
+       ext2_ino_t      orig;
+       __u16           flags;
+       __u16           max_refs;
+};
+
+typedef struct ext2_inode_relocation_table *ext2_irel;
+
+struct ext2_inode_relocation_table {
+       __u32   magic;
+       char    *name;
+       ext2_ino_t      current;
+       void    *priv_data;
+
+       /*
+        * Add an inode relocation entry.
+        */
+       errcode_t (*put)(ext2_irel irel, ext2_ino_t old,
+                             struct ext2_inode_relocate_entry *ent);
+       /*
+        * Get an inode relocation entry.
+        */
+       errcode_t (*get)(ext2_irel irel, ext2_ino_t old,
+                             struct ext2_inode_relocate_entry *ent);
+
+       /*
+        * Get an inode relocation entry by its original inode number
+        */
+       errcode_t (*get_by_orig)(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old,
+                                struct ext2_inode_relocate_entry *ent);
+
+       /*
+        * Initialize for iterating over the inode relocation entries.
+        */
+       errcode_t (*start_iter)(ext2_irel irel);
+
+       /*
+        * The iterator function for the inode relocation entries.
+        * Returns an inode number of 0 when out of entries.
+        */
+       errcode_t (*next)(ext2_irel irel, ext2_ino_t *old,
+                         struct ext2_inode_relocate_entry *ent);
+
+       /*
+        * Add an inode reference (i.e., note the fact that a
+        * particular block/offset contains a reference to an inode)
+        */
+       errcode_t (*add_ref)(ext2_irel irel, ext2_ino_t ino,
+                            struct ext2_inode_reference *ref);
+
+       /*
+        * Initialize for iterating over the inode references for a
+        * particular inode.
+        */
+       errcode_t (*start_iter_ref)(ext2_irel irel, ext2_ino_t ino);
+
+       /*
+        * The iterator function for the inode references for an
+        * inode.  The references for only one inode can be interator
+        * over at a time, as the iterator state is stored in ext2_irel.
+        */
+       errcode_t (*next_ref)(ext2_irel irel,
+                             struct ext2_inode_reference *ref);
+
+       /*
+        * Move the inode relocation table from one inode number to
+        * another.  Note that the inode references also must move.
+        */
+       errcode_t (*move)(ext2_irel irel, ext2_ino_t old, ext2_ino_t new);
+
+       /*
+        * Remove an inode relocation entry, along with all of the
+        * inode references.
+        */
+       errcode_t (*delete)(ext2_irel irel, ext2_ino_t old);
+
+       /*
+        * Free the inode relocation table.
+        */
+       errcode_t (*free)(ext2_irel irel);
+};
+
+errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode,
+                                   ext2_irel *irel);
+
+#define ext2fs_irel_put(irel, old, ent) ((irel)->put((irel), old, ent))
+#define ext2fs_irel_get(irel, old, ent) ((irel)->get((irel), old, ent))
+#define ext2fs_irel_get_by_orig(irel, orig, old, ent) \
+                       ((irel)->get_by_orig((irel), orig, old, ent))
+#define ext2fs_irel_start_iter(irel) ((irel)->start_iter((irel)))
+#define ext2fs_irel_next(irel, old, ent) ((irel)->next((irel), old, ent))
+#define ext2fs_irel_add_ref(irel, ino, ref) ((irel)->add_ref((irel), ino, ref))
+#define ext2fs_irel_start_iter_ref(irel, ino) ((irel)->start_iter_ref((irel), ino))
+#define ext2fs_irel_next_ref(irel, ref) ((irel)->next_ref((irel), ref))
+#define ext2fs_irel_move(irel, old, new) ((irel)->move((irel), old, new))
+#define ext2fs_irel_delete(irel, old) ((irel)->delete((irel), old))
+#define ext2fs_irel_free(irel) ((irel)->free((irel)))
diff --git a/e2fsprogs/ext2fs/irel_ma.c b/e2fsprogs/ext2fs/irel_ma.c
new file mode 100644 (file)
index 0000000..eedbe55
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * irel_ma.c
+ * 
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "irel.h"
+
+static errcode_t ima_put(ext2_irel irel, ext2_ino_t old,
+                        struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_get(ext2_irel irel, ext2_ino_t old,
+                        struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old,
+                                struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_start_iter(ext2_irel irel);
+static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old,
+                         struct ext2_inode_relocate_entry *ent);
+static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino,
+                            struct ext2_inode_reference *ref);
+static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino);
+static errcode_t ima_next_ref(ext2_irel irel, struct ext2_inode_reference *ref);
+static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new);
+static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old);
+static errcode_t ima_free(ext2_irel irel);
+
+/*
+ * This data structure stores the array of inode references; there is
+ * a structure for each inode.
+ */
+struct inode_reference_entry {
+       __u16 num;
+       struct ext2_inode_reference *refs;
+};
+
+struct irel_ma {
+       __u32 magic;
+       ext2_ino_t max_inode;
+       ext2_ino_t ref_current;
+       int   ref_iter;
+       ext2_ino_t      *orig_map;
+       struct ext2_inode_relocate_entry *entries;
+       struct inode_reference_entry *ref_entries;
+};
+
+errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode,
+                                     ext2_irel *new_irel)
+{
+       ext2_irel               irel = 0;
+       errcode_t       retval;
+       struct irel_ma  *ma = 0;
+       size_t          size;
+
+       *new_irel = 0;
+
+       /*
+        * Allocate memory structures
+        */
+       retval = ext2fs_get_mem(sizeof(struct ext2_inode_relocation_table),
+                               &irel);
+       if (retval)
+               goto errout;
+       memset(irel, 0, sizeof(struct ext2_inode_relocation_table));
+       
+       retval = ext2fs_get_mem(strlen(name)+1, &irel->name);
+       if (retval)
+               goto errout;
+       strcpy(irel->name, name);
+       
+       retval = ext2fs_get_mem(sizeof(struct irel_ma), &ma);
+       if (retval)
+               goto errout;
+       memset(ma, 0, sizeof(struct irel_ma));
+       irel->priv_data = ma;
+       
+       size = (size_t) (sizeof(ext2_ino_t) * (max_inode+1));
+       retval = ext2fs_get_mem(size, &ma->orig_map);
+       if (retval)
+               goto errout;
+       memset(ma->orig_map, 0, size);
+
+       size = (size_t) (sizeof(struct ext2_inode_relocate_entry) *
+                        (max_inode+1));
+       retval = ext2fs_get_mem(size, &ma->entries);
+       if (retval)
+               goto errout;
+       memset(ma->entries, 0, size);
+
+       size = (size_t) (sizeof(struct inode_reference_entry) *
+                        (max_inode+1));
+       retval = ext2fs_get_mem(size, &ma->ref_entries);
+       if (retval)
+               goto errout;
+       memset(ma->ref_entries, 0, size);
+       ma->max_inode = max_inode;
+
+       /*
+        * Fill in the irel data structure
+        */
+       irel->put = ima_put;
+       irel->get = ima_get;
+       irel->get_by_orig = ima_get_by_orig;
+       irel->start_iter = ima_start_iter;
+       irel->next = ima_next;
+       irel->add_ref = ima_add_ref;
+       irel->start_iter_ref = ima_start_iter_ref;
+       irel->next_ref = ima_next_ref;
+       irel->move = ima_move;
+       irel->delete = ima_delete;
+       irel->free = ima_free;
+       
+       *new_irel = irel;
+       return 0;
+
+errout:
+       ima_free(irel);
+       return retval;
+}
+
+static errcode_t ima_put(ext2_irel irel, ext2_ino_t old,
+                       struct ext2_inode_relocate_entry *ent)
+{
+       struct inode_reference_entry    *ref_ent;
+       struct irel_ma                  *ma;
+       errcode_t                       retval;
+       size_t                          size, old_size;
+
+       ma = irel->priv_data;
+       if (old > ma->max_inode)
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       /*
+        * Force the orig field to the correct value; the application
+        * program shouldn't be messing with this field.
+        */
+       if (ma->entries[(unsigned) old].new == 0)
+               ent->orig = old;
+       else
+               ent->orig = ma->entries[(unsigned) old].orig;
+       
+       /*
+        * If max_refs has changed, reallocate the refs array
+        */
+       ref_ent = ma->ref_entries + (unsigned) old;
+       if (ref_ent->refs && ent->max_refs !=
+           ma->entries[(unsigned) old].max_refs) {
+               size = (sizeof(struct ext2_inode_reference) * ent->max_refs);
+               old_size = (sizeof(struct ext2_inode_reference) *
+                           ma->entries[(unsigned) old].max_refs);
+               retval = ext2fs_resize_mem(old_size, size, &ref_ent->refs);
+               if (retval)
+                       return retval;
+       }
+
+       ma->entries[(unsigned) old] = *ent;
+       ma->orig_map[(unsigned) ent->orig] = old;
+       return 0;
+}
+
+static errcode_t ima_get(ext2_irel irel, ext2_ino_t old,
+                       struct ext2_inode_relocate_entry *ent)
+{
+       struct irel_ma  *ma;
+
+       ma = irel->priv_data;
+       if (old > ma->max_inode)
+               return EXT2_ET_INVALID_ARGUMENT;
+       if (ma->entries[(unsigned) old].new == 0)
+               return ENOENT;
+       *ent = ma->entries[(unsigned) old];
+       return 0;
+}
+
+static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old,
+                       struct ext2_inode_relocate_entry *ent)
+{
+       struct irel_ma  *ma;
+       ext2_ino_t      ino;
+
+       ma = irel->priv_data;
+       if (orig > ma->max_inode)
+               return EXT2_ET_INVALID_ARGUMENT;
+       ino = ma->orig_map[(unsigned) orig];
+       if (ino == 0)
+               return ENOENT;
+       *old = ino;
+       *ent = ma->entries[(unsigned) ino];
+       return 0;
+}
+
+static errcode_t ima_start_iter(ext2_irel irel)
+{
+       irel->current = 0;
+       return 0;
+}
+
+static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old,
+                        struct ext2_inode_relocate_entry *ent)
+{
+       struct irel_ma  *ma;
+
+       ma = irel->priv_data;
+       while (++irel->current < ma->max_inode) {
+               if (ma->entries[(unsigned) irel->current].new == 0)
+                       continue;
+               *old = irel->current;
+               *ent = ma->entries[(unsigned) irel->current];
+               return 0;
+       }
+       *old = 0;
+       return 0;
+}
+
+static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino,
+                            struct ext2_inode_reference *ref)
+{
+       struct irel_ma  *ma;
+       size_t          size;
+       struct inode_reference_entry *ref_ent;
+       struct ext2_inode_relocate_entry *ent;
+       errcode_t               retval;
+
+       ma = irel->priv_data;
+       if (ino > ma->max_inode)
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       ref_ent = ma->ref_entries + (unsigned) ino;
+       ent = ma->entries + (unsigned) ino;
+       
+       /*
+        * If the inode reference array doesn't exist, create it.
+        */
+       if (ref_ent->refs == 0) {
+               size = (size_t) ((sizeof(struct ext2_inode_reference) * 
+                                 ent->max_refs));
+               retval = ext2fs_get_mem(size, &ref_ent->refs);
+               if (retval)
+                       return retval;
+               memset(ref_ent->refs, 0, size);
+               ref_ent->num = 0;
+       }
+
+       if (ref_ent->num >= ent->max_refs)
+               return EXT2_ET_TOO_MANY_REFS;
+
+       ref_ent->refs[(unsigned) ref_ent->num++] = *ref;
+       return 0;
+}
+
+static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino)
+{
+       struct irel_ma  *ma;
+
+       ma = irel->priv_data;
+       if (ino > ma->max_inode)
+               return EXT2_ET_INVALID_ARGUMENT;
+       if (ma->entries[(unsigned) ino].new == 0)
+               return ENOENT;
+       ma->ref_current = ino;
+       ma->ref_iter = 0;
+       return 0;
+}
+
+static errcode_t ima_next_ref(ext2_irel irel,
+                             struct ext2_inode_reference *ref)
+{
+       struct irel_ma  *ma;
+       struct inode_reference_entry *ref_ent;
+
+       ma = irel->priv_data;
+       
+       ref_ent = ma->ref_entries + ma->ref_current;
+
+       if ((ref_ent->refs == NULL) ||
+           (ma->ref_iter >= ref_ent->num)) {
+               ref->block = 0;
+               ref->offset = 0;
+               return 0;
+       }
+       *ref = ref_ent->refs[ma->ref_iter++];
+       return 0;
+}
+
+
+static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new)
+{
+       struct irel_ma  *ma;
+
+       ma = irel->priv_data;
+       if ((old > ma->max_inode) || (new > ma->max_inode))
+               return EXT2_ET_INVALID_ARGUMENT;
+       if (ma->entries[(unsigned) old].new == 0)
+               return ENOENT;
+       
+       ma->entries[(unsigned) new] = ma->entries[(unsigned) old];
+       if (ma->ref_entries[(unsigned) new].refs)
+               ext2fs_free_mem(&ma->ref_entries[(unsigned) new].refs);
+       ma->ref_entries[(unsigned) new] = ma->ref_entries[(unsigned) old];
+       
+       ma->entries[(unsigned) old].new = 0;
+       ma->ref_entries[(unsigned) old].num = 0;
+       ma->ref_entries[(unsigned) old].refs = 0;
+
+       ma->orig_map[ma->entries[new].orig] = new;
+       return 0;
+}
+
+static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old)
+{
+       struct irel_ma  *ma;
+
+       ma = irel->priv_data;
+       if (old > ma->max_inode)
+               return EXT2_ET_INVALID_ARGUMENT;
+       if (ma->entries[(unsigned) old].new == 0)
+               return ENOENT;
+       
+       ma->entries[old].new = 0;
+       if (ma->ref_entries[(unsigned) old].refs)
+               ext2fs_free_mem(&ma->ref_entries[(unsigned) old].refs);
+       ma->orig_map[ma->entries[(unsigned) old].orig] = 0;
+       
+       ma->ref_entries[(unsigned) old].num = 0;
+       ma->ref_entries[(unsigned) old].refs = 0;
+       return 0;
+}
+
+static errcode_t ima_free(ext2_irel irel)
+{
+       struct irel_ma  *ma;
+       ext2_ino_t      ino;
+
+       if (!irel)
+               return 0;
+
+       ma = irel->priv_data;
+
+       if (ma) {
+               if (ma->orig_map)
+                       ext2fs_free_mem(&ma->orig_map);
+               if (ma->entries)
+                       ext2fs_free_mem(&ma->entries);
+               if (ma->ref_entries) {
+                       for (ino = 0; ino <= ma->max_inode; ino++) {
+                               if (ma->ref_entries[(unsigned) ino].refs)
+                                       ext2fs_free_mem(&ma->ref_entries[(unsigned) ino].refs);
+                       }
+                       ext2fs_free_mem(&ma->ref_entries);
+               }
+               ext2fs_free_mem(&ma);
+       }
+       if (irel->name)
+               ext2fs_free_mem(&irel->name);
+       ext2fs_free_mem(&irel);
+       return 0;
+}
diff --git a/e2fsprogs/ext2fs/ismounted.c b/e2fsprogs/ext2fs/ismounted.c
new file mode 100644 (file)
index 0000000..3f2241d
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * ismounted.c --- Check to see if the filesystem was mounted
+ * 
+ * Copyright (C) 1995,1996,1997,1998,1999,2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#ifdef HAVE_GETMNTINFO
+#include <paths.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif /* HAVE_GETMNTINFO */
+#include <string.h>
+#include <sys/stat.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifdef HAVE_MNTENT_H
+/*
+ * Helper function which checks a file in /etc/mtab format to see if a
+ * filesystem is mounted.  Returns an error if the file doesn't exist
+ * or can't be opened.  
+ */
+static errcode_t check_mntent_file(const char *mtab_file, const char *file, 
+                                  int *mount_flags, char *mtpt, int mtlen)
+{
+       struct mntent   *mnt;
+       struct stat     st_buf;
+       errcode_t       retval = 0;
+       dev_t           file_dev=0, file_rdev=0;
+       ino_t           file_ino=0;
+       FILE            *f;
+       int             fd;
+
+       *mount_flags = 0;
+       if ((f = setmntent (mtab_file, "r")) == NULL)
+               return errno;
+       if (stat(file, &st_buf) == 0) {
+               if (S_ISBLK(st_buf.st_mode)) {
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+                       file_rdev = st_buf.st_rdev;
+#endif /* __GNU__ */
+               } else {
+                       file_dev = st_buf.st_dev;
+                       file_ino = st_buf.st_ino;
+               }
+       }
+       while ((mnt = getmntent (f)) != NULL) {
+               if (strcmp(file, mnt->mnt_fsname) == 0)
+                       break;
+               if (stat(mnt->mnt_fsname, &st_buf) == 0) {
+                       if (S_ISBLK(st_buf.st_mode)) {
+#ifndef __GNU__
+                               if (file_rdev && (file_rdev == st_buf.st_rdev))
+                                       break;
+#endif /* __GNU__ */
+                       } else {
+                               if (file_dev && ((file_dev == st_buf.st_dev) &&
+                                                (file_ino == st_buf.st_ino)))
+                                       break;
+                       }
+               }
+       }
+
+       if (mnt == 0) {
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+               /*
+                * Do an extra check to see if this is the root device.  We
+                * can't trust /etc/mtab, and /proc/mounts will only list
+                * /dev/root for the root filesystem.  Argh.  Instead we
+                * check if the given device has the same major/minor number
+                * as the device that the root directory is on.
+                */
+               if (file_rdev && stat("/", &st_buf) == 0) {
+                       if (st_buf.st_dev == file_rdev) {
+                               *mount_flags = EXT2_MF_MOUNTED;
+                               if (mtpt)
+                                       strncpy(mtpt, "/", mtlen);
+                               goto is_root;
+                       }
+               }
+#endif /* __GNU__ */
+               goto errout;
+       }
+#ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */
+       /* Validate the entry in case /etc/mtab is out of date */
+       /* 
+        * We need to be paranoid, because some broken distributions
+        * (read: Slackware) don't initialize /etc/mtab before checking
+        * all of the non-root filesystems on the disk.
+        */
+       if (stat(mnt->mnt_dir, &st_buf) < 0) {
+               retval = errno;
+               if (retval == ENOENT) {
+#ifdef DEBUG
+                       printf("Bogus entry in %s!  (%s does not exist)\n",
+                              mtab_file, mnt->mnt_dir);
+#endif /* DEBUG */
+                       retval = 0;
+               }
+               goto errout;
+       }
+       if (file_rdev && (st_buf.st_dev != file_rdev)) {
+#ifdef DEBUG
+               printf("Bogus entry in %s!  (%s not mounted on %s)\n",
+                      mtab_file, file, mnt->mnt_dir);
+#endif /* DEBUG */
+               goto errout;
+       }
+#endif /* __GNU__ */
+       *mount_flags = EXT2_MF_MOUNTED;
+       
+#ifdef MNTOPT_RO
+       /* Check to see if the ro option is set */
+       if (hasmntopt(mnt, MNTOPT_RO))
+               *mount_flags |= EXT2_MF_READONLY;
+#endif
+
+       if (mtpt)
+               strncpy(mtpt, mnt->mnt_dir, mtlen);
+       /*
+        * Check to see if we're referring to the root filesystem.
+        * If so, do a manual check to see if we can open /etc/mtab
+        * read/write, since if the root is mounted read/only, the
+        * contents of /etc/mtab may not be accurate.
+        */
+       if (!strcmp(mnt->mnt_dir, "/")) {
+is_root:
+#define TEST_FILE "/.ismount-test-file"                
+               *mount_flags |= EXT2_MF_ISROOT;
+               fd = open(TEST_FILE, O_RDWR|O_CREAT);
+               if (fd < 0) {
+                       if (errno == EROFS)
+                               *mount_flags |= EXT2_MF_READONLY;
+               } else
+                       close(fd);
+               (void) unlink(TEST_FILE);
+       }
+       retval = 0;
+errout:
+       endmntent (f);
+       return retval;
+}
+
+static errcode_t check_mntent(const char *file, int *mount_flags,
+                             char *mtpt, int mtlen)
+{
+       errcode_t       retval;
+
+#ifdef DEBUG
+       retval = check_mntent_file("/tmp/mtab", file, mount_flags,
+                                  mtpt, mtlen);
+       if (retval == 0)
+               return 0;
+#endif /* DEBUG */
+#ifdef __linux__
+       retval = check_mntent_file("/proc/mounts", file, mount_flags,
+                                  mtpt, mtlen);
+       if (retval == 0 && (*mount_flags != 0))
+               return 0;
+#endif /* __linux__ */
+#if defined(MOUNTED) || defined(_PATH_MOUNTED)
+#ifndef MOUNTED
+#define MOUNTED _PATH_MOUNTED
+#endif /* MOUNTED */
+       retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen);
+       return retval;
+#else 
+       *mount_flags = 0;
+       return 0;
+#endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */
+}
+
+#else
+#if defined(HAVE_GETMNTINFO)
+
+static errcode_t check_getmntinfo(const char *file, int *mount_flags,
+                                 char *mtpt, int mtlen)
+{
+       struct statfs *mp;
+        int    len, n;
+        const  char   *s1;
+       char    *s2;
+
+        n = getmntinfo(&mp, MNT_NOWAIT);
+        if (n == 0)
+               return errno;
+
+        len = sizeof(_PATH_DEV) - 1;
+        s1 = file;
+        if (strncmp(_PATH_DEV, s1, len) == 0)
+                s1 += len;
+       *mount_flags = 0;
+        while (--n >= 0) {
+                s2 = mp->f_mntfromname;
+                if (strncmp(_PATH_DEV, s2, len) == 0) {
+                        s2 += len - 1;
+                        *s2 = 'r';
+                }
+                if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) {
+                       *mount_flags = EXT2_MF_MOUNTED;
+                       break;
+               }
+                ++mp;
+       }
+       if (mtpt)
+               strncpy(mtpt, mp->f_mntonname, mtlen);
+       return 0;
+}
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+
+/*
+ * Check to see if we're dealing with the swap device.
+ */
+static int is_swap_device(const char *file)
+{
+       FILE            *f;
+       char            buf[1024], *cp;
+       dev_t           file_dev;
+       struct stat     st_buf;
+       int             ret = 0;
+
+       file_dev = 0;
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+       if ((stat(file, &st_buf) == 0) &&
+           S_ISBLK(st_buf.st_mode))
+               file_dev = st_buf.st_rdev;
+#endif /* __GNU__ */
+
+       if (!(f = fopen("/proc/swaps", "r")))
+               return 0;
+       /* Skip the first line */
+       fgets(buf, sizeof(buf), f);
+       while (!feof(f)) {
+               if (!fgets(buf, sizeof(buf), f))
+                       break;
+               if ((cp = strchr(buf, ' ')) != NULL)
+                       *cp = 0;
+               if ((cp = strchr(buf, '\t')) != NULL)
+                       *cp = 0;
+               if (strcmp(buf, file) == 0) {
+                       ret++;
+                       break;
+               }
+#ifndef __GNU__
+               if (file_dev && (stat(buf, &st_buf) == 0) &&
+                   S_ISBLK(st_buf.st_mode) &&
+                   file_dev == st_buf.st_rdev) {
+                       ret++;
+                       break;
+               }
+#endif         /* __GNU__ */
+       }
+       fclose(f);
+       return ret;
+}
+
+
+/*
+ * ext2fs_check_mount_point() returns 1 if the device is mounted, 0
+ * otherwise.  If mtpt is non-NULL, the directory where the device is
+ * mounted is copied to where mtpt is pointing, up to mtlen
+ * characters.
+ */
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags,
+                                 char *mtpt, int mtlen)
+{
+       if (is_swap_device(device)) {
+               *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP;
+               strncpy(mtpt, "<swap>", mtlen);
+               return 0;
+       }
+#ifdef HAVE_MNTENT_H
+       return check_mntent(device, mount_flags, mtpt, mtlen);
+#else 
+#ifdef HAVE_GETMNTINFO
+       return check_getmntinfo(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef __GNUC__
+ #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!"
+#endif
+       *mount_flags = 0;
+       return 0;
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+}
+
+/*
+ * ext2fs_check_if_mounted() sets the mount_flags EXT2_MF_MOUNTED,
+ * EXT2_MF_READONLY, and EXT2_MF_ROOT
+ * 
+ */
+errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags)
+{
+       return ext2fs_check_mount_point(file, mount_flags, NULL, 0);
+}
+
+#ifdef DEBUG
+int main(int argc, char **argv)
+{
+       int     retval, mount_flags;
+       char    mntpt[80];
+       
+       if (argc < 2) {
+               fprintf(stderr, "Usage: %s device\n", argv[0]);
+               exit(1);
+       }
+
+       mntpt[0] = 0;
+       retval = ext2fs_check_mount_point(argv[1], &mount_flags,
+                                         mntpt, sizeof(mntpt));
+       if (retval) {
+               com_err(argv[0], retval,
+                       "while calling ext2fs_check_if_mounted");
+               exit(1);
+       }
+       printf("Device %s reports flags %02x\n", argv[1], mount_flags);
+       if (mount_flags & EXT2_MF_MOUNTED)
+               printf("\t%s is mounted.\n", argv[1]);
+
+       if (mount_flags & EXT2_MF_SWAP)
+               printf("\t%s is a swap device.\n", argv[1]);
+
+       if (mount_flags & EXT2_MF_READONLY)
+               printf("\t%s is read-only.\n", argv[1]);
+       
+       if (mount_flags & EXT2_MF_ISROOT)
+               printf("\t%s is the root filesystem.\n", argv[1]);
+       if (mntpt[0])
+               printf("\t%s is mounted on %s.\n", argv[1], mntpt);
+       
+       exit(0);
+}
+#endif /* DEBUG */
diff --git a/e2fsprogs/ext2fs/jfs_compat.h b/e2fsprogs/ext2fs/jfs_compat.h
new file mode 100644 (file)
index 0000000..30ad1ef
--- /dev/null
@@ -0,0 +1,67 @@
+
+#ifndef _JFS_COMPAT_H
+#define _JFS_COMPAT_H
+
+#include "kernel-list.h"
+#include <errno.h>
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#define printk printf
+#define KERN_ERR ""
+#define KERN_DEBUG ""
+
+#define READ 0
+#define WRITE 1
+
+#define cpu_to_be32(n) htonl(n)
+#define be32_to_cpu(n) ntohl(n)
+
+typedef unsigned int tid_t;
+typedef struct journal_s journal_t;
+
+struct buffer_head;
+struct inode;
+
+struct journal_s
+{
+       unsigned long           j_flags;
+       int                     j_errno;
+       struct buffer_head *    j_sb_buffer;
+       struct journal_superblock_s *j_superblock;
+       int                     j_format_version;
+       unsigned long           j_head;
+       unsigned long           j_tail;
+       unsigned long           j_free;
+       unsigned long           j_first, j_last;
+       kdev_t                  j_dev;
+       kdev_t                  j_fs_dev;
+       int                     j_blocksize;
+       unsigned int            j_blk_offset;
+       unsigned int            j_maxlen;
+       struct inode *          j_inode;
+       tid_t                   j_tail_sequence;
+       tid_t                   j_transaction_sequence;
+       __u8                    j_uuid[16];
+       struct jbd_revoke_table_s *j_revoke;
+};
+
+#define J_ASSERT(assert)                                               \
+       do { if (!(assert)) {                                           \
+               printf ("Assertion failure in %s() at %s line %d: "     \
+                       "\"%s\"\n",                                     \
+                       __FUNCTION__, __FILE__, __LINE__, # assert);    \
+               fatal_error(e2fsck_global_ctx, 0);                      \
+       } } while (0)
+
+#define is_journal_abort(x) 0
+
+#define BUFFER_TRACE(bh, info) do {} while (0)
+
+/* Need this so we can compile with configure --enable-gcc-wall */
+#ifdef NO_INLINE_FUNCS
+#define inline
+#endif
+
+#endif /* _JFS_COMPAT_H */
diff --git a/e2fsprogs/ext2fs/jfs_dat.h b/e2fsprogs/ext2fs/jfs_dat.h
new file mode 100644 (file)
index 0000000..d6ad9c4
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * jfs_dat.h --- stripped down header file which only contains the JFS
+ *     on-disk data structures
+ */
+
+#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
+
+/*
+ * On-disk structures
+ */
+
+/* 
+ * Descriptor block types:
+ */
+
+#define JFS_DESCRIPTOR_BLOCK   1
+#define JFS_COMMIT_BLOCK       2
+#define JFS_SUPERBLOCK         3
+
+/*
+ * Standard header for all descriptor blocks:
+ */
+typedef struct journal_header_s
+{
+       __u32           h_magic;
+       __u32           h_blocktype;
+       __u32           h_sequence;
+} journal_header_t;
+
+
+/* 
+ * The block tag: used to describe a single buffer in the journal 
+ */
+typedef struct journal_block_tag_s
+{
+       __u32           t_blocknr;      /* The on-disk block number */
+       __u32           t_flags;        /* See below */
+} journal_block_tag_t;
+
+/* Definitions for the journal tag flags word: */
+#define JFS_FLAG_ESCAPE                1       /* on-disk block is escaped */
+#define JFS_FLAG_SAME_UUID     2       /* block has same uuid as previous */
+#define JFS_FLAG_DELETED       4       /* block deleted by this transaction */
+#define JFS_FLAG_LAST_TAG      8       /* last tag in this descriptor block */
+
+
+/*
+ * The journal superblock
+ */
+typedef struct journal_superblock_s
+{
+       journal_header_t s_header;
+
+       /* Static information describing the journal */
+       __u32           s_blocksize;    /* journal device blocksize */
+       __u32           s_maxlen;       /* total blocks in journal file */
+       __u32           s_first;        /* first block of log information */
+       
+       /* Dynamic information describing the current state of the log */
+       __u32           s_sequence;     /* first commit ID expected in log */
+       __u32           s_start;        /* blocknr of start of log */
+       
+} journal_superblock_t;
+
diff --git a/e2fsprogs/ext2fs/jfs_user.h b/e2fsprogs/ext2fs/jfs_user.h
new file mode 100644 (file)
index 0000000..3a52123
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef _JFS_USER_H
+#define _JFS_USER_H
+
+typedef unsigned short kdev_t;
+
+#include "kernel-jbd.h"
+
+#endif /* _JFS_USER_H */
diff --git a/e2fsprogs/ext2fs/kernel-jbd.h b/e2fsprogs/ext2fs/kernel-jbd.h
new file mode 100644 (file)
index 0000000..d0efdb3
--- /dev/null
@@ -0,0 +1,910 @@
+/*
+ * linux/include/linux/jbd.h
+ * 
+ * Written by Stephen C. Tweedie <sct@redhat.com>
+ *
+ * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * Definitions for transaction data structures for the buffer cache
+ * filesystem journaling support.
+ */
+
+#ifndef _LINUX_JBD_H
+#define _LINUX_JBD_H
+
+#if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) || !defined(__KERNEL__)
+
+/* Allow this file to be included directly into e2fsprogs */
+#ifndef __KERNEL__
+#include "jfs_compat.h"
+#define JFS_DEBUG
+#define jfs_debug jbd_debug
+#else
+
+#include <linux/journal-head.h>
+#include <linux/stddef.h>
+#include <asm/semaphore.h>
+#endif
+
+#ifndef __GNUC__
+#define __FUNCTION__ ""
+#endif
+
+#define journal_oom_retry 1
+
+#ifdef __STDC__
+#ifdef __CONFIG_JBD_DEBUG__E2FS
+/*
+ * Define JBD_EXPENSIVE_CHECKING to enable more expensive internal
+ * consistency checks.  By default we don't do this unless
+ * __CONFIG_JBD_DEBUG__E2FS is on.
+ */
+#define JBD_EXPENSIVE_CHECKING
+extern int journal_enable_debug;
+
+#define jbd_debug(n, f, a...)                                          \
+       do {                                                            \
+               if ((n) <= journal_enable_debug) {                      \
+                       printk (KERN_DEBUG "(%s, %d): %s: ",            \
+                               __FILE__, __LINE__, __FUNCTION__);      \
+                       printk (f, ## a);                               \
+               }                                                       \
+       } while (0)
+#else
+#ifdef __GNUC__
+#define jbd_debug(f, a...)     /**/
+#else
+#define jbd_debug(f, ...)      /**/
+#endif 
+#endif
+#else
+#define jbd_debug(x)           /* AIX doesn't do STDC */
+#endif
+
+extern void * __jbd_kmalloc (char *where, size_t size, int flags, int retry);
+#define jbd_kmalloc(size, flags) \
+       __jbd_kmalloc(__FUNCTION__, (size), (flags), journal_oom_retry)
+#define jbd_rep_kmalloc(size, flags) \
+       __jbd_kmalloc(__FUNCTION__, (size), (flags), 1)
+
+#define JFS_MIN_JOURNAL_BLOCKS 1024
+
+#ifdef __KERNEL__
+typedef struct handle_s                handle_t;       /* Atomic operation type */
+typedef struct journal_s       journal_t;      /* Journal control structure */
+#endif
+
+/*
+ * Internal structures used by the logging mechanism:
+ */
+
+#define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */
+
+/*
+ * On-disk structures
+ */
+
+/* 
+ * Descriptor block types:
+ */
+
+#define JFS_DESCRIPTOR_BLOCK   1
+#define JFS_COMMIT_BLOCK       2
+#define JFS_SUPERBLOCK_V1      3
+#define JFS_SUPERBLOCK_V2      4
+#define JFS_REVOKE_BLOCK       5
+
+/*
+ * Standard header for all descriptor blocks:
+ */
+typedef struct journal_header_s
+{
+       __u32           h_magic;
+       __u32           h_blocktype;
+       __u32           h_sequence;
+} journal_header_t;
+
+
+/* 
+ * The block tag: used to describe a single buffer in the journal 
+ */
+typedef struct journal_block_tag_s
+{
+       __u32           t_blocknr;      /* The on-disk block number */
+       __u32           t_flags;        /* See below */
+} journal_block_tag_t;
+
+/* 
+ * The revoke descriptor: used on disk to describe a series of blocks to
+ * be revoked from the log 
+ */
+typedef struct journal_revoke_header_s
+{
+       journal_header_t r_header;
+       int              r_count;       /* Count of bytes used in the block */
+} journal_revoke_header_t;
+
+
+/* Definitions for the journal tag flags word: */
+#define JFS_FLAG_ESCAPE                1       /* on-disk block is escaped */
+#define JFS_FLAG_SAME_UUID     2       /* block has same uuid as previous */
+#define JFS_FLAG_DELETED       4       /* block deleted by this transaction */
+#define JFS_FLAG_LAST_TAG      8       /* last tag in this descriptor block */
+
+
+/*
+ * The journal superblock.  All fields are in big-endian byte order.
+ */
+typedef struct journal_superblock_s
+{
+/* 0x0000 */
+       journal_header_t s_header;
+
+/* 0x000C */
+       /* Static information describing the journal */
+       __u32   s_blocksize;            /* journal device blocksize */
+       __u32   s_maxlen;               /* total blocks in journal file */
+       __u32   s_first;                /* first block of log information */
+       
+/* 0x0018 */
+       /* Dynamic information describing the current state of the log */
+       __u32   s_sequence;             /* first commit ID expected in log */
+       __u32   s_start;                /* blocknr of start of log */
+
+/* 0x0020 */
+       /* Error value, as set by journal_abort(). */
+       __s32   s_errno;
+
+/* 0x0024 */
+       /* Remaining fields are only valid in a version-2 superblock */
+       __u32   s_feature_compat;       /* compatible feature set */
+       __u32   s_feature_incompat;     /* incompatible feature set */
+       __u32   s_feature_ro_compat;    /* readonly-compatible feature set */
+/* 0x0030 */
+       __u8    s_uuid[16];             /* 128-bit uuid for journal */
+
+/* 0x0040 */
+       __u32   s_nr_users;             /* Nr of filesystems sharing log */
+       
+       __u32   s_dynsuper;             /* Blocknr of dynamic superblock copy*/
+       
+/* 0x0048 */
+       __u32   s_max_transaction;      /* Limit of journal blocks per trans.*/
+       __u32   s_max_trans_data;       /* Limit of data blocks per trans. */
+
+/* 0x0050 */
+       __u32   s_padding[44];
+
+/* 0x0100 */
+       __u8    s_users[16*48];         /* ids of all fs'es sharing the log */
+/* 0x0400 */
+} journal_superblock_t;
+
+#define JFS_HAS_COMPAT_FEATURE(j,mask)                                 \
+       ((j)->j_format_version >= 2 &&                                  \
+        ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask))))
+#define JFS_HAS_RO_COMPAT_FEATURE(j,mask)                              \
+       ((j)->j_format_version >= 2 &&                                  \
+        ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask))))
+#define JFS_HAS_INCOMPAT_FEATURE(j,mask)                               \
+       ((j)->j_format_version >= 2 &&                                  \
+        ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask))))
+
+#define JFS_FEATURE_INCOMPAT_REVOKE    0x00000001
+
+/* Features known to this kernel version: */
+#define JFS_KNOWN_COMPAT_FEATURES      0
+#define JFS_KNOWN_ROCOMPAT_FEATURES    0
+#define JFS_KNOWN_INCOMPAT_FEATURES    JFS_FEATURE_INCOMPAT_REVOKE
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+
+#define JBD_ASSERTIONS
+#ifdef JBD_ASSERTIONS
+#define J_ASSERT(assert)                                               \
+do {                                                                   \
+       if (!(assert)) {                                                \
+               printk (KERN_EMERG                                      \
+                       "Assertion failure in %s() at %s:%d: \"%s\"\n", \
+                       __FUNCTION__, __FILE__, __LINE__, # assert);    \
+               BUG();                                                  \
+       }                                                               \
+} while (0)
+
+#if defined(CONFIG_BUFFER_DEBUG)
+void buffer_assertion_failure(struct buffer_head *bh);
+#define J_ASSERT_BH(bh, expr)                                          \
+       do {                                                            \
+               if (!(expr))                                            \
+                       buffer_assertion_failure(bh);                   \
+               J_ASSERT(expr);                                         \
+       } while (0)
+#define J_ASSERT_JH(jh, expr)  J_ASSERT_BH(jh2bh(jh), expr)
+#else
+#define J_ASSERT_BH(bh, expr)  J_ASSERT(expr)
+#define J_ASSERT_JH(jh, expr)  J_ASSERT(expr)
+#endif
+
+#else
+#define J_ASSERT(assert)
+#endif         /* JBD_ASSERTIONS */
+
+enum jbd_state_bits {
+       BH_JWrite
+         = BH_PrivateStart,    /* 1 if being written to log (@@@ DEBUGGING) */
+       BH_Freed,               /* 1 if buffer has been freed (truncated) */
+       BH_Revoked,             /* 1 if buffer has been revoked from the log */
+       BH_RevokeValid,         /* 1 if buffer revoked flag is valid */
+       BH_JBDDirty,            /* 1 if buffer is dirty but journaled */
+};
+
+/* Return true if the buffer is one which JBD is managing */
+static inline int buffer_jbd(struct buffer_head *bh)
+{
+       return __buffer_state(bh, JBD);
+}
+
+static inline struct buffer_head *jh2bh(struct journal_head *jh)
+{
+       return jh->b_bh;
+}
+
+static inline struct journal_head *bh2jh(struct buffer_head *bh)
+{
+       return bh->b_private;
+}
+
+struct jbd_revoke_table_s;
+
+/* The handle_t type represents a single atomic update being performed
+ * by some process.  All filesystem modifications made by the process go
+ * through this handle.  Recursive operations (such as quota operations)
+ * are gathered into a single update.
+ *
+ * The buffer credits field is used to account for journaled buffers
+ * being modified by the running process.  To ensure that there is
+ * enough log space for all outstanding operations, we need to limit the
+ * number of outstanding buffers possible at any time.  When the
+ * operation completes, any buffer credits not used are credited back to
+ * the transaction, so that at all times we know how many buffers the
+ * outstanding updates on a transaction might possibly touch. */
+
+struct handle_s 
+{
+       /* Which compound transaction is this update a part of? */
+       transaction_t         * h_transaction;
+
+       /* Number of remaining buffers we are allowed to dirty: */
+       int                     h_buffer_credits;
+
+       /* Reference count on this handle */
+       int                     h_ref;
+
+       /* Field for caller's use to track errors through large fs
+          operations */
+       int                     h_err;
+
+       /* Flags */
+       unsigned int    h_sync:         1;      /* sync-on-close */
+       unsigned int    h_jdata:        1;      /* force data journaling */
+       unsigned int    h_aborted:      1;      /* fatal error on handle */
+};
+
+
+/* The transaction_t type is the guts of the journaling mechanism.  It
+ * tracks a compound transaction through its various states:
+ *
+ * RUNNING:    accepting new updates
+ * LOCKED:     Updates still running but we don't accept new ones
+ * RUNDOWN:    Updates are tidying up but have finished requesting
+ *             new buffers to modify (state not used for now)
+ * FLUSH:       All updates complete, but we are still writing to disk
+ * COMMIT:      All data on disk, writing commit record
+ * FINISHED:   We still have to keep the transaction for checkpointing.
+ *
+ * The transaction keeps track of all of the buffers modified by a
+ * running transaction, and all of the buffers committed but not yet
+ * flushed to home for finished transactions.
+ */
+
+struct transaction_s 
+{
+       /* Pointer to the journal for this transaction. */
+       journal_t *             t_journal;
+       
+       /* Sequence number for this transaction */
+       tid_t                   t_tid;
+       
+       /* Transaction's current state */
+       enum {
+               T_RUNNING,
+               T_LOCKED,
+               T_RUNDOWN,
+               T_FLUSH,
+               T_COMMIT,
+               T_FINISHED 
+       }                       t_state;
+
+       /* Where in the log does this transaction's commit start? */
+       unsigned long           t_log_start;
+       
+       /* Doubly-linked circular list of all inodes owned by this
+           transaction */      /* AKPM: unused */
+       struct inode *          t_ilist;
+       
+       /* Number of buffers on the t_buffers list */
+       int                     t_nr_buffers;
+       
+       /* Doubly-linked circular list of all buffers reserved but not
+           yet modified by this transaction */
+       struct journal_head *   t_reserved_list;
+       
+       /* Doubly-linked circular list of all metadata buffers owned by this
+           transaction */
+       struct journal_head *   t_buffers;
+       
+       /*
+        * Doubly-linked circular list of all data buffers still to be
+        * flushed before this transaction can be committed.
+        * Protected by journal_datalist_lock.
+        */
+       struct journal_head *   t_sync_datalist;
+       
+       /*
+        * Doubly-linked circular list of all writepage data buffers
+        * still to be written before this transaction can be committed.
+        * Protected by journal_datalist_lock.
+        */
+       struct journal_head *   t_async_datalist;
+       
+       /* Doubly-linked circular list of all forget buffers (superceded
+           buffers which we can un-checkpoint once this transaction
+           commits) */
+       struct journal_head *   t_forget;
+       
+       /*
+        * Doubly-linked circular list of all buffers still to be
+        * flushed before this transaction can be checkpointed.
+        */
+       /* Protected by journal_datalist_lock */
+       struct journal_head *   t_checkpoint_list;
+       
+       /* Doubly-linked circular list of temporary buffers currently
+           undergoing IO in the log */
+       struct journal_head *   t_iobuf_list;
+       
+       /* Doubly-linked circular list of metadata buffers being
+           shadowed by log IO.  The IO buffers on the iobuf list and the
+           shadow buffers on this list match each other one for one at
+           all times. */
+       struct journal_head *   t_shadow_list;
+       
+       /* Doubly-linked circular list of control buffers being written
+           to the log. */
+       struct journal_head *   t_log_list;
+       
+       /* Number of outstanding updates running on this transaction */
+       int                     t_updates;
+
+       /* Number of buffers reserved for use by all handles in this
+        * transaction handle but not yet modified. */
+       int                     t_outstanding_credits;
+       
+       /*
+        * Forward and backward links for the circular list of all
+        * transactions awaiting checkpoint.
+        */
+       /* Protected by journal_datalist_lock */
+       transaction_t           *t_cpnext, *t_cpprev;
+
+       /* When will the transaction expire (become due for commit), in
+        * jiffies ? */
+       unsigned long           t_expires;
+
+       /* How many handles used this transaction? */
+       int t_handle_count;
+};
+
+
+/* The journal_t maintains all of the journaling state information for a
+ * single filesystem.  It is linked to from the fs superblock structure.
+ * 
+ * We use the journal_t to keep track of all outstanding transaction
+ * activity on the filesystem, and to manage the state of the log
+ * writing process. */
+
+struct journal_s
+{
+       /* General journaling state flags */
+       unsigned long           j_flags;
+
+       /* Is there an outstanding uncleared error on the journal (from
+        * a prior abort)? */
+       int                     j_errno;
+       
+       /* The superblock buffer */
+       struct buffer_head *    j_sb_buffer;
+       journal_superblock_t *  j_superblock;
+
+       /* Version of the superblock format */
+       int                     j_format_version;
+
+       /* Number of processes waiting to create a barrier lock */
+       int                     j_barrier_count;
+       
+       /* The barrier lock itself */
+       struct semaphore        j_barrier;
+       
+       /* Transactions: The current running transaction... */
+       transaction_t *         j_running_transaction;
+       
+       /* ... the transaction we are pushing to disk ... */
+       transaction_t *         j_committing_transaction;
+       
+       /* ... and a linked circular list of all transactions waiting
+        * for checkpointing. */
+       /* Protected by journal_datalist_lock */
+       transaction_t *         j_checkpoint_transactions;
+
+       /* Wait queue for waiting for a locked transaction to start
+           committing, or for a barrier lock to be released */
+       wait_queue_head_t       j_wait_transaction_locked;
+       
+       /* Wait queue for waiting for checkpointing to complete */
+       wait_queue_head_t       j_wait_logspace;
+       
+       /* Wait queue for waiting for commit to complete */
+       wait_queue_head_t       j_wait_done_commit;
+       
+       /* Wait queue to trigger checkpointing */
+       wait_queue_head_t       j_wait_checkpoint;
+       
+       /* Wait queue to trigger commit */
+       wait_queue_head_t       j_wait_commit;
+       
+       /* Wait queue to wait for updates to complete */
+       wait_queue_head_t       j_wait_updates;
+
+       /* Semaphore for locking against concurrent checkpoints */
+       struct semaphore        j_checkpoint_sem;
+
+       /* The main journal lock, used by lock_journal() */
+       struct semaphore        j_sem;
+               
+       /* Journal head: identifies the first unused block in the journal. */
+       unsigned long           j_head;
+       
+       /* Journal tail: identifies the oldest still-used block in the
+        * journal. */
+       unsigned long           j_tail;
+
+       /* Journal free: how many free blocks are there in the journal? */
+       unsigned long           j_free;
+
+       /* Journal start and end: the block numbers of the first usable
+        * block and one beyond the last usable block in the journal. */
+       unsigned long           j_first, j_last;
+
+       /* Device, blocksize and starting block offset for the location
+        * where we store the journal. */
+       kdev_t                  j_dev;
+       int                     j_blocksize;
+       unsigned int            j_blk_offset;
+
+       /* Device which holds the client fs.  For internal journal this
+        * will be equal to j_dev. */
+       kdev_t                  j_fs_dev;
+
+       /* Total maximum capacity of the journal region on disk. */
+       unsigned int            j_maxlen;
+
+       /* Optional inode where we store the journal.  If present, all
+        * journal block numbers are mapped into this inode via
+        * bmap(). */
+       struct inode *          j_inode;
+
+       /* Sequence number of the oldest transaction in the log */
+       tid_t                   j_tail_sequence;
+       /* Sequence number of the next transaction to grant */
+       tid_t                   j_transaction_sequence;
+       /* Sequence number of the most recently committed transaction */
+       tid_t                   j_commit_sequence;
+       /* Sequence number of the most recent transaction wanting commit */
+       tid_t                   j_commit_request;
+
+       /* Journal uuid: identifies the object (filesystem, LVM volume
+        * etc) backed by this journal.  This will eventually be
+        * replaced by an array of uuids, allowing us to index multiple
+        * devices within a single journal and to perform atomic updates
+        * across them.  */
+
+       __u8                    j_uuid[16];
+
+       /* Pointer to the current commit thread for this journal */
+       struct task_struct *    j_task;
+
+       /* Maximum number of metadata buffers to allow in a single
+        * compound commit transaction */
+       int                     j_max_transaction_buffers;
+
+       /* What is the maximum transaction lifetime before we begin a
+        * commit? */
+       unsigned long           j_commit_interval;
+
+       /* The timer used to wakeup the commit thread: */
+       struct timer_list *     j_commit_timer;
+       int                     j_commit_timer_active;
+
+       /* Link all journals together - system-wide */
+       struct list_head        j_all_journals;
+
+       /* The revoke table: maintains the list of revoked blocks in the
+           current transaction. */
+       struct jbd_revoke_table_s *j_revoke;
+};
+
+/* 
+ * Journal flag definitions 
+ */
+#define JFS_UNMOUNT    0x001   /* Journal thread is being destroyed */
+#define JFS_ABORT      0x002   /* Journaling has been aborted for errors. */
+#define JFS_ACK_ERR    0x004   /* The errno in the sb has been acked */
+#define JFS_FLUSHED    0x008   /* The journal superblock has been flushed */
+#define JFS_LOADED     0x010   /* The journal superblock has been loaded */
+
+/* 
+ * Function declarations for the journaling transaction and buffer
+ * management
+ */
+
+/* Filing buffers */
+extern void __journal_unfile_buffer(struct journal_head *);
+extern void journal_unfile_buffer(struct journal_head *);
+extern void __journal_refile_buffer(struct journal_head *);
+extern void journal_refile_buffer(struct journal_head *);
+extern void __journal_file_buffer(struct journal_head *, transaction_t *, int);
+extern void __journal_free_buffer(struct journal_head *bh);
+extern void journal_file_buffer(struct journal_head *, transaction_t *, int);
+extern void __journal_clean_data_list(transaction_t *transaction);
+
+/* Log buffer allocation */
+extern struct journal_head * journal_get_descriptor_buffer(journal_t *);
+extern unsigned long journal_next_log_block(journal_t *);
+
+/* Commit management */
+extern void journal_commit_transaction(journal_t *);
+
+/* Checkpoint list management */
+int __journal_clean_checkpoint_list(journal_t *journal);
+extern void journal_remove_checkpoint(struct journal_head *);
+extern void __journal_remove_checkpoint(struct journal_head *);
+extern void journal_insert_checkpoint(struct journal_head *, transaction_t *);
+extern void __journal_insert_checkpoint(struct journal_head *,transaction_t *);
+
+/* Buffer IO */
+extern int 
+journal_write_metadata_buffer(transaction_t      *transaction,
+                             struct journal_head  *jh_in,
+                             struct journal_head **jh_out,
+                             int                  blocknr);
+
+/* Transaction locking */
+extern void            __wait_on_journal (journal_t *);
+
+/*
+ * Journal locking.
+ *
+ * We need to lock the journal during transaction state changes so that
+ * nobody ever tries to take a handle on the running transaction while
+ * we are in the middle of moving it to the commit phase.  
+ *
+ * Note that the locking is completely interrupt unsafe.  We never touch
+ * journal structures from interrupts.
+ *
+ * In 2.2, the BKL was required for lock_journal.  This is no longer
+ * the case.
+ */
+
+static inline void lock_journal(journal_t *journal)
+{
+       down(&journal->j_sem);
+}
+
+/* This returns zero if we acquired the semaphore */
+static inline int try_lock_journal(journal_t * journal)
+{
+       return down_trylock(&journal->j_sem);
+}
+
+static inline void unlock_journal(journal_t * journal)
+{
+       up(&journal->j_sem);
+}
+
+
+static inline handle_t *journal_current_handle(void)
+{
+       return current->journal_info;
+}
+
+/* The journaling code user interface:
+ *
+ * Create and destroy handles
+ * Register buffer modifications against the current transaction. 
+ */
+
+extern handle_t *journal_start(journal_t *, int nblocks);
+extern handle_t *journal_try_start(journal_t *, int nblocks);
+extern int      journal_restart (handle_t *, int nblocks);
+extern int      journal_extend (handle_t *, int nblocks);
+extern int      journal_get_write_access (handle_t *, struct buffer_head *);
+extern int      journal_get_create_access (handle_t *, struct buffer_head *);
+extern int      journal_get_undo_access (handle_t *, struct buffer_head *);
+extern int      journal_dirty_data (handle_t *,
+                               struct buffer_head *, int async);
+extern int      journal_dirty_metadata (handle_t *, struct buffer_head *);
+extern void     journal_release_buffer (handle_t *, struct buffer_head *);
+extern void     journal_forget (handle_t *, struct buffer_head *);
+extern void     journal_sync_buffer (struct buffer_head *);
+extern int      journal_flushpage(journal_t *, struct page *, unsigned long);
+extern int      journal_try_to_free_buffers(journal_t *, struct page *, int);
+extern int      journal_stop(handle_t *);
+extern int      journal_flush (journal_t *);
+
+extern void     journal_lock_updates (journal_t *);
+extern void     journal_unlock_updates (journal_t *);
+
+extern journal_t * journal_init_dev(kdev_t dev, kdev_t fs_dev,
+                               int start, int len, int bsize);
+extern journal_t * journal_init_inode (struct inode *);
+extern int        journal_update_format (journal_t *);
+extern int        journal_check_used_features 
+                  (journal_t *, unsigned long, unsigned long, unsigned long);
+extern int        journal_check_available_features 
+                  (journal_t *, unsigned long, unsigned long, unsigned long);
+extern int        journal_set_features 
+                  (journal_t *, unsigned long, unsigned long, unsigned long);
+extern int        journal_create     (journal_t *);
+extern int        journal_load       (journal_t *journal);
+extern void       journal_destroy    (journal_t *);
+extern int        journal_recover    (journal_t *journal);
+extern int        journal_wipe       (journal_t *, int);
+extern int        journal_skip_recovery (journal_t *);
+extern void       journal_update_superblock (journal_t *, int);
+extern void       __journal_abort      (journal_t *);
+extern void       journal_abort      (journal_t *, int);
+extern int        journal_errno      (journal_t *);
+extern void       journal_ack_err    (journal_t *);
+extern int        journal_clear_err  (journal_t *);
+extern unsigned long journal_bmap(journal_t *journal, unsigned long blocknr);
+extern int         journal_force_commit(journal_t *journal);
+
+/*
+ * journal_head management
+ */
+extern struct journal_head
+               *journal_add_journal_head(struct buffer_head *bh);
+extern void    journal_remove_journal_head(struct buffer_head *bh);
+extern void    __journal_remove_journal_head(struct buffer_head *bh);
+extern void    journal_unlock_journal_head(struct journal_head *jh);
+
+/* Primary revoke support */
+#define JOURNAL_REVOKE_DEFAULT_HASH 256
+extern int        journal_init_revoke(journal_t *, int);
+extern void       journal_destroy_revoke_caches(void);
+extern int        journal_init_revoke_caches(void);
+
+extern void       journal_destroy_revoke(journal_t *);
+extern int        journal_revoke (handle_t *,
+                               unsigned long, struct buffer_head *);
+extern int        journal_cancel_revoke(handle_t *, struct journal_head *);
+extern void       journal_write_revoke_records(journal_t *, transaction_t *);
+
+/* Recovery revoke support */
+extern int        journal_set_revoke(journal_t *, unsigned long, tid_t);
+extern int        journal_test_revoke(journal_t *, unsigned long, tid_t);
+extern void       journal_clear_revoke(journal_t *);
+extern void       journal_brelse_array(struct buffer_head *b[], int n);
+
+/* The log thread user interface:
+ *
+ * Request space in the current transaction, and force transaction commit
+ * transitions on demand.
+ */
+
+extern int     log_space_left (journal_t *); /* Called with journal locked */
+extern tid_t   log_start_commit (journal_t *, transaction_t *);
+extern void    log_wait_commit (journal_t *, tid_t);
+extern int     log_do_checkpoint (journal_t *, int);
+
+extern void    log_wait_for_space(journal_t *, int nblocks);
+extern void    __journal_drop_transaction(journal_t *, transaction_t *);
+extern int     cleanup_journal_tail(journal_t *);
+
+/* Reduce journal memory usage by flushing */
+extern void shrink_journal_memory(void);
+
+/* Debugging code only: */
+
+#define jbd_ENOSYS() \
+do {                                                                 \
+       printk (KERN_ERR "JBD unimplemented function " __FUNCTION__); \
+       current->state = TASK_UNINTERRUPTIBLE;                        \
+       schedule();                                                   \
+} while (1)
+
+/*
+ * is_journal_abort
+ *
+ * Simple test wrapper function to test the JFS_ABORT state flag.  This
+ * bit, when set, indicates that we have had a fatal error somewhere,
+ * either inside the journaling layer or indicated to us by the client
+ * (eg. ext3), and that we and should not commit any further
+ * transactions.  
+ */
+
+static inline int is_journal_aborted(journal_t *journal)
+{
+       return journal->j_flags & JFS_ABORT;
+}
+
+static inline int is_handle_aborted(handle_t *handle)
+{
+       if (handle->h_aborted)
+               return 1;
+       return is_journal_aborted(handle->h_transaction->t_journal);
+}
+
+static inline void journal_abort_handle(handle_t *handle)
+{
+       handle->h_aborted = 1;
+}
+
+/* Not all architectures define BUG() */
+#ifndef BUG
+#define BUG() do { \
+        printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \
+       * ((char *) 0) = 0; \
+ } while (0)
+#endif /* BUG */
+
+#else
+
+extern int        journal_recover    (journal_t *journal);
+extern int        journal_skip_recovery (journal_t *);
+
+/* Primary revoke support */
+extern int        journal_init_revoke(journal_t *, int);
+extern void       journal_destroy_revoke_caches(void);
+extern int        journal_init_revoke_caches(void);
+
+/* Recovery revoke support */
+extern int        journal_set_revoke(journal_t *, unsigned long, tid_t);
+extern int        journal_test_revoke(journal_t *, unsigned long, tid_t);
+extern void       journal_clear_revoke(journal_t *);
+extern void       journal_brelse_array(struct buffer_head *b[], int n);
+
+extern void       journal_destroy_revoke(journal_t *);
+#endif /* __KERNEL__   */
+
+/* Comparison functions for transaction IDs: perform comparisons using
+ * modulo arithmetic so that they work over sequence number wraps. */
+
+static inline int tid_gt(tid_t x, tid_t y)
+{
+       int difference = (x - y);
+       return (difference > 0);
+}
+
+static inline int tid_geq(tid_t x, tid_t y)
+{
+       int difference = (x - y);
+       return (difference >= 0);
+}
+
+extern int journal_blocks_per_page(struct inode *inode);
+
+/*
+ * Definitions which augment the buffer_head layer
+ */
+
+/* journaling buffer types */
+#define BJ_None                0       /* Not journaled */
+#define BJ_SyncData    1       /* Normal data: flush before commit */
+#define BJ_AsyncData   2       /* writepage data: wait on it before commit */
+#define BJ_Metadata    3       /* Normal journaled metadata */
+#define BJ_Forget      4       /* Buffer superceded by this transaction */
+#define BJ_IO          5       /* Buffer is for temporary IO use */
+#define BJ_Shadow      6       /* Buffer contents being shadowed to the log */
+#define BJ_LogCtl      7       /* Buffer contains log descriptors */
+#define BJ_Reserved    8       /* Buffer is reserved for access by journal */
+#define BJ_Types       9
+extern int jbd_blocks_per_page(struct inode *inode);
+
+#ifdef __KERNEL__
+
+extern spinlock_t jh_splice_lock;
+/*
+ * Once `expr1' has been found true, take jh_splice_lock
+ * and then reevaluate everything.
+ */
+#define SPLICE_LOCK(expr1, expr2)                              \
+       ({                                                      \
+               int ret = (expr1);                              \
+               if (ret) {                                      \
+                       spin_lock(&jh_splice_lock);             \
+                       ret = (expr1) && (expr2);               \
+                       spin_unlock(&jh_splice_lock);           \
+               }                                               \
+               ret;                                            \
+       })
+
+/*
+ * A number of buffer state predicates.  They test for
+ * buffer_jbd() because they are used in core kernel code.
+ *
+ * These will be racy on SMP unless we're *sure* that the
+ * buffer won't be detached from the journalling system
+ * in parallel.
+ */
+
+/* Return true if the buffer is on journal list `list' */
+static inline int buffer_jlist_eq(struct buffer_head *bh, int list)
+{
+       return SPLICE_LOCK(buffer_jbd(bh), bh2jh(bh)->b_jlist == list);
+}
+
+/* Return true if this bufer is dirty wrt the journal */
+static inline int buffer_jdirty(struct buffer_head *bh)
+{
+       return buffer_jbd(bh) && __buffer_state(bh, JBDDirty);
+}
+
+/* Return true if it's a data buffer which journalling is managing */
+static inline int buffer_jbd_data(struct buffer_head *bh)
+{
+       return SPLICE_LOCK(buffer_jbd(bh),
+                       bh2jh(bh)->b_jlist == BJ_SyncData ||
+                       bh2jh(bh)->b_jlist == BJ_AsyncData);
+}
+
+#ifdef CONFIG_SMP
+#define assert_spin_locked(lock)       J_ASSERT(spin_is_locked(lock))
+#else
+#define assert_spin_locked(lock)       do {} while(0)
+#endif
+
+#define buffer_trace_init(bh)  do {} while (0)
+#define print_buffer_fields(bh)        do {} while (0)
+#define print_buffer_trace(bh) do {} while (0)
+#define BUFFER_TRACE(bh, info) do {} while (0)
+#define BUFFER_TRACE2(bh, bh2, info)   do {} while (0)
+#define JBUFFER_TRACE(jh, info)        do {} while (0)
+
+#endif /* __KERNEL__ */
+
+#endif /* CONFIG_JBD || CONFIG_JBD_MODULE || !__KERNEL__ */
+
+/*
+ * Compatibility no-ops which allow the kernel to compile without CONFIG_JBD
+ * go here.
+ */
+
+#if defined(__KERNEL__) && !(defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE))
+
+#define J_ASSERT(expr)                 do {} while (0)
+#define J_ASSERT_BH(bh, expr)          do {} while (0)
+#define buffer_jbd(bh)                 0
+#define buffer_jlist_eq(bh, val)       0
+#define journal_buffer_journal_lru(bh) 0
+
+#endif /* defined(__KERNEL__) && !defined(CONFIG_JBD) */
+#endif /* _LINUX_JBD_H */
diff --git a/e2fsprogs/ext2fs/kernel-list.h b/e2fsprogs/ext2fs/kernel-list.h
new file mode 100644 (file)
index 0000000..24e6ab4
--- /dev/null
@@ -0,0 +1,112 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = { &name, &name }
+
+#define INIT_LIST_HEAD(ptr) do { \
+       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+#if (!defined(__GNUC__) && !defined(__WATCOMC__))
+#define __inline__
+#endif
+
+/*
+ * Insert a new entry between two known consecutive entries. 
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * new,
+       struct list_head * prev,
+       struct list_head * next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+/*
+ * Insert a new entry after the specified head..
+ */
+static __inline__ void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+/*
+ * Insert a new entry at the tail
+ */
+static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+                                 struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+static __inline__ void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+}
+
+static __inline__ int list_empty(struct list_head *head)
+{
+       return head->next == head;
+}
+
+/*
+ * Splice in "list" into "head"
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+       struct list_head *first = list->next;
+
+       if (first != list) {
+               struct list_head *last = list->prev;
+               struct list_head *at = head->next;
+
+               first->prev = head;
+               head->next = first;
+
+               last->next = at;
+               at->prev = last;
+       }
+}
+
+#define list_entry(ptr, type, member) \
+       ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+#define list_for_each(pos, head) \
+        for (pos = (head)->next; pos != (head); pos = pos->next)
+
+#endif
diff --git a/e2fsprogs/ext2fs/link.c b/e2fsprogs/ext2fs/link.c
new file mode 100644 (file)
index 0000000..5e0f4f3
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * link.c --- create links in a ext2fs directory
+ * 
+ * Copyright (C) 1993, 1994 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct link_struct  {
+       const char      *name;
+       int             namelen;
+       ext2_ino_t      inode;
+       int             flags;
+       int             done;
+       struct ext2_super_block *sb;
+};     
+
+static int link_proc(struct ext2_dir_entry *dirent,
+                    int        offset,
+                    int        blocksize,
+                    char       *buf,
+                    void       *priv_data)
+{
+       struct link_struct *ls = (struct link_struct *) priv_data;
+       struct ext2_dir_entry *next;
+       int rec_len, min_rec_len;
+       int ret = 0;
+
+       rec_len = EXT2_DIR_REC_LEN(ls->namelen);
+
+       /*
+        * See if the following directory entry (if any) is unused;
+        * if so, absorb it into this one.
+        */
+       next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len);
+       if ((offset + dirent->rec_len < blocksize - 8) &&
+           (next->inode == 0) &&
+           (offset + dirent->rec_len + next->rec_len <= blocksize)) {
+               dirent->rec_len += next->rec_len;
+               ret = DIRENT_CHANGED;
+       }
+
+       /*
+        * If the directory entry is used, see if we can split the
+        * directory entry to make room for the new name.  If so,
+        * truncate it and return.
+        */
+       if (dirent->inode) {
+               min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
+               if (dirent->rec_len < (min_rec_len + rec_len))
+                       return ret;
+               rec_len = dirent->rec_len - min_rec_len;
+               dirent->rec_len = min_rec_len;
+               next = (struct ext2_dir_entry *) (buf + offset +
+                                                 dirent->rec_len);
+               next->inode = 0;
+               next->name_len = 0;
+               next->rec_len = rec_len;
+               return DIRENT_CHANGED;
+       }
+
+       /*
+        * If we get this far, then the directory entry is not used.
+        * See if we can fit the request entry in.  If so, do it.
+        */
+       if (dirent->rec_len < rec_len)
+               return ret;
+       dirent->inode = ls->inode;
+       dirent->name_len = ls->namelen;
+       strncpy(dirent->name, ls->name, ls->namelen);
+       if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
+               dirent->name_len |= (ls->flags & 0x7) << 8;
+
+       ls->done++;
+       return DIRENT_ABORT|DIRENT_CHANGED;
+}
+
+/*
+ * Note: the low 3 bits of the flags field are used as the directory
+ * entry filetype.
+ */
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, 
+                     ext2_ino_t ino, int flags)
+{
+       errcode_t               retval;
+       struct link_struct      ls;
+       struct ext2_inode       inode;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (!(fs->flags & EXT2_FLAG_RW))
+               return EXT2_ET_RO_FILSYS;
+
+       ls.name = name;
+       ls.namelen = name ? strlen(name) : 0;
+       ls.inode = ino;
+       ls.flags = flags;
+       ls.done = 0;
+       ls.sb = fs->super;
+
+       retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
+                                   0, link_proc, &ls);
+       if (retval)
+               return retval;
+
+       if (!ls.done)
+               return EXT2_ET_DIR_NO_SPACE;
+
+       if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
+               return retval;
+
+       if (inode.i_flags & EXT2_INDEX_FL) {
+               inode.i_flags &= ~EXT2_INDEX_FL;
+               if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0)
+                       return retval;
+       }
+
+       return 0;
+}
diff --git a/e2fsprogs/ext2fs/llseek.c b/e2fsprogs/ext2fs/llseek.c
new file mode 100644 (file)
index 0000000..e0bedb5
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * llseek.c -- stub calling the llseek system call
+ *
+ * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef __MSDOS__
+#include <io.h>
+#endif
+#include "et/com_err.h"
+#include "ext2fs/ext2_io.h"
+
+#ifdef __linux__
+
+#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
+
+#define my_llseek lseek64
+
+#else
+#if defined(HAVE_LLSEEK)
+#include <syscall.h>
+
+#ifndef HAVE_LLSEEK_PROTOTYPE
+extern long long llseek (int fd, long long offset, int origin);
+#endif
+
+#define my_llseek llseek
+
+#else  /* ! HAVE_LLSEEK */
+
+#if defined(__alpha__) || defined (__ia64__)
+
+#define llseek lseek
+
+#else /* !__alpha__ && !__ia64__*/
+
+#include <linux/unistd.h>
+
+#ifndef __NR__llseek
+#define __NR__llseek            140
+#endif
+
+#ifndef __i386__
+static int _llseek (unsigned int, unsigned long,
+                  unsigned long, ext2_loff_t *, unsigned int);
+
+static _syscall5(int,_llseek,unsigned int,fd,unsigned long,offset_high,
+                unsigned long, offset_low,ext2_loff_t *,result,
+                unsigned int, origin)
+#endif
+
+static ext2_loff_t my_llseek (int fd, ext2_loff_t offset, int origin)
+{
+       ext2_loff_t result;
+       int retval;
+
+#ifndef __i386__
+       retval = _llseek(fd, ((unsigned long long) offset) >> 32,
+#else                    
+       retval = syscall(__NR__llseek, fd, (unsigned long long) (offset >> 32),
+#endif
+                         ((unsigned long long) offset) & 0xffffffff,
+                       &result, origin);
+       return (retval == -1 ? (ext2_loff_t) retval : result);
+}
+
+#endif /* __alpha__ || __ia64__ */
+
+#endif /* HAVE_LLSEEK */
+#endif /* defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) */
+
+ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin)
+{
+       ext2_loff_t result;
+       static int do_compat = 0;
+
+       if ((sizeof(off_t) >= sizeof(ext2_loff_t)) ||
+           (offset < ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1))))
+               return lseek(fd, (off_t) offset, origin);
+
+       if (do_compat) {
+               errno = EINVAL;
+               return -1;
+       }
+       
+       result = my_llseek (fd, offset, origin);
+       if (result == -1 && errno == ENOSYS) {
+               /*
+                * Just in case this code runs on top of an old kernel
+                * which does not support the llseek system call
+                */
+               do_compat++;
+               errno = EINVAL;
+       }
+       return result;
+}
+
+#else /* !linux */
+
+#ifndef EINVAL
+#define EINVAL EXT2_ET_INVALID_ARGUMENT
+#endif
+
+ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin)
+{
+#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
+       return lseek64 (fd, offset, origin);
+#else
+       if ((sizeof(off_t) < sizeof(ext2_loff_t)) &&
+           (offset >= ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) {
+               errno = EINVAL;
+               return -1;
+       }
+       return lseek (fd, (off_t) offset, origin);
+#endif
+}
+
+#endif         /* linux */
+
+
diff --git a/e2fsprogs/ext2fs/lookup.c b/e2fsprogs/ext2fs/lookup.c
new file mode 100644 (file)
index 0000000..1745f33
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * lookup.c --- ext2fs directory lookup operations
+ * 
+ * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct lookup_struct  {
+       const char      *name;
+       int             len;
+       ext2_ino_t      *inode;
+       int             found;
+};     
+
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+static int lookup_proc(struct ext2_dir_entry *dirent,
+                      int      offset EXT2FS_ATTR((unused)),
+                      int      blocksize EXT2FS_ATTR((unused)),
+                      char     *buf EXT2FS_ATTR((unused)),
+                      void     *priv_data)
+{
+       struct lookup_struct *ls = (struct lookup_struct *) priv_data;
+
+       if (ls->len != (dirent->name_len & 0xFF))
+               return 0;
+       if (strncmp(ls->name, dirent->name, (dirent->name_len & 0xFF)))
+               return 0;
+       *ls->inode = dirent->inode;
+       ls->found++;
+       return DIRENT_ABORT;
+}
+
+
+errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name,
+                       int namelen, char *buf, ext2_ino_t *inode)
+{
+       errcode_t       retval;
+       struct lookup_struct ls;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       ls.name = name;
+       ls.len = namelen;
+       ls.inode = inode;
+       ls.found = 0;
+
+       retval = ext2fs_dir_iterate(fs, dir, 0, buf, lookup_proc, &ls);
+       if (retval)
+               return retval;
+
+       return (ls.found) ? 0 : EXT2_ET_FILE_NOT_FOUND;
+}
+
+
diff --git a/e2fsprogs/ext2fs/mkdir.c b/e2fsprogs/ext2fs/mkdir.c
new file mode 100644 (file)
index 0000000..81e7aea
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * mkdir.c --- make a directory in the filesystem
+ * 
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef EXT2_FT_DIR
+#define EXT2_FT_DIR            2
+#endif
+
+errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum,
+                      const char *name)
+{
+       errcode_t               retval;
+       struct ext2_inode       parent_inode, inode;
+       ext2_ino_t              ino = inum;
+       ext2_ino_t              scratch_ino;
+       blk_t                   blk;
+       char                    *block = 0;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       /*
+        * Allocate an inode, if necessary
+        */
+       if (!ino) {
+               retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755,
+                                         0, &ino);
+               if (retval)
+                       goto cleanup;
+       }
+
+       /*
+        * Allocate a data block for the directory
+        */
+       retval = ext2fs_new_block(fs, 0, 0, &blk);
+       if (retval)
+               goto cleanup;
+
+       /*
+        * Create a scratch template for the directory
+        */
+       retval = ext2fs_new_dir_block(fs, ino, parent, &block);
+       if (retval)
+               goto cleanup;
+
+       /*
+        * Get the parent's inode, if necessary
+        */
+       if (parent != ino) {
+               retval = ext2fs_read_inode(fs, parent, &parent_inode);
+               if (retval)
+                       goto cleanup;
+       } else
+               memset(&parent_inode, 0, sizeof(parent_inode));
+
+       /*
+        * Create the inode structure....
+        */
+       memset(&inode, 0, sizeof(struct ext2_inode));
+       inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask);
+       inode.i_uid = inode.i_gid = 0;
+       inode.i_blocks = fs->blocksize / 512;
+       inode.i_block[0] = blk;
+       inode.i_links_count = 2;
+       inode.i_ctime = inode.i_atime = inode.i_mtime = time(NULL);
+       inode.i_size = fs->blocksize;
+
+       /*
+        * Write out the inode and inode data block
+        */
+       retval = ext2fs_write_dir_block(fs, blk, block);
+       if (retval)
+               goto cleanup;
+       retval = ext2fs_write_new_inode(fs, ino, &inode); 
+       if (retval)
+               goto cleanup;
+
+       /*
+        * Link the directory into the filesystem hierarchy
+        */
+       if (name) {
+               retval = ext2fs_lookup(fs, parent, name, strlen(name), 0,
+                                      &scratch_ino);
+               if (!retval) {
+                       retval = EXT2_ET_DIR_EXISTS;
+                       name = 0;
+                       goto cleanup;
+               }
+               if (retval != EXT2_ET_FILE_NOT_FOUND)
+                       goto cleanup;
+               retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR);
+               if (retval)
+                       goto cleanup;
+       }
+
+       /*
+        * Update parent inode's counts
+        */
+       if (parent != ino) {
+               parent_inode.i_links_count++;
+               retval = ext2fs_write_inode(fs, parent, &parent_inode);
+               if (retval)
+                       goto cleanup;
+       }
+       
+       /*
+        * Update accounting....
+        */
+       ext2fs_block_alloc_stats(fs, blk, +1);
+       ext2fs_inode_alloc_stats2(fs, ino, +1, 1);
+
+cleanup:
+       if (block)
+               ext2fs_free_mem(&block);
+       return retval;
+
+}
+
+
diff --git a/e2fsprogs/ext2fs/mkjournal.c b/e2fsprogs/ext2fs/mkjournal.c
new file mode 100644 (file)
index 0000000..427a08e
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * mkjournal.c --- make a journal for a filesystem
+ *
+ * Copyright (C) 2000 Theodore Ts'o.
+ * 
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include "ext2_fs.h"
+#include "e2p/e2p.h"
+#include "ext2fs.h"
+#include "jfs_user.h"
+
+/*
+ * This function automatically sets up the journal superblock and
+ * returns it as an allocated block.
+ */
+errcode_t ext2fs_create_journal_superblock(ext2_filsys fs,
+                                          __u32 size, int flags,
+                                          char  **ret_jsb)
+{
+       errcode_t               retval;
+       journal_superblock_t    *jsb;
+
+       if (size < 1024)
+               return EXT2_ET_JOURNAL_TOO_SMALL;
+
+       if ((retval = ext2fs_get_mem(fs->blocksize, &jsb)))
+               return retval;
+
+       memset (jsb, 0, fs->blocksize);
+
+       jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER);
+       if (flags & EXT2_MKJOURNAL_V1_SUPER)
+               jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1);
+       else
+               jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2);
+       jsb->s_blocksize = htonl(fs->blocksize);
+       jsb->s_maxlen = htonl(size);
+       jsb->s_nr_users = htonl(1);
+       jsb->s_first = htonl(1);
+       jsb->s_sequence = htonl(1);
+       memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid));
+       /*
+        * If we're creating an external journal device, we need to
+        * adjust these fields.
+        */
+       if (fs->super->s_feature_incompat &
+           EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
+               jsb->s_nr_users = 0;
+               if (fs->blocksize == 1024)
+                       jsb->s_first = htonl(3);
+               else
+                       jsb->s_first = htonl(2);
+       }
+
+       *ret_jsb = (char *) jsb;
+       return 0;
+}
+
+/*
+ * This function writes a journal using POSIX routines.  It is used
+ * for creating external journals and creating journals on live
+ * filesystems.
+ */
+static errcode_t write_journal_file(ext2_filsys fs, char *filename,
+                                   blk_t size, int flags)
+{
+       errcode_t       retval;
+       char            *buf = 0;
+       int             fd, ret_size;
+       blk_t           i;
+
+       if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf)))
+               return retval;
+
+       /* Open the device or journal file */
+       if ((fd = open(filename, O_WRONLY)) < 0) {
+               retval = errno;
+               goto errout;
+       }
+
+       /* Write the superblock out */
+       retval = EXT2_ET_SHORT_WRITE;
+       ret_size = write(fd, buf, fs->blocksize);
+       if (ret_size < 0) {
+               retval = errno;
+               goto errout;
+       }
+       if (ret_size != (int) fs->blocksize)
+               goto errout;
+       memset(buf, 0, fs->blocksize);
+
+       for (i = 1; i < size; i++) {
+               ret_size = write(fd, buf, fs->blocksize);
+               if (ret_size < 0) {
+                       retval = errno;
+                       goto errout;
+               }
+               if (ret_size != (int) fs->blocksize)
+                       goto errout;
+       }
+       close(fd);
+
+       retval = 0;
+errout:
+       ext2fs_free_mem(&buf);
+       return retval;
+}
+
+/*
+ * Helper function for creating the journal using direct I/O routines
+ */
+struct mkjournal_struct {
+       int             num_blocks;
+       int             newblocks;
+       char            *buf;
+       errcode_t       err;
+};
+
+static int mkjournal_proc(ext2_filsys  fs,
+                          blk_t        *blocknr,
+                          e2_blkcnt_t  blockcnt,
+                          blk_t        ref_block EXT2FS_ATTR((unused)),
+                          int          ref_offset EXT2FS_ATTR((unused)),
+                          void         *priv_data)
+{
+       struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data;
+       blk_t   new_blk;
+       static blk_t    last_blk = 0;
+       errcode_t       retval;
+       
+       if (*blocknr) {
+               last_blk = *blocknr;
+               return 0;
+       }
+       retval = ext2fs_new_block(fs, last_blk, 0, &new_blk);
+       if (retval) {
+               es->err = retval;
+               return BLOCK_ABORT;
+       }
+       if (blockcnt > 0)
+               es->num_blocks--;
+
+       es->newblocks++;
+       retval = io_channel_write_blk(fs->io, new_blk, 1, es->buf);
+
+       if (blockcnt == 0)
+               memset(es->buf, 0, fs->blocksize);
+
+       if (retval) {
+               es->err = retval;
+               return BLOCK_ABORT;
+       }
+       *blocknr = new_blk;
+       last_blk = new_blk;
+       ext2fs_block_alloc_stats(fs, new_blk, +1);
+
+       if (es->num_blocks == 0)
+               return (BLOCK_CHANGED | BLOCK_ABORT);
+       else
+               return BLOCK_CHANGED;
+       
+}
+
+/*
+ * This function creates a journal using direct I/O routines.
+ */
+static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
+                                    blk_t size, int flags)
+{
+       char                    *buf;
+       errcode_t               retval;
+       struct ext2_inode       inode;
+       struct mkjournal_struct es;
+
+       if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf)))
+               return retval;
+       
+       if ((retval = ext2fs_read_bitmaps(fs)))
+               return retval;
+
+       if ((retval = ext2fs_read_inode(fs, journal_ino, &inode)))
+               return retval;
+
+       if (inode.i_blocks > 0)
+               return EEXIST;
+
+       es.num_blocks = size;
+       es.newblocks = 0;
+       es.buf = buf;
+       es.err = 0;
+
+       retval = ext2fs_block_iterate2(fs, journal_ino, BLOCK_FLAG_APPEND,
+                                      0, mkjournal_proc, &es);
+       if (es.err) {
+               retval = es.err;
+               goto errout;
+       }
+
+       if ((retval = ext2fs_read_inode(fs, journal_ino, &inode)))
+               goto errout;
+
+       inode.i_size += fs->blocksize * size;
+       inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
+       inode.i_mtime = inode.i_ctime = time(0);
+       inode.i_links_count = 1;
+       inode.i_mode = LINUX_S_IFREG | 0600;
+
+       if ((retval = ext2fs_write_inode(fs, journal_ino, &inode)))
+               goto errout;
+       retval = 0;
+
+       memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4);
+       fs->super->s_jnl_blocks[16] = inode.i_size;
+       fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS;
+       ext2fs_mark_super_dirty(fs);
+
+errout:
+       ext2fs_free_mem(&buf);
+       return retval;
+}
+
+/*
+ * This function adds a journal device to a filesystem
+ */
+errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev)
+{
+       struct stat     st;
+       errcode_t       retval;
+       char            buf[1024];
+       journal_superblock_t    *jsb;
+       int             start;
+       __u32           i, nr_users;
+
+       /* Make sure the device exists and is a block device */
+       if (stat(journal_dev->device_name, &st) < 0)
+               return errno;
+       
+       if (!S_ISBLK(st.st_mode))
+               return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */
+
+       /* Get the journal superblock */
+       start = 1;
+       if (journal_dev->blocksize == 1024)
+               start++;
+       if ((retval = io_channel_read_blk(journal_dev->io, start, -1024, buf)))
+               return retval;
+
+       jsb = (journal_superblock_t *) buf;
+       if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) ||
+           (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2)))
+               return EXT2_ET_NO_JOURNAL_SB;
+
+       if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize)
+               return EXT2_ET_UNEXPECTED_BLOCK_SIZE;
+
+       /* Check and see if this filesystem has already been added */
+       nr_users = ntohl(jsb->s_nr_users);
+       for (i=0; i < nr_users; i++) {
+               if (memcmp(fs->super->s_uuid,
+                          &jsb->s_users[i*16], 16) == 0)
+                       break;
+       }
+       if (i >= nr_users) {
+               memcpy(&jsb->s_users[nr_users*16],
+                      fs->super->s_uuid, 16);
+               jsb->s_nr_users = htonl(nr_users+1);
+       }
+
+       /* Writeback the journal superblock */
+       if ((retval = io_channel_write_blk(journal_dev->io, start, -1024, buf)))
+               return retval;
+       
+       fs->super->s_journal_inum = 0;
+       fs->super->s_journal_dev = st.st_rdev;
+       memcpy(fs->super->s_journal_uuid, jsb->s_uuid,
+              sizeof(fs->super->s_journal_uuid));
+       fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+       ext2fs_mark_super_dirty(fs);
+       return 0;
+}
+
+/*
+ * This function adds a journal inode to a filesystem, using either
+ * POSIX routines if the filesystem is mounted, or using direct I/O
+ * functions if it is not.
+ */
+errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags)
+{
+       errcode_t               retval;
+       ext2_ino_t              journal_ino;
+       struct stat             st;
+       char                    jfile[1024];
+       int                     fd, mount_flags, f;
+
+       if ((retval = ext2fs_check_mount_point(fs->device_name, &mount_flags,
+                                              jfile, sizeof(jfile)-10)))
+               return retval;
+
+       if (mount_flags & EXT2_MF_MOUNTED) {
+               strcat(jfile, "/.journal");
+
+               /*
+                * If .../.journal already exists, make sure any 
+                * immutable or append-only flags are cleared.
+                */
+#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
+               (void) chflags (jfile, 0);
+#else
+#if HAVE_EXT2_IOCTLS
+               fd = open(jfile, O_RDONLY);
+               if (fd >= 0) {
+                       f = 0;
+                       ioctl(fd, EXT2_IOC_SETFLAGS, &f);
+                       close(fd);
+               }
+#endif
+#endif
+
+               /* Create the journal file */
+               if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0)
+                       return errno;
+
+               if ((retval = write_journal_file(fs, jfile, size, flags)))
+                       goto errout;
+               
+               /* Get inode number of the journal file */
+               if (fstat(fd, &st) < 0)
+                       goto errout;
+
+#if defined(HAVE_CHFLAGS) && defined(UF_NODUMP)
+               retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE);
+#else
+#if HAVE_EXT2_IOCTLS
+               f = EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL;
+               retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f);
+#endif
+#endif
+               if (retval)
+                       goto errout;
+               
+               close(fd);
+               journal_ino = st.st_ino;
+       } else {
+               journal_ino = EXT2_JOURNAL_INO;
+               if ((retval = write_journal_inode(fs, journal_ino,
+                                                 size, flags)))
+                       return retval;
+       }
+       
+       fs->super->s_journal_inum = journal_ino;
+       fs->super->s_journal_dev = 0;
+       memset(fs->super->s_journal_uuid, 0,
+              sizeof(fs->super->s_journal_uuid));
+       fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL;
+
+       ext2fs_mark_super_dirty(fs);
+       return 0;
+errout:
+       close(fd);
+       return retval;
+}
+
+#ifdef DEBUG
+main(int argc, char **argv)
+{
+       errcode_t       retval;
+       char            *device_name;
+       ext2_filsys     fs;
+
+       if (argc < 2) {
+               fprintf(stderr, "Usage: %s filesystem\n", argv[0]);
+               exit(1);
+       }
+       device_name = argv[1];
+       
+       retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0,
+                             unix_io_manager, &fs);
+       if (retval) {
+               com_err(argv[0], retval, "while opening %s", device_name);
+               exit(1);
+       }
+
+       retval = ext2fs_add_journal_inode(fs, 1024);
+       if (retval) {
+               com_err(argv[0], retval, "while adding journal to %s",
+                       device_name);
+               exit(1);
+       }
+       retval = ext2fs_flush(fs);
+       if (retval) {
+               printf("Warning, had trouble writing out superblocks.\n");
+       }
+       ext2fs_close(fs);
+       exit(0);
+       
+}
+#endif
diff --git a/e2fsprogs/ext2fs/namei.c b/e2fsprogs/ext2fs/namei.c
new file mode 100644 (file)
index 0000000..13d13ad
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * namei.c --- ext2fs directory lookup operations
+ * 
+ * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* #define NAMEI_DEBUG */
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base,
+                           const char *pathname, size_t pathlen, int follow,
+                           int link_count, char *buf, ext2_ino_t *res_inode);
+
+static errcode_t follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir,
+                            ext2_ino_t inode, int link_count,
+                            char *buf, ext2_ino_t *res_inode)
+{
+       char *pathname;
+       char *buffer = 0;
+       errcode_t retval;
+       struct ext2_inode ei;
+
+#ifdef NAMEI_DEBUG
+       printf("follow_link: root=%lu, dir=%lu, inode=%lu, lc=%d\n",
+              root, dir, inode, link_count);
+       
+#endif
+       retval = ext2fs_read_inode (fs, inode, &ei);
+       if (retval) return retval;
+       if (!LINUX_S_ISLNK (ei.i_mode)) {
+               *res_inode = inode;
+               return 0;
+       }
+       if (link_count++ > 5) {
+               return EXT2_ET_SYMLINK_LOOP;
+       }
+       if (ext2fs_inode_data_blocks(fs,&ei)) {
+               retval = ext2fs_get_mem(fs->blocksize, &buffer);
+               if (retval)
+                       return retval;
+               retval = io_channel_read_blk(fs->io, ei.i_block[0], 1, buffer);
+               if (retval) {
+                       ext2fs_free_mem(&buffer);
+                       return retval;
+               }
+               pathname = buffer;
+       } else
+               pathname = (char *)&(ei.i_block[0]);
+       retval = open_namei(fs, root, dir, pathname, ei.i_size, 1,
+                           link_count, buf, res_inode);
+       if (buffer)
+               ext2fs_free_mem(&buffer);
+       return retval;
+}
+
+/*
+ * This routine interprets a pathname in the context of the current
+ * directory and the root directory, and returns the inode of the
+ * containing directory, and a pointer to the filename of the file
+ * (pointing into the pathname) and the length of the filename.
+ */
+static errcode_t dir_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir,
+                          const char *pathname, int pathlen,
+                          int link_count, char *buf,
+                          const char **name, int *namelen,
+                          ext2_ino_t *res_inode)
+{
+       char c;
+       const char *thisname;
+       int len;
+       ext2_ino_t inode;
+       errcode_t retval;
+
+       if ((c = *pathname) == '/') {
+               dir = root;
+               pathname++;
+               pathlen--;
+       }
+       while (1) {
+               thisname = pathname;
+               for (len=0; --pathlen >= 0;len++) {
+                       c = *(pathname++);
+                       if (c == '/')
+                               break;
+               }
+               if (pathlen < 0)
+                       break;
+               retval = ext2fs_lookup (fs, dir, thisname, len, buf, &inode);
+               if (retval) return retval;
+               retval = follow_link (fs, root, dir, inode,
+                                     link_count, buf, &dir);
+               if (retval) return retval;
+       }
+       *name = thisname;
+       *namelen = len;
+       *res_inode = dir;
+       return 0;
+}
+
+static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base,
+                           const char *pathname, size_t pathlen, int follow,
+                           int link_count, char *buf, ext2_ino_t *res_inode)
+{
+       const char *basename;
+       int namelen;
+       ext2_ino_t dir, inode;
+       errcode_t retval;
+
+#ifdef NAMEI_DEBUG
+       printf("open_namei: root=%lu, dir=%lu, path=%*s, lc=%d\n",
+              root, base, pathlen, pathname, link_count);
+#endif
+       retval = dir_namei(fs, root, base, pathname, pathlen,
+                          link_count, buf, &basename, &namelen, &dir);
+       if (retval) return retval;
+       if (!namelen) {                     /* special case: '/usr/' etc */
+               *res_inode=dir;
+               return 0;
+       }
+       retval = ext2fs_lookup (fs, dir, basename, namelen, buf, &inode);
+       if (retval)
+               return retval;
+       if (follow) {
+               retval = follow_link(fs, root, dir, inode, link_count,
+                                    buf, &inode);
+               if (retval)
+                       return retval;
+       }
+#ifdef NAMEI_DEBUG
+       printf("open_namei: (link_count=%d) returns %lu\n",
+              link_count, inode);
+#endif
+       *res_inode = inode;
+       return 0;
+}
+
+errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+                      const char *name, ext2_ino_t *inode)
+{
+       char *buf;
+       errcode_t retval;
+       
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       retval = ext2fs_get_mem(fs->blocksize, &buf);
+       if (retval)
+               return retval;
+       
+       retval = open_namei(fs, root, cwd, name, strlen(name), 0, 0,
+                           buf, inode);
+
+       ext2fs_free_mem(&buf);
+       return retval;
+}
+
+errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+                             const char *name, ext2_ino_t *inode)
+{
+       char *buf;
+       errcode_t retval;
+       
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       retval = ext2fs_get_mem(fs->blocksize, &buf);
+       if (retval)
+               return retval;
+       
+       retval = open_namei(fs, root, cwd, name, strlen(name), 1, 0,
+                           buf, inode);
+
+       ext2fs_free_mem(&buf);
+       return retval;
+}
+
+errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd,
+                       ext2_ino_t inode, ext2_ino_t *res_inode)
+{
+       char *buf;
+       errcode_t retval;
+       
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       retval = ext2fs_get_mem(fs->blocksize, &buf);
+       if (retval)
+               return retval;
+
+       retval = follow_link(fs, root, cwd, inode, 0, buf, res_inode);
+
+       ext2fs_free_mem(&buf);
+       return retval;
+}
+
diff --git a/e2fsprogs/ext2fs/native.c b/e2fsprogs/ext2fs/native.c
new file mode 100644 (file)
index 0000000..85d0989
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * native.c --- returns the ext2_flag for a native byte order
+ * 
+ * Copyright (C) 1996 Theodore Ts'o.
+ * 
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+int ext2fs_native_flag(void)
+{
+#ifdef WORDS_BIGENDIAN
+       return EXT2_FLAG_SWAP_BYTES;
+#else
+       return 0;
+#endif
+}
+
+       
+       
diff --git a/e2fsprogs/ext2fs/newdir.c b/e2fsprogs/ext2fs/newdir.c
new file mode 100644 (file)
index 0000000..3904d91
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * newdir.c --- create a new directory block
+ * 
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+#ifndef EXT2_FT_DIR
+#define EXT2_FT_DIR            2
+#endif
+
+/*
+ * Create new directory block
+ */
+errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino,
+                              ext2_ino_t parent_ino, char **block)
+{
+       struct ext2_dir_entry   *dir = NULL;
+       errcode_t               retval;
+       char                    *buf;
+       int                     rec_len;
+       int                     filetype = 0;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       retval = ext2fs_get_mem(fs->blocksize, &buf);
+       if (retval)
+               return retval;
+       memset(buf, 0, fs->blocksize);
+       dir = (struct ext2_dir_entry *) buf;
+       dir->rec_len = fs->blocksize;
+
+       if (dir_ino) {
+               if (fs->super->s_feature_incompat &
+                   EXT2_FEATURE_INCOMPAT_FILETYPE)
+                       filetype = EXT2_FT_DIR << 8;
+               /*
+                * Set up entry for '.'
+                */
+               dir->inode = dir_ino;
+               dir->name_len = 1 | filetype;
+               dir->name[0] = '.';
+               rec_len = dir->rec_len - EXT2_DIR_REC_LEN(1);
+               dir->rec_len = EXT2_DIR_REC_LEN(1);
+
+               /*
+                * Set up entry for '..'
+                */
+               dir = (struct ext2_dir_entry *) (buf + dir->rec_len);
+               dir->rec_len = rec_len;
+               dir->inode = parent_ino;
+               dir->name_len = 2 | filetype;
+               dir->name[0] = '.';
+               dir->name[1] = '.';
+               
+       }
+       *block = buf;
+       return 0;
+}
diff --git a/e2fsprogs/ext2fs/openfs.c b/e2fsprogs/ext2fs/openfs.c
new file mode 100644 (file)
index 0000000..d27c1b9
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * openfs.c --- open an ext2 filesystem
+ * 
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ * 
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+
+
+#include "ext2fs.h"
+#include "e2image.h"
+
+blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i)
+{
+       int     bg;
+       int     has_super = 0;
+       int     ret_blk;
+
+       if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) ||
+           (i < fs->super->s_first_meta_bg))
+               return (group_block + i + 1);
+
+       bg = (fs->blocksize / sizeof (struct ext2_group_desc)) * i;
+       if (ext2fs_bg_has_super(fs, bg))
+               has_super = 1;
+       ret_blk = (fs->super->s_first_data_block + has_super + 
+                  (bg * fs->super->s_blocks_per_group));
+       /*
+        * If group_block is not the normal value, we're trying to use
+        * the backup group descriptors and superblock --- so use the
+        * alternate location of the second block group in the
+        * metablock group.  Ideally we should be testing each bg
+        * descriptor block individually for correctness, but we don't
+        * have the infrastructure in place to do that.
+        */
+       if (group_block != fs->super->s_first_data_block &&
+           ((ret_blk + fs->super->s_blocks_per_group) <
+            fs->super->s_blocks_count))
+               ret_blk += fs->super->s_blocks_per_group;
+       return ret_blk;
+}
+
+errcode_t ext2fs_open(const char *name, int flags, int superblock,
+                     unsigned int block_size, io_manager manager, 
+                     ext2_filsys *ret_fs)
+{
+       return ext2fs_open2(name, 0, flags, superblock, block_size, 
+                           manager, ret_fs);
+}
+
+/*
+ *  Note: if superblock is non-zero, block-size must also be non-zero.
+ *     Superblock and block_size can be zero to use the default size.
+ *
+ * Valid flags for ext2fs_open()
+ * 
+ *     EXT2_FLAG_RW    - Open the filesystem for read/write.
+ *     EXT2_FLAG_FORCE - Open the filesystem even if some of the
+ *                             features aren't supported.
+ *     EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device
+ */
+errcode_t ext2fs_open2(const char *name, const char *io_options,
+                      int flags, int superblock,
+                      unsigned int block_size, io_manager manager, 
+                      ext2_filsys *ret_fs)
+{
+       ext2_filsys     fs;
+       errcode_t       retval;
+       unsigned long   i;
+       int             j, groups_per_block, blocks_per_group;
+       blk_t           group_block, blk;
+       char            *dest, *cp;
+       struct ext2_group_desc *gdp;
+       
+       EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER);
+
+       retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs);
+       if (retval)
+               return retval;
+       
+       memset(fs, 0, sizeof(struct struct_ext2_filsys));
+       fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS;
+       fs->flags = flags;
+       fs->umask = 022;
+       retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name);
+       if (retval)
+               goto cleanup;
+       strcpy(fs->device_name, name);
+       cp = strchr(fs->device_name, '?');
+       if (!io_options && cp) {
+               *cp++ = 0;
+               io_options = cp;
+       }
+               
+       retval = manager->open(fs->device_name, 
+                              (flags & EXT2_FLAG_RW) ? IO_FLAG_RW : 0,
+                              &fs->io);
+       if (retval)
+               goto cleanup;
+       if (io_options && 
+           (retval = io_channel_set_options(fs->io, io_options)))
+               goto cleanup;
+       fs->image_io = fs->io;
+       fs->io->app_data = fs;
+       retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super);
+       if (retval)
+               goto cleanup;
+       if (flags & EXT2_FLAG_IMAGE_FILE) {
+               retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr),
+                                       &fs->image_header);
+               if (retval)
+                       goto cleanup;
+               retval = io_channel_read_blk(fs->io, 0,
+                                            -(int)sizeof(struct ext2_image_hdr),
+                                            fs->image_header);
+               if (retval)
+                       goto cleanup;
+               if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE)
+                       return EXT2_ET_MAGIC_E2IMAGE;
+               superblock = 1;
+               block_size = fs->image_header->fs_blocksize;
+       }
+
+       /*
+        * If the user specifies a specific block # for the
+        * superblock, then he/she must also specify the block size!
+        * Otherwise, read the master superblock located at offset
+        * SUPERBLOCK_OFFSET from the start of the partition.
+        *
+        * Note: we only save a backup copy of the superblock if we
+        * are reading the superblock from the primary superblock location.
+        */
+       if (superblock) {
+               if (!block_size) {
+                       retval = EXT2_ET_INVALID_ARGUMENT;
+                       goto cleanup;
+               }
+               io_channel_set_blksize(fs->io, block_size);
+               group_block = superblock;
+               fs->orig_super = 0;
+       } else {
+               io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET);
+               superblock = 1;
+               group_block = 0;
+               retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super);
+               if (retval)
+                       goto cleanup;
+       }
+       retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE,
+                                    fs->super);
+       if (retval)
+               goto cleanup;
+       if (fs->orig_super)
+               memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE);
+
+#ifdef EXT2FS_ENABLE_SWAPFS
+       if ((fs->super->s_magic == ext2fs_swab16(EXT2_SUPER_MAGIC)) ||
+           (fs->flags & EXT2_FLAG_SWAP_BYTES)) {
+               fs->flags |= EXT2_FLAG_SWAP_BYTES;
+
+               ext2fs_swap_super(fs->super);
+       }
+#endif
+       
+       if (fs->super->s_magic != EXT2_SUPER_MAGIC) {
+               retval = EXT2_ET_BAD_MAGIC;
+               goto cleanup;
+       }
+       if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) {
+               retval = EXT2_ET_REV_TOO_HIGH;
+               goto cleanup;
+       }
+
+       /*
+        * Check for feature set incompatibility
+        */
+       if (!(flags & EXT2_FLAG_FORCE)) {
+               if (fs->super->s_feature_incompat &
+                   ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) {
+                       retval = EXT2_ET_UNSUPP_FEATURE;
+                       goto cleanup;
+               }
+               if ((flags & EXT2_FLAG_RW) &&
+                   (fs->super->s_feature_ro_compat &
+                    ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) {
+                       retval = EXT2_ET_RO_UNSUPP_FEATURE;
+                       goto cleanup;
+               }
+               if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) &&
+                   (fs->super->s_feature_incompat &
+                    EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
+                       retval = EXT2_ET_UNSUPP_FEATURE;
+                       goto cleanup;
+               }
+       }
+       
+       fs->blocksize = EXT2_BLOCK_SIZE(fs->super);
+       if (fs->blocksize == 0) {
+               retval = EXT2_ET_CORRUPT_SUPERBLOCK;
+               goto cleanup;
+       }
+       fs->fragsize = EXT2_FRAG_SIZE(fs->super);
+       fs->inode_blocks_per_group = ((fs->super->s_inodes_per_group *
+                                      EXT2_INODE_SIZE(fs->super) +
+                                      EXT2_BLOCK_SIZE(fs->super) - 1) /
+                                     EXT2_BLOCK_SIZE(fs->super));
+       if (block_size) {
+               if (block_size != fs->blocksize) {
+                       retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE;
+                       goto cleanup;
+               }
+       }
+       /*
+        * Set the blocksize to the filesystem's blocksize.
+        */
+       io_channel_set_blksize(fs->io, fs->blocksize);
+
+       /*
+        * If this is an external journal device, don't try to read
+        * the group descriptors, because they're not there.
+        */
+       if (fs->super->s_feature_incompat &
+           EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) {
+               fs->group_desc_count = 0;
+               *ret_fs = fs;
+               return 0;
+       }
+       
+       /*
+        * Read group descriptors
+        */
+       blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super);
+       if (blocks_per_group == 0 ||
+           blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) ||
+           fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super)) {
+               retval = EXT2_ET_CORRUPT_SUPERBLOCK;
+               goto cleanup;
+       }
+       fs->group_desc_count = (fs->super->s_blocks_count -
+                               fs->super->s_first_data_block +
+                               blocks_per_group - 1) / blocks_per_group;
+       fs->desc_blocks = (fs->group_desc_count +
+                          EXT2_DESC_PER_BLOCK(fs->super) - 1)
+               / EXT2_DESC_PER_BLOCK(fs->super);
+       retval = ext2fs_get_mem(fs->desc_blocks * fs->blocksize,
+                               &fs->group_desc);
+       if (retval)
+               goto cleanup;
+       if (!group_block)
+               group_block = fs->super->s_first_data_block;
+       dest = (char *) fs->group_desc;
+       groups_per_block = fs->blocksize / sizeof(struct ext2_group_desc);
+       for (i=0 ; i < fs->desc_blocks; i++) {
+               blk = ext2fs_descriptor_block_loc(fs, group_block, i);
+               retval = io_channel_read_blk(fs->io, blk, 1, dest);
+               if (retval)
+                       goto cleanup;
+#ifdef EXT2FS_ENABLE_SWAPFS
+               if (fs->flags & EXT2_FLAG_SWAP_BYTES) {
+                       gdp = (struct ext2_group_desc *) dest;
+                       for (j=0; j < groups_per_block; j++)
+                               ext2fs_swap_group_desc(gdp++);
+               }
+#endif
+               dest += fs->blocksize;
+       }
+
+       *ret_fs = fs;
+       return 0;
+cleanup:
+       ext2fs_free(fs);
+       return retval;
+}
+
+/*
+ * Set/get the filesystem data I/O channel.
+ * 
+ * These functions are only valid if EXT2_FLAG_IMAGE_FILE is true.
+ */
+errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io)
+{
+       if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
+               return EXT2_ET_NOT_IMAGE_FILE;
+       if (old_io) {
+               *old_io = (fs->image_io == fs->io) ? 0 : fs->io;
+       }
+       return 0;
+}
+
+errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io)
+{
+       if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
+               return EXT2_ET_NOT_IMAGE_FILE;
+       fs->io = new_io ? new_io : fs->image_io;
+       return 0;
+}
+
+errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io)
+{
+       if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0)
+               return EXT2_ET_NOT_IMAGE_FILE;
+       fs->io = fs->image_io = new_io;
+       fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW | 
+               EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY;
+       fs->flags &= ~EXT2_FLAG_IMAGE_FILE;
+       return 0;
+}
diff --git a/e2fsprogs/ext2fs/read_bb.c b/e2fsprogs/ext2fs/read_bb.c
new file mode 100644 (file)
index 0000000..c717adc
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * read_bb --- read the bad blocks inode
+ *
+ * Copyright (C) 1994 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct read_bb_record {
+       ext2_badblocks_list     bb_list;
+       errcode_t       err;
+};
+
+/*
+ * Helper function for ext2fs_read_bb_inode()
+ */
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+static int mark_bad_block(ext2_filsys fs, blk_t *block_nr,
+                         e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), 
+                         blk_t ref_block EXT2FS_ATTR((unused)),
+                         int ref_offset EXT2FS_ATTR((unused)), 
+                         void *priv_data)
+{
+       struct read_bb_record *rb = (struct read_bb_record *) priv_data;
+       
+       if (blockcnt < 0)
+               return 0;
+       
+       if ((*block_nr < fs->super->s_first_data_block) ||
+           (*block_nr >= fs->super->s_blocks_count))
+               return 0;       /* Ignore illegal blocks */
+
+       rb->err = ext2fs_badblocks_list_add(rb->bb_list, *block_nr);
+       if (rb->err)
+               return BLOCK_ABORT;
+       return 0;
+}
+
+/*
+ * Reads the current bad blocks from the bad blocks inode.
+ */
+errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list)
+{
+       errcode_t       retval;
+       struct read_bb_record rb;
+       struct ext2_inode inode;
+       blk_t   numblocks;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (!*bb_list) {
+               retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode);
+               if (retval)
+                       return retval;
+               if (inode.i_blocks < 500)
+                       numblocks = (inode.i_blocks /
+                                    (fs->blocksize / 512)) + 20;
+               else
+                       numblocks = 500;
+               retval = ext2fs_badblocks_list_create(bb_list, numblocks);
+               if (retval)
+                       return retval;
+       }
+
+       rb.bb_list = *bb_list;
+       rb.err = 0;
+       retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, 0, 0,
+                                     mark_bad_block, &rb);
+       if (retval)
+               return retval;
+
+       return rb.err;
+}
+
+
diff --git a/e2fsprogs/ext2fs/read_bb_file.c b/e2fsprogs/ext2fs/read_bb_file.c
new file mode 100644 (file)
index 0000000..40c34ee
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * read_bb_file.c --- read a list of bad blocks from a FILE *
+ *
+ * Copyright (C) 1994, 1995, 2000 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Reads a list of bad blocks from  a FILE *
+ */
+errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, 
+                              ext2_badblocks_list *bb_list,
+                              void *priv_data,
+                              void (*invalid)(ext2_filsys fs,
+                                              blk_t blk,
+                                              char *badstr,
+                                              void *priv_data))
+{
+       errcode_t       retval;
+       blk_t           blockno;
+       int             count;
+       char            buf[128];
+
+       if (fs)
+               EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (!*bb_list) {
+               retval = ext2fs_badblocks_list_create(bb_list, 10);
+               if (retval)
+                       return retval;
+       }
+
+       while (!feof (f)) {
+               if (fgets(buf, sizeof(buf), f) == NULL)
+                       break;
+               count = sscanf(buf, "%u", &blockno);
+               if (count <= 0)
+                       continue;
+               if (fs &&
+                   ((blockno < fs->super->s_first_data_block) ||
+                   (blockno >= fs->super->s_blocks_count))) {
+                       if (invalid)
+                               (invalid)(fs, blockno, buf, priv_data);
+                       continue;
+               }
+               retval = ext2fs_badblocks_list_add(*bb_list, blockno);
+               if (retval)
+                       return retval;
+       }
+       return 0;
+}
+
+static void call_compat_invalid(ext2_filsys fs, blk_t blk,
+                               char *badstr EXT2FS_ATTR((unused)), 
+                               void *priv_data)
+{
+       void (*invalid)(ext2_filsys, blk_t);
+
+       invalid = (void (*)(ext2_filsys, blk_t)) priv_data;
+       if (invalid)
+               invalid(fs, blk);
+}
+
+
+/*
+ * Reads a list of bad blocks from  a FILE *
+ */
+errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, 
+                             ext2_badblocks_list *bb_list,
+                             void (*invalid)(ext2_filsys fs, blk_t blk))
+{
+       return ext2fs_read_bb_FILE2(fs, f, bb_list, (void *) invalid,
+                                   call_compat_invalid);
+}
+
+
diff --git a/e2fsprogs/ext2fs/res_gdt.c b/e2fsprogs/ext2fs/res_gdt.c
new file mode 100644 (file)
index 0000000..8b4ddca
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * res_gdt.c --- reserve blocks for growing the group descriptor table
+ *               during online resizing.
+ *
+ * Copyright (C) 2002 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * Iterate through the groups which hold BACKUP superblock/GDT copies in an
+ * ext3 filesystem.  The counters should be initialized to 1, 5, and 7 before
+ * calling this for the first time.  In a sparse filesystem it will be the
+ * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
+ * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
+ */
+static unsigned int list_backups(ext2_filsys fs, unsigned int *three,
+                                unsigned int *five, unsigned int *seven)
+{
+       unsigned int *min = three;
+       int mult = 3;
+       unsigned int ret;
+
+       if (!(fs->super->s_feature_ro_compat &
+             EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
+               ret = *min;
+               *min += 1;
+               return ret;
+       }
+
+       if (*five < *min) {
+               min = five;
+               mult = 5;
+       }
+       if (*seven < *min) {
+               min = seven;
+               mult = 7;
+       }
+
+       ret = *min;
+       *min *= mult;
+
+       return ret;
+}
+
+/*
+ * This code assumes that the reserved blocks have already been marked in-use
+ * during ext2fs_initialize(), so that they are not allocated for other
+ * uses before we can add them to the resize inode (which has to come
+ * after the creation of the inode table).
+ */
+errcode_t ext2fs_create_resize_inode(ext2_filsys fs)
+{
+       errcode_t               retval, retval2;
+       struct ext2_super_block *sb;
+       struct ext2_inode       inode;
+       __u32                   *dindir_buf, *gdt_buf;
+       int                     rsv_add;
+       unsigned long long      apb, inode_size;
+       blk_t                   dindir_blk, rsv_off, gdt_off, gdt_blk;
+       int                     dindir_dirty = 0, inode_dirty = 0;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       sb = fs->super;
+
+       retval = ext2fs_get_mem(2 * fs->blocksize, (void **)&dindir_buf);
+       if (retval)
+               goto out_free;
+       gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize);
+
+       retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode);
+       if (retval)
+               goto out_free;
+
+       /* Maximum possible file size (we donly use the dindirect blocks) */
+       apb = EXT2_ADDR_PER_BLOCK(sb);
+       rsv_add = fs->blocksize / 512;
+       if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) {
+#ifdef RES_GDT_DEBUG
+               printf("reading GDT dindir %u\n", dindir_blk);
+#endif
+               retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf);
+               if (retval)
+                       goto out_inode;
+       } else {
+               blk_t goal = 3 + sb->s_reserved_gdt_blocks +
+                       fs->desc_blocks + fs->inode_blocks_per_group;
+
+               retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk);
+               if (retval)
+                       goto out_free;
+               inode.i_mode = LINUX_S_IFREG | 0600;
+               inode.i_links_count = 1;
+               inode.i_block[EXT2_DIND_BLOCK] = dindir_blk;
+               inode.i_blocks = rsv_add;
+               memset(dindir_buf, 0, fs->blocksize);
+#ifdef RES_GDT_DEBUG
+               printf("allocated GDT dindir %u\n", dindir_blk);
+#endif
+               dindir_dirty = inode_dirty = 1;
+               inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS;
+               inode_size *= fs->blocksize;
+               inode.i_size = inode_size & 0xFFFFFFFF;
+               inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF;
+               if(inode.i_size_high) {
+                       sb->s_feature_ro_compat |=
+                               EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
+               }
+               inode.i_ctime = time(0);
+       }
+
+       for (rsv_off = 0, gdt_off = fs->desc_blocks,
+            gdt_blk = sb->s_first_data_block + 1 + fs->desc_blocks;
+            rsv_off < sb->s_reserved_gdt_blocks;
+            rsv_off++, gdt_off++, gdt_blk++) {
+               unsigned int three = 1, five = 5, seven = 7;
+               unsigned int grp, last = 0;
+               int gdt_dirty = 0;
+
+               gdt_off %= apb;
+               if (!dindir_buf[gdt_off]) {
+                       /* FIXME XXX XXX
+                       blk_t new_blk;
+
+                       retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk);
+                       if (retval)
+                               goto out_free;
+                       if (new_blk != gdt_blk) {
+                               // XXX free block
+                               retval = -1; // XXX
+                       }
+                       */
+                       gdt_dirty = dindir_dirty = inode_dirty = 1;
+                       memset(gdt_buf, 0, fs->blocksize);
+                       dindir_buf[gdt_off] = gdt_blk;
+                       inode.i_blocks += rsv_add;
+#ifdef RES_GDT_DEBUG
+                       printf("added primary GDT block %u at %u[%u]\n",
+                              gdt_blk, dindir_blk, gdt_off);
+#endif
+               } else if (dindir_buf[gdt_off] == gdt_blk) {
+#ifdef RES_GDT_DEBUG
+                       printf("reading primary GDT block %u\n", gdt_blk);
+#endif
+                       retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf);
+                       if (retval)
+                               goto out_dindir;
+               } else {
+#ifdef RES_GDT_DEBUG
+                       printf("bad primary GDT %u != %u at %u[%u]\n",
+                              dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off);
+#endif
+                       retval = EXT2_ET_RESIZE_INODE_CORRUPT;
+                       goto out_dindir;
+               }
+
+               while ((grp = list_backups(fs, &three, &five, &seven)) <
+                      fs->group_desc_count) {
+                       blk_t expect = gdt_blk + grp * sb->s_blocks_per_group;
+
+                       if (!gdt_buf[last]) {
+#ifdef RES_GDT_DEBUG
+                               printf("added backup GDT %u grp %u@%u[%u]\n",
+                                      expect, grp, gdt_blk, last);
+#endif
+                               gdt_buf[last] = expect;
+                               inode.i_blocks += rsv_add;
+                               gdt_dirty = inode_dirty = 1;
+                       } else if (gdt_buf[last] != expect) {
+#ifdef RES_GDT_DEBUG
+                               printf("bad backup GDT %u != %u at %u[%u]\n",
+                                      gdt_buf[last], expect, gdt_blk, last);
+#endif
+                               retval = EXT2_ET_RESIZE_INODE_CORRUPT;
+                               goto out_dindir;
+                       }
+                       last++;
+               }
+               if (gdt_dirty) {
+#ifdef RES_GDT_DEBUG
+                       printf("writing primary GDT block %u\n", gdt_blk);
+#endif
+                       retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf);
+                       if (retval)
+                               goto out_dindir;
+               }
+       }
+
+out_dindir:
+       if (dindir_dirty) {
+               retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf);
+               if (!retval)
+                       retval = retval2;
+       }
+out_inode:
+#ifdef RES_GDT_DEBUG
+       printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks,
+              inode.i_size);
+#endif
+       if (inode_dirty) {
+               inode.i_atime = inode.i_mtime = time(0);
+               retval2 = ext2fs_write_inode(fs, EXT2_RESIZE_INO, &inode);
+               if (!retval)
+                       retval = retval2;
+       }
+out_free:
+       ext2fs_free_mem((void **)&dindir_buf);
+       return retval;
+}
+
diff --git a/e2fsprogs/ext2fs/rs_bitmap.c b/e2fsprogs/ext2fs/rs_bitmap.c
new file mode 100644 (file)
index 0000000..46653f0
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * rs_bitmap.c --- routine for changing the size of a bitmap
+ *
+ * Copyright (C) 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_resize_generic_bitmap(__u32 new_end, __u32 new_real_end,
+                                      ext2fs_generic_bitmap bmap)
+{
+       errcode_t       retval;
+       size_t          size, new_size;
+       __u32           bitno;
+
+       if (!bmap)
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_GENERIC_BITMAP);
+
+       /*
+        * If we're expanding the bitmap, make sure all of the new
+        * parts of the bitmap are zero.
+        */
+       if (new_end > bmap->end) {
+               bitno = bmap->real_end;
+               if (bitno > new_end)
+                       bitno = new_end;
+               for (; bitno > bmap->end; bitno--)
+                       ext2fs_clear_bit(bitno - bmap->start, bmap->bitmap);
+       }
+       if (new_real_end == bmap->real_end) {
+               bmap->end = new_end;
+               return 0;
+       }
+       
+       size = ((bmap->real_end - bmap->start) / 8) + 1;
+       new_size = ((new_real_end - bmap->start) / 8) + 1;
+
+       if (size != new_size) {
+               retval = ext2fs_resize_mem(size, new_size, &bmap->bitmap);
+               if (retval)
+                       return retval;
+       }
+       if (new_size > size)
+               memset(bmap->bitmap + size, 0, new_size - size);
+
+       bmap->end = new_end;
+       bmap->real_end = new_real_end;
+       return 0;
+}
+
+errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end,
+                                    ext2fs_inode_bitmap bmap)
+{
+       errcode_t       retval;
+       
+       if (!bmap)
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_INODE_BITMAP);
+
+       bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+       retval = ext2fs_resize_generic_bitmap(new_end, new_real_end,
+                                             bmap);
+       bmap->magic = EXT2_ET_MAGIC_INODE_BITMAP;
+       return retval;
+}
+
+errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end,
+                                    ext2fs_block_bitmap bmap)
+{
+       errcode_t       retval;
+       
+       if (!bmap)
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       EXT2_CHECK_MAGIC(bmap, EXT2_ET_MAGIC_BLOCK_BITMAP);
+
+       bmap->magic = EXT2_ET_MAGIC_GENERIC_BITMAP;
+       retval = ext2fs_resize_generic_bitmap(new_end, new_real_end,
+                                             bmap);
+       bmap->magic = EXT2_ET_MAGIC_BLOCK_BITMAP;
+       return retval;
+}
+
diff --git a/e2fsprogs/ext2fs/rw_bitmaps.c b/e2fsprogs/ext2fs/rw_bitmaps.c
new file mode 100644 (file)
index 0000000..b67a925
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * rw_bitmaps.c --- routines to read and write the  inode and block bitmaps.
+ *
+ * Copyright (C) 1993, 1994, 1994, 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include "e2image.h"
+
+#if defined(__powerpc__) && defined(EXT2FS_ENABLE_SWAPFS)
+/*
+ * On the PowerPC, the big-endian variant of the ext2 filesystem
+ * has its bitmaps stored as 32-bit words with bit 0 as the LSB
+ * of each word.  Thus a bitmap with only bit 0 set would be, as
+ * a string of bytes, 00 00 00 01 00 ...
+ * To cope with this, we byte-reverse each word of a bitmap if
+ * we have a big-endian filesystem, that is, if we are *not*
+ * byte-swapping other word-sized numbers.
+ */
+#define EXT2_BIG_ENDIAN_BITMAPS
+#endif
+
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+static void ext2fs_swap_bitmap(ext2_filsys fs, char *bitmap, int nbytes)
+{
+       __u32 *p = (__u32 *) bitmap;
+       int n;
+               
+       for (n = nbytes / sizeof(__u32); n > 0; --n, ++p)
+               *p = ext2fs_swab32(*p);
+}
+#endif
+
+errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs)
+{
+       dgrp_t          i;
+       size_t          nbytes;
+       errcode_t       retval;
+       char * inode_bitmap = fs->inode_map->bitmap;
+       char * bitmap_block = NULL;
+       blk_t           blk;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (!(fs->flags & EXT2_FLAG_RW))
+               return EXT2_ET_RO_FILSYS;
+       if (!inode_bitmap)
+               return 0;
+       nbytes = (size_t) ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8);
+       
+       retval = ext2fs_get_mem(fs->blocksize, &bitmap_block);
+       if (retval)
+               return retval;
+       memset(bitmap_block, 0xff, fs->blocksize);
+       for (i = 0; i < fs->group_desc_count; i++) {
+               memcpy(bitmap_block, inode_bitmap, nbytes);
+               blk = fs->group_desc[i].bg_inode_bitmap;
+               if (blk) {
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+                       if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+                             (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)))
+                               ext2fs_swap_bitmap(fs, bitmap_block, nbytes);
+#endif
+                       retval = io_channel_write_blk(fs->io, blk, 1,
+                                                     bitmap_block);
+                       if (retval)
+                               return EXT2_ET_INODE_BITMAP_WRITE;
+               }
+               inode_bitmap += nbytes;
+       }
+       fs->flags &= ~EXT2_FLAG_IB_DIRTY;
+       ext2fs_free_mem(&bitmap_block);
+       return 0;
+}
+
+errcode_t ext2fs_write_block_bitmap (ext2_filsys fs)
+{
+       dgrp_t          i;
+       unsigned int    j;
+       int             nbytes;
+       unsigned int    nbits;
+       errcode_t       retval;
+       char * block_bitmap = fs->block_map->bitmap;
+       char * bitmap_block = NULL;
+       blk_t           blk;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (!(fs->flags & EXT2_FLAG_RW))
+               return EXT2_ET_RO_FILSYS;
+       if (!block_bitmap)
+               return 0;
+       nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+       retval = ext2fs_get_mem(fs->blocksize, &bitmap_block);
+       if (retval)
+               return retval;
+       memset(bitmap_block, 0xff, fs->blocksize);
+       for (i = 0; i < fs->group_desc_count; i++) {
+               memcpy(bitmap_block, block_bitmap, nbytes);
+               if (i == fs->group_desc_count - 1) {
+                       /* Force bitmap padding for the last group */
+                       nbits = ((fs->super->s_blocks_count
+                                 - fs->super->s_first_data_block)
+                                % EXT2_BLOCKS_PER_GROUP(fs->super));
+                       if (nbits)
+                               for (j = nbits; j < fs->blocksize * 8; j++)
+                                       ext2fs_set_bit(j, bitmap_block);
+               }
+               blk = fs->group_desc[i].bg_block_bitmap;
+               if (blk) {
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+                       if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+                             (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)))
+                               ext2fs_swap_bitmap(fs, bitmap_block, nbytes);
+#endif
+                       retval = io_channel_write_blk(fs->io, blk, 1,
+                                                     bitmap_block);
+                       if (retval)
+                               return EXT2_ET_BLOCK_BITMAP_WRITE;
+               }
+               block_bitmap += nbytes;
+       }
+       fs->flags &= ~EXT2_FLAG_BB_DIRTY;
+       ext2fs_free_mem(&bitmap_block);
+       return 0;
+}
+
+static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block)
+{
+       dgrp_t i;
+       char *block_bitmap = 0, *inode_bitmap = 0;
+       char *buf;
+       errcode_t retval;
+       int block_nbytes = (int) EXT2_BLOCKS_PER_GROUP(fs->super) / 8;
+       int inode_nbytes = (int) EXT2_INODES_PER_GROUP(fs->super) / 8;
+       blk_t   blk;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       fs->write_bitmaps = ext2fs_write_bitmaps;
+
+       retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf);
+       if (retval)
+               return retval;
+       if (do_block) {
+               if (fs->block_map)
+                       ext2fs_free_block_bitmap(fs->block_map);
+               sprintf(buf, "block bitmap for %s", fs->device_name);
+               retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map);
+               if (retval)
+                       goto cleanup;
+               block_bitmap = fs->block_map->bitmap;
+       }
+       if (do_inode) {
+               if (fs->inode_map)
+                       ext2fs_free_inode_bitmap(fs->inode_map);
+               sprintf(buf, "inode bitmap for %s", fs->device_name);
+               retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map);
+               if (retval)
+                       goto cleanup;
+               inode_bitmap = fs->inode_map->bitmap;
+       }
+       ext2fs_free_mem(&buf);
+
+       if (fs->flags & EXT2_FLAG_IMAGE_FILE) {
+               if (inode_bitmap) {
+                       blk = (fs->image_header->offset_inodemap /
+                              fs->blocksize);
+                       retval = io_channel_read_blk(fs->image_io, blk,
+                            -(inode_nbytes * fs->group_desc_count),
+                            inode_bitmap);
+                       if (retval)
+                               goto cleanup;
+               }
+               if (block_bitmap) {
+                       blk = (fs->image_header->offset_blockmap /
+                              fs->blocksize);
+                       retval = io_channel_read_blk(fs->image_io, blk, 
+                            -(block_nbytes * fs->group_desc_count),
+                            block_bitmap);
+                       if (retval)
+                               goto cleanup;
+               }
+               return 0;
+       }
+
+       for (i = 0; i < fs->group_desc_count; i++) {
+               if (block_bitmap) {
+                       blk = fs->group_desc[i].bg_block_bitmap;
+                       if (blk) {
+                               retval = io_channel_read_blk(fs->io, blk,
+                                            -block_nbytes, block_bitmap);
+                               if (retval) {
+                                       retval = EXT2_ET_BLOCK_BITMAP_READ;
+                                       goto cleanup;
+                               }
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+                               if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+                                     (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)))
+                                       ext2fs_swap_bitmap(fs, block_bitmap, block_nbytes);
+#endif
+                       } else
+                               memset(block_bitmap, 0, block_nbytes);
+                       block_bitmap += block_nbytes;
+               }
+               if (inode_bitmap) {
+                       blk = fs->group_desc[i].bg_inode_bitmap;
+                       if (blk) {
+                               retval = io_channel_read_blk(fs->io, blk,
+                                            -inode_nbytes, inode_bitmap);
+                               if (retval) {
+                                       retval = EXT2_ET_INODE_BITMAP_READ;
+                                       goto cleanup;
+                               }
+#ifdef EXT2_BIG_ENDIAN_BITMAPS
+                               if (!((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
+                                     (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)))
+                                       ext2fs_swap_bitmap(fs, inode_bitmap, inode_nbytes);
+#endif
+                       } else
+                               memset(inode_bitmap, 0, inode_nbytes);
+                       inode_bitmap += inode_nbytes;
+               }
+       }
+       return 0;
+       
+cleanup:
+       if (do_block) {
+               ext2fs_free_mem(&fs->block_map);
+               fs->block_map = 0;
+       }
+       if (do_inode) {
+               ext2fs_free_mem(&fs->inode_map);
+               fs->inode_map = 0;
+       }
+       if (buf)
+               ext2fs_free_mem(&buf);
+       return retval;
+}
+
+errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs)
+{
+       return read_bitmaps(fs, 1, 0);
+}
+
+errcode_t ext2fs_read_block_bitmap(ext2_filsys fs)
+{
+       return read_bitmaps(fs, 0, 1);
+}
+
+errcode_t ext2fs_read_bitmaps(ext2_filsys fs)
+{
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (fs->inode_map && fs->block_map)
+               return 0;
+
+       return read_bitmaps(fs, !fs->inode_map, !fs->block_map);
+}
+
+errcode_t ext2fs_write_bitmaps(ext2_filsys fs)
+{
+       errcode_t       retval;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (fs->block_map && ext2fs_test_bb_dirty(fs)) {
+               retval = ext2fs_write_block_bitmap(fs);
+               if (retval)
+                       return retval;
+       }
+       if (fs->inode_map && ext2fs_test_ib_dirty(fs)) {
+               retval = ext2fs_write_inode_bitmap(fs);
+               if (retval)
+                       return retval;
+       }
+       return 0;
+}      
+
diff --git a/e2fsprogs/ext2fs/sparse.c b/e2fsprogs/ext2fs/sparse.c
new file mode 100644 (file)
index 0000000..90b028f
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * sparse.c --- find the groups in an ext2 filesystem with metadata backups
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
+ * Copyright (C) 2002 Andreas Dilger.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fsP.h"
+
+static int test_root(int a, int b)
+{
+       if (a == 0)
+               return 1;
+       while (1) {
+               if (a == 1)
+                       return 1;
+               if (a % b)
+                       return 0;
+               a = a / b;
+       }
+}
+
+int ext2fs_bg_has_super(ext2_filsys fs, int group_block)
+{
+       if (!(fs->super->s_feature_ro_compat &
+             EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER))
+               return 1;
+
+       if (test_root(group_block, 3) || (test_root(group_block, 5)) ||
+           test_root(group_block, 7))
+               return 1;
+
+       return 0;
+}
+
+/*
+ * Iterate through the groups which hold BACKUP superblock/GDT copies in an
+ * ext3 filesystem.  The counters should be initialized to 1, 5, and 7 before
+ * calling this for the first time.  In a sparse filesystem it will be the
+ * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ...
+ * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ...
+ */
+unsigned int ext2fs_list_backups(ext2_filsys fs, unsigned int *three,
+                                unsigned int *five, unsigned int *seven)
+{
+       unsigned int *min = three;
+       int mult = 3;
+       unsigned int ret;
+
+       if (!(fs->super->s_feature_ro_compat &
+             EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
+               ret = *min;
+               *min += 1;
+               return ret;
+       }
+
+       if (*five < *min) {
+               min = five;
+               mult = 5;
+       }
+       if (*seven < *min) {
+               min = seven;
+               mult = 7;
+       }
+
+       ret = *min;
+       *min *= mult;
+
+       return ret;
+}
diff --git a/e2fsprogs/ext2fs/swapfs.c b/e2fsprogs/ext2fs/swapfs.c
new file mode 100644 (file)
index 0000000..9081701
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * swapfs.c --- swap ext2 filesystem data structures
+ * 
+ * Copyright (C) 1995, 1996, 2002 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+#include <ext2fs/ext2_ext_attr.h>
+
+#ifdef EXT2FS_ENABLE_SWAPFS
+void ext2fs_swap_super(struct ext2_super_block * sb)
+{
+       int i;
+       sb->s_inodes_count = ext2fs_swab32(sb->s_inodes_count);
+       sb->s_blocks_count = ext2fs_swab32(sb->s_blocks_count);
+       sb->s_r_blocks_count = ext2fs_swab32(sb->s_r_blocks_count);
+       sb->s_free_blocks_count = ext2fs_swab32(sb->s_free_blocks_count);
+       sb->s_free_inodes_count = ext2fs_swab32(sb->s_free_inodes_count);
+       sb->s_first_data_block = ext2fs_swab32(sb->s_first_data_block);
+       sb->s_log_block_size = ext2fs_swab32(sb->s_log_block_size);
+       sb->s_log_frag_size = ext2fs_swab32(sb->s_log_frag_size);
+       sb->s_blocks_per_group = ext2fs_swab32(sb->s_blocks_per_group);
+       sb->s_frags_per_group = ext2fs_swab32(sb->s_frags_per_group);
+       sb->s_inodes_per_group = ext2fs_swab32(sb->s_inodes_per_group);
+       sb->s_mtime = ext2fs_swab32(sb->s_mtime);
+       sb->s_wtime = ext2fs_swab32(sb->s_wtime);
+       sb->s_mnt_count = ext2fs_swab16(sb->s_mnt_count);
+       sb->s_max_mnt_count = ext2fs_swab16(sb->s_max_mnt_count);
+       sb->s_magic = ext2fs_swab16(sb->s_magic);
+       sb->s_state = ext2fs_swab16(sb->s_state);
+       sb->s_errors = ext2fs_swab16(sb->s_errors);
+       sb->s_minor_rev_level = ext2fs_swab16(sb->s_minor_rev_level);
+       sb->s_lastcheck = ext2fs_swab32(sb->s_lastcheck);
+       sb->s_checkinterval = ext2fs_swab32(sb->s_checkinterval);
+       sb->s_creator_os = ext2fs_swab32(sb->s_creator_os);
+       sb->s_rev_level = ext2fs_swab32(sb->s_rev_level);
+       sb->s_def_resuid = ext2fs_swab16(sb->s_def_resuid);
+       sb->s_def_resgid = ext2fs_swab16(sb->s_def_resgid);
+       sb->s_first_ino = ext2fs_swab32(sb->s_first_ino);
+       sb->s_inode_size = ext2fs_swab16(sb->s_inode_size);
+       sb->s_block_group_nr = ext2fs_swab16(sb->s_block_group_nr);
+       sb->s_feature_compat = ext2fs_swab32(sb->s_feature_compat);
+       sb->s_feature_incompat = ext2fs_swab32(sb->s_feature_incompat);
+       sb->s_feature_ro_compat = ext2fs_swab32(sb->s_feature_ro_compat);
+       sb->s_algorithm_usage_bitmap = ext2fs_swab32(sb->s_algorithm_usage_bitmap);
+       sb->s_reserved_gdt_blocks = ext2fs_swab16(sb->s_reserved_gdt_blocks);
+       sb->s_journal_inum = ext2fs_swab32(sb->s_journal_inum);
+       sb->s_journal_dev = ext2fs_swab32(sb->s_journal_dev);
+       sb->s_last_orphan = ext2fs_swab32(sb->s_last_orphan);
+       sb->s_default_mount_opts = ext2fs_swab32(sb->s_default_mount_opts);
+       sb->s_first_meta_bg = ext2fs_swab32(sb->s_first_meta_bg);
+       sb->s_mkfs_time = ext2fs_swab32(sb->s_mkfs_time);
+       for (i=0; i < 4; i++)
+               sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]);
+       for (i=0; i < 17; i++)
+               sb->s_jnl_blocks[i] = ext2fs_swab32(sb->s_jnl_blocks[i]);
+
+}
+
+void ext2fs_swap_group_desc(struct ext2_group_desc *gdp)
+{
+       gdp->bg_block_bitmap = ext2fs_swab32(gdp->bg_block_bitmap);
+       gdp->bg_inode_bitmap = ext2fs_swab32(gdp->bg_inode_bitmap);
+       gdp->bg_inode_table = ext2fs_swab32(gdp->bg_inode_table);
+       gdp->bg_free_blocks_count = ext2fs_swab16(gdp->bg_free_blocks_count);
+       gdp->bg_free_inodes_count = ext2fs_swab16(gdp->bg_free_inodes_count);
+       gdp->bg_used_dirs_count = ext2fs_swab16(gdp->bg_used_dirs_count);
+}
+
+void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header)
+{
+       struct ext2_ext_attr_header *from_header =
+               (struct ext2_ext_attr_header *)from;
+       struct ext2_ext_attr_header *to_header =
+               (struct ext2_ext_attr_header *)to;
+       struct ext2_ext_attr_entry *from_entry, *to_entry;
+       char *from_end = (char *)from_header + bufsize;
+       int n;
+
+       if (to_header != from_header)
+               memcpy(to_header, from_header, bufsize);
+
+       from_entry = (struct ext2_ext_attr_entry *)from_header;
+       to_entry   = (struct ext2_ext_attr_entry *)to_header;
+
+       if (has_header) {
+               to_header->h_magic    = ext2fs_swab32(from_header->h_magic);
+               to_header->h_blocks   = ext2fs_swab32(from_header->h_blocks);
+               to_header->h_refcount = ext2fs_swab32(from_header->h_refcount);
+               for (n=0; n<4; n++)
+                       to_header->h_reserved[n] =
+                               ext2fs_swab32(from_header->h_reserved[n]);
+               from_entry = (struct ext2_ext_attr_entry *)(from_header+1);
+               to_entry   = (struct ext2_ext_attr_entry *)(to_header+1);
+       }
+
+       while ((char *)from_entry < from_end && *(__u32 *)from_entry) {
+               to_entry->e_value_offs  =       
+                       ext2fs_swab16(from_entry->e_value_offs);
+               to_entry->e_value_block =       
+                       ext2fs_swab32(from_entry->e_value_block);
+               to_entry->e_value_size  =       
+                       ext2fs_swab32(from_entry->e_value_size);
+               from_entry = EXT2_EXT_ATTR_NEXT(from_entry);
+               to_entry   = EXT2_EXT_ATTR_NEXT(to_entry);
+       }
+}
+
+void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
+                           struct ext2_inode_large *f, int hostorder,
+                           int bufsize)
+{
+       unsigned i;
+       int islnk = 0;
+       __u32 *eaf, *eat;
+
+       if (hostorder && LINUX_S_ISLNK(f->i_mode))
+               islnk = 1;
+       t->i_mode = ext2fs_swab16(f->i_mode);
+       if (!hostorder && LINUX_S_ISLNK(t->i_mode))
+               islnk = 1;
+       t->i_uid = ext2fs_swab16(f->i_uid);
+       t->i_size = ext2fs_swab32(f->i_size);
+       t->i_atime = ext2fs_swab32(f->i_atime);
+       t->i_ctime = ext2fs_swab32(f->i_ctime);
+       t->i_mtime = ext2fs_swab32(f->i_mtime);
+       t->i_dtime = ext2fs_swab32(f->i_dtime);
+       t->i_gid = ext2fs_swab16(f->i_gid);
+       t->i_links_count = ext2fs_swab16(f->i_links_count);
+       t->i_blocks = ext2fs_swab32(f->i_blocks);
+       t->i_flags = ext2fs_swab32(f->i_flags);
+       t->i_file_acl = ext2fs_swab32(f->i_file_acl);
+       t->i_dir_acl = ext2fs_swab32(f->i_dir_acl);
+       if (!islnk || ext2fs_inode_data_blocks(fs, (struct ext2_inode *)t)) {
+               for (i = 0; i < EXT2_N_BLOCKS; i++)
+                       t->i_block[i] = ext2fs_swab32(f->i_block[i]);
+       } else if (t != f) {
+               for (i = 0; i < EXT2_N_BLOCKS; i++)
+                       t->i_block[i] = f->i_block[i];
+       }
+       t->i_generation = ext2fs_swab32(f->i_generation);
+       t->i_faddr = ext2fs_swab32(f->i_faddr);
+
+       switch (fs->super->s_creator_os) {
+       case EXT2_OS_LINUX:
+               t->osd1.linux1.l_i_reserved1 =
+                       ext2fs_swab32(f->osd1.linux1.l_i_reserved1);
+               t->osd2.linux2.l_i_frag = f->osd2.linux2.l_i_frag;
+               t->osd2.linux2.l_i_fsize = f->osd2.linux2.l_i_fsize;
+               t->osd2.linux2.i_pad1 = ext2fs_swab16(f->osd2.linux2.i_pad1);
+               t->osd2.linux2.l_i_uid_high =
+                 ext2fs_swab16 (f->osd2.linux2.l_i_uid_high);
+               t->osd2.linux2.l_i_gid_high =
+                 ext2fs_swab16 (f->osd2.linux2.l_i_gid_high);
+               t->osd2.linux2.l_i_reserved2 =
+                       ext2fs_swab32(f->osd2.linux2.l_i_reserved2);
+               break;
+       case EXT2_OS_HURD:
+               t->osd1.hurd1.h_i_translator =
+                 ext2fs_swab32 (f->osd1.hurd1.h_i_translator);
+               t->osd2.hurd2.h_i_frag = f->osd2.hurd2.h_i_frag;
+               t->osd2.hurd2.h_i_fsize = f->osd2.hurd2.h_i_fsize;
+               t->osd2.hurd2.h_i_mode_high =
+                 ext2fs_swab16 (f->osd2.hurd2.h_i_mode_high);
+               t->osd2.hurd2.h_i_uid_high =
+                 ext2fs_swab16 (f->osd2.hurd2.h_i_uid_high);
+               t->osd2.hurd2.h_i_gid_high =
+                 ext2fs_swab16 (f->osd2.hurd2.h_i_gid_high);
+               t->osd2.hurd2.h_i_author =
+                 ext2fs_swab32 (f->osd2.hurd2.h_i_author);
+               break;
+       case EXT2_OS_MASIX:
+               t->osd1.masix1.m_i_reserved1 =
+                       ext2fs_swab32(f->osd1.masix1.m_i_reserved1);
+               t->osd2.masix2.m_i_frag = f->osd2.masix2.m_i_frag;
+               t->osd2.masix2.m_i_fsize = f->osd2.masix2.m_i_fsize;
+               t->osd2.masix2.m_pad1 = ext2fs_swab16(f->osd2.masix2.m_pad1);
+               t->osd2.masix2.m_i_reserved2[0] =
+                       ext2fs_swab32(f->osd2.masix2.m_i_reserved2[0]);
+               t->osd2.masix2.m_i_reserved2[1] =
+                       ext2fs_swab32(f->osd2.masix2.m_i_reserved2[1]);
+               break;
+       }
+
+       if (bufsize < (int) (sizeof(struct ext2_inode) + sizeof(__u16)))
+               return; /* no i_extra_isize field */
+
+       t->i_extra_isize = ext2fs_swab16(f->i_extra_isize);
+       if (t->i_extra_isize > EXT2_INODE_SIZE(fs->super) -
+                               sizeof(struct ext2_inode)) {
+               /* this is error case: i_extra_size is too large */
+               return;
+       }
+
+       i = sizeof(struct ext2_inode) + t->i_extra_isize + sizeof(__u32);
+       if (bufsize < (int) i)
+               return; /* no space for EA magic */
+
+       eaf = (__u32 *) (((char *) f) + sizeof(struct ext2_inode) +
+                                       f->i_extra_isize);
+
+       if (ext2fs_swab32(*eaf) != EXT2_EXT_ATTR_MAGIC)
+               return; /* it seems no magic here */
+
+       eat = (__u32 *) (((char *) t) + sizeof(struct ext2_inode) +
+                                       f->i_extra_isize);
+       *eat = ext2fs_swab32(*eaf);
+
+       /* convert EA(s) */
+       ext2fs_swap_ext_attr((char *) (eat + 1), (char *) (eaf + 1),
+                            bufsize - sizeof(struct ext2_inode) -
+                            t->i_extra_isize - sizeof(__u32), 0);
+
+}
+
+void ext2fs_swap_inode(ext2_filsys fs, struct ext2_inode *t,
+                      struct ext2_inode *f, int hostorder)
+{
+       ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) t,
+                               (struct ext2_inode_large *) f, hostorder,
+                               sizeof(struct ext2_inode));
+}
+
+#endif
diff --git a/e2fsprogs/ext2fs/test_io.c b/e2fsprogs/ext2fs/test_io.c
new file mode 100644 (file)
index 0000000..6a3b248
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * test_io.c --- This is the Test I/O interface.
+ *
+ * Copyright (C) 1996 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+         if ((struct)->magic != (code)) return (code)
+  
+struct test_private_data {
+       int     magic;
+       io_channel real;
+       int flags;
+       FILE *outfile;
+       unsigned long block;
+       int read_abort_count, write_abort_count;
+       void (*read_blk)(unsigned long block, int count, errcode_t err);
+       void (*write_blk)(unsigned long block, int count, errcode_t err);
+       void (*set_blksize)(int blksize, errcode_t err);
+       void (*write_byte)(unsigned long block, int count, errcode_t err);
+};
+
+static errcode_t test_open(const char *name, int flags, io_channel *channel);
+static errcode_t test_close(io_channel channel);
+static errcode_t test_set_blksize(io_channel channel, int blksize);
+static errcode_t test_read_blk(io_channel channel, unsigned long block,
+                              int count, void *data);
+static errcode_t test_write_blk(io_channel channel, unsigned long block,
+                               int count, const void *data);
+static errcode_t test_flush(io_channel channel);
+static errcode_t test_write_byte(io_channel channel, unsigned long offset,
+                                int count, const void *buf);
+static errcode_t test_set_option(io_channel channel, const char *option, 
+                                const char *arg);
+
+static struct struct_io_manager struct_test_manager = {
+       EXT2_ET_MAGIC_IO_MANAGER,
+       "Test I/O Manager",
+       test_open,
+       test_close,
+       test_set_blksize,
+       test_read_blk,
+       test_write_blk,
+       test_flush,
+       test_write_byte,
+       test_set_option
+};
+
+io_manager test_io_manager = &struct_test_manager;
+
+/*
+ * These global variable can be set by the test program as
+ * necessary *before* calling test_open
+ */
+io_manager test_io_backing_manager = 0;
+void (*test_io_cb_read_blk)
+       (unsigned long block, int count, errcode_t err) = 0;
+void (*test_io_cb_write_blk)
+       (unsigned long block, int count, errcode_t err) = 0;
+void (*test_io_cb_set_blksize)
+       (int blksize, errcode_t err) = 0;
+void (*test_io_cb_write_byte)
+       (unsigned long block, int count, errcode_t err) = 0;
+
+/*
+ * Test flags
+ */
+#define TEST_FLAG_READ                 0x01
+#define TEST_FLAG_WRITE                        0x02
+#define TEST_FLAG_SET_BLKSIZE          0x04
+#define TEST_FLAG_FLUSH                        0x08
+#define TEST_FLAG_DUMP                 0x10
+#define TEST_FLAG_SET_OPTION           0x20
+
+static void test_dump_block(io_channel channel,
+                           struct test_private_data *data,
+                           unsigned long block, const void *buf)
+{
+       const unsigned char *cp;
+       FILE *f = data->outfile;
+       int     i;
+       unsigned long   cksum = 0;
+
+       for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
+               cksum += *cp;
+       }
+       fprintf(f, "Contents of block %lu, checksum %08lu: \n", block, cksum);
+       for (i=0, cp = buf; i < channel->block_size; i++, cp++) {
+               if ((i % 16) == 0)
+                       fprintf(f, "%04x: ", i);
+               fprintf(f, "%02x%c", *cp, ((i % 16) == 15) ? '\n' : ' ');
+       }
+}
+
+static void test_abort(io_channel channel, unsigned long block)
+{
+       struct test_private_data *data;
+       FILE *f;
+
+       data = (struct test_private_data *) channel->private_data;
+       f = data->outfile;
+       test_flush(channel);
+
+       fprintf(f, "Aborting due to I/O to block %lu\n", block);
+       fflush(f);
+       abort();
+}
+
+static errcode_t test_open(const char *name, int flags, io_channel *channel)
+{
+       io_channel      io = NULL;
+       struct test_private_data *data = NULL;
+       errcode_t       retval;
+       char            *value;
+
+       if (name == 0)
+               return EXT2_ET_BAD_DEVICE_NAME;
+       retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
+       if (retval)
+               return retval;
+       memset(io, 0, sizeof(struct struct_io_channel));
+       io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+       retval = ext2fs_get_mem(sizeof(struct test_private_data), &data);
+       if (retval) {
+               retval = EXT2_ET_NO_MEMORY;
+               goto cleanup;
+       }
+       io->manager = test_io_manager;
+       retval = ext2fs_get_mem(strlen(name)+1, &io->name);
+       if (retval)
+               goto cleanup;
+
+       strcpy(io->name, name);
+       io->private_data = data;
+       io->block_size = 1024;
+       io->read_error = 0;
+       io->write_error = 0;
+       io->refcount = 1;
+
+       memset(data, 0, sizeof(struct test_private_data));
+       data->magic = EXT2_ET_MAGIC_TEST_IO_CHANNEL;
+       if (test_io_backing_manager) {
+               retval = test_io_backing_manager->open(name, flags,
+                                                      &data->real);
+               if (retval)
+                       goto cleanup;
+       } else
+               data->real = 0;
+       data->read_blk =        test_io_cb_read_blk;
+       data->write_blk =       test_io_cb_write_blk;
+       data->set_blksize =     test_io_cb_set_blksize;
+       data->write_byte =      test_io_cb_write_byte;
+
+       data->outfile = NULL;
+       if ((value = getenv("TEST_IO_LOGFILE")) != NULL)
+               data->outfile = fopen(value, "w");
+       if (!data->outfile)
+               data->outfile = stderr;
+
+       data->flags = 0;
+       if ((value = getenv("TEST_IO_FLAGS")) != NULL)
+               data->flags = strtoul(value, NULL, 0);
+       
+       data->block = 0;
+       if ((value = getenv("TEST_IO_BLOCK")) != NULL)
+               data->block = strtoul(value, NULL, 0);
+
+       data->read_abort_count = 0;
+       if ((value = getenv("TEST_IO_READ_ABORT")) != NULL)
+               data->read_abort_count = strtoul(value, NULL, 0);
+
+       data->write_abort_count = 0;
+       if ((value = getenv("TEST_IO_WRITE_ABORT")) != NULL)
+               data->write_abort_count = strtoul(value, NULL, 0);
+       
+       *channel = io;
+       return 0;
+
+cleanup:
+       if (io)
+               ext2fs_free_mem(&io);
+       if (data)
+               ext2fs_free_mem(&data);
+       return retval;
+}
+
+static errcode_t test_close(io_channel channel)
+{
+       struct test_private_data *data;
+       errcode_t       retval = 0;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct test_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+       if (--channel->refcount > 0)
+               return 0;
+       
+       if (data->real)
+               retval = io_channel_close(data->real);
+
+       if (data->outfile && data->outfile != stderr)
+               fclose(data->outfile);
+       
+       ext2fs_free_mem(&channel->private_data);
+       if (channel->name)
+               ext2fs_free_mem(&channel->name);
+       ext2fs_free_mem(&channel);
+       return retval;
+}
+
+static errcode_t test_set_blksize(io_channel channel, int blksize)
+{
+       struct test_private_data *data;
+       errcode_t       retval = 0;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct test_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+       if (data->real)
+               retval = io_channel_set_blksize(data->real, blksize);
+       if (data->set_blksize)
+               data->set_blksize(blksize, retval);
+       if (data->flags & TEST_FLAG_SET_BLKSIZE)
+               fprintf(data->outfile,
+                       "Test_io: set_blksize(%d) returned %s\n",
+                       blksize, retval ? error_message(retval) : "OK");
+       channel->block_size = blksize;
+       return retval;
+}
+
+
+static errcode_t test_read_blk(io_channel channel, unsigned long block,
+                              int count, void *buf)
+{
+       struct test_private_data *data;
+       errcode_t       retval = 0;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct test_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+       if (data->real)
+               retval = io_channel_read_blk(data->real, block, count, buf);
+       if (data->read_blk)
+               data->read_blk(block, count, retval);
+       if (data->flags & TEST_FLAG_READ)
+               fprintf(data->outfile,
+                       "Test_io: read_blk(%lu, %d) returned %s\n",
+                       block, count, retval ? error_message(retval) : "OK");
+       if (data->block && data->block == block) {
+               if (data->flags & TEST_FLAG_DUMP)
+                       test_dump_block(channel, data, block, buf);
+               if (--data->read_abort_count == 0)
+                       test_abort(channel, block);
+       } 
+       return retval;
+}
+
+static errcode_t test_write_blk(io_channel channel, unsigned long block,
+                              int count, const void *buf)
+{
+       struct test_private_data *data;
+       errcode_t       retval = 0;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct test_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+       if (data->real)
+               retval = io_channel_write_blk(data->real, block, count, buf);
+       if (data->write_blk)
+               data->write_blk(block, count, retval);
+       if (data->flags & TEST_FLAG_WRITE)
+               fprintf(data->outfile,
+                       "Test_io: write_blk(%lu, %d) returned %s\n",
+                       block, count, retval ? error_message(retval) : "OK");
+       if (data->block && data->block == block) {
+               if (data->flags & TEST_FLAG_DUMP)
+                       test_dump_block(channel, data, block, buf);
+               if (--data->write_abort_count == 0)
+                       test_abort(channel, block);
+       }
+       return retval;
+}
+
+static errcode_t test_write_byte(io_channel channel, unsigned long offset,
+                              int count, const void *buf)
+{
+       struct test_private_data *data;
+       errcode_t       retval = 0;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct test_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+       if (data->real && data->real->manager->write_byte)
+               retval = io_channel_write_byte(data->real, offset, count, buf);
+       if (data->write_byte)
+               data->write_byte(offset, count, retval);
+       if (data->flags & TEST_FLAG_WRITE)
+               fprintf(data->outfile,
+                       "Test_io: write_byte(%lu, %d) returned %s\n",
+                       offset, count, retval ? error_message(retval) : "OK");
+       return retval;
+}
+
+/*
+ * Flush data buffers to disk.
+ */
+static errcode_t test_flush(io_channel channel)
+{
+       struct test_private_data *data;
+       errcode_t       retval = 0;
+       
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct test_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+       if (data->real)
+               retval = io_channel_flush(data->real);
+       
+       if (data->flags & TEST_FLAG_FLUSH)
+               fprintf(data->outfile, "Test_io: flush() returned %s\n",
+                       retval ? error_message(retval) : "OK");
+       
+       return retval;
+}
+
+static errcode_t test_set_option(io_channel channel, const char *option, 
+                                const char *arg)
+{
+       struct test_private_data *data;
+       errcode_t       retval = 0;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct test_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL);
+
+
+       if (data->flags & TEST_FLAG_SET_OPTION)
+               fprintf(data->outfile, "Test_io: set_option(%s, %s) ", 
+                       option, arg);
+       if (data->real && data->real->manager->set_option) {
+               retval = (data->real->manager->set_option)(data->real, 
+                                                          option, arg);
+               if (data->flags & TEST_FLAG_SET_OPTION)
+                       fprintf(data->outfile, "returned %s\n",
+                               retval ? error_message(retval) : "OK");
+       } else {
+               if (data->flags & TEST_FLAG_SET_OPTION)
+                       fprintf(data->outfile, "not implemented\n");
+       }
+       return retval;
+}
diff --git a/e2fsprogs/ext2fs/unix_io.c b/e2fsprogs/ext2fs/unix_io.c
new file mode 100644 (file)
index 0000000..5bc7a6a
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+ * unix_io.c --- This is the Unix (well, really POSIX) implementation
+ *     of the I/O manager.
+ *
+ * Implements a one-block write-through cache.
+ *
+ * Includes support for Windows NT support under Cygwin. 
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ *     2002 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#include <time.h>
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * For checking structure magic numbers...
+ */
+
+#define EXT2_CHECK_MAGIC(struct, code) \
+         if ((struct)->magic != (code)) return (code)
+
+struct unix_cache {
+       char            *buf;
+       unsigned long   block;
+       int             access_time;
+       unsigned        dirty:1;
+       unsigned        in_use:1;
+};
+
+#define CACHE_SIZE 8
+#define WRITE_DIRECT_SIZE 4    /* Must be smaller than CACHE_SIZE */
+#define READ_DIRECT_SIZE 4     /* Should be smaller than CACHE_SIZE */
+
+struct unix_private_data {
+       int     magic;
+       int     dev;
+       int     flags;
+       int     access_time;
+       ext2_loff_t offset;
+       struct unix_cache cache[CACHE_SIZE];
+};
+
+static errcode_t unix_open(const char *name, int flags, io_channel *channel);
+static errcode_t unix_close(io_channel channel);
+static errcode_t unix_set_blksize(io_channel channel, int blksize);
+static errcode_t unix_read_blk(io_channel channel, unsigned long block,
+                              int count, void *data);
+static errcode_t unix_write_blk(io_channel channel, unsigned long block,
+                               int count, const void *data);
+static errcode_t unix_flush(io_channel channel);
+static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
+                               int size, const void *data);
+static errcode_t unix_set_option(io_channel channel, const char *option, 
+                                const char *arg);
+
+static void reuse_cache(io_channel channel, struct unix_private_data *data,
+                struct unix_cache *cache, unsigned long block);
+
+/* __FreeBSD_kernel__ is defined by GNU/kFreeBSD - the FreeBSD kernel
+ * does not know buffered block devices - everything is raw. */
+#if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#define NEED_BOUNCE_BUFFER
+#else
+#undef NEED_BOUNCE_BUFFER
+#endif
+
+static struct struct_io_manager struct_unix_manager = {
+       EXT2_ET_MAGIC_IO_MANAGER,
+       "Unix I/O Manager",
+       unix_open,
+       unix_close,
+       unix_set_blksize,
+       unix_read_blk,
+       unix_write_blk,
+       unix_flush,
+#ifdef NEED_BOUNCE_BUFFER
+       0,
+#else
+       unix_write_byte,
+#endif
+       unix_set_option
+};
+
+io_manager unix_io_manager = &struct_unix_manager;
+
+/*
+ * Here are the raw I/O functions
+ */
+#ifndef NEED_BOUNCE_BUFFER
+static errcode_t raw_read_blk(io_channel channel,
+                             struct unix_private_data *data,
+                             unsigned long block,
+                             int count, void *buf)
+{
+       errcode_t       retval;
+       ssize_t         size;
+       ext2_loff_t     location;
+       int             actual = 0;
+
+       size = (count < 0) ? -count : count * channel->block_size;
+       location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+       if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
+               retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+               goto error_out;
+       }
+       actual = read(data->dev, buf, size);
+       if (actual != size) {
+               if (actual < 0)
+                       actual = 0;
+               retval = EXT2_ET_SHORT_READ;
+               goto error_out;
+       }
+       return 0;
+       
+error_out:
+       memset((char *) buf+actual, 0, size-actual);
+       if (channel->read_error)
+               retval = (channel->read_error)(channel, block, count, buf,
+                                              size, actual, retval);
+       return retval;
+}
+#else /* NEED_BOUNCE_BUFFER */
+/*
+ * Windows and FreeBSD block devices only allow sector alignment IO in offset and size
+ */
+static errcode_t raw_read_blk(io_channel channel,
+                             struct unix_private_data *data,
+                             unsigned long block,
+                             int count, void *buf)
+{
+       errcode_t       retval;
+       size_t          size, alignsize, fragment;
+       ext2_loff_t     location;
+       int             total = 0, actual;
+#define BLOCKALIGN 512
+       char            sector[BLOCKALIGN];
+
+       size = (count < 0) ? -count : count * channel->block_size;
+       location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+#ifdef DEBUG
+       printf("count=%d, size=%d, block=%d, blk_size=%d, location=%lx\n",
+                       count, size, block, channel->block_size, location);
+#endif
+       if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
+               retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+               goto error_out;
+       }
+       fragment = size % BLOCKALIGN;
+       alignsize = size - fragment;
+       if (alignsize) {
+               actual = read(data->dev, buf, alignsize);
+               if (actual != alignsize)
+                       goto short_read;
+       }
+       if (fragment) {
+               actual = read(data->dev, sector, BLOCKALIGN);
+               if (actual != BLOCKALIGN)
+                       goto short_read;
+               memcpy(buf+alignsize, sector, fragment);
+       }
+       return 0;
+
+short_read:
+       if (actual>0)
+               total += actual;
+       retval = EXT2_ET_SHORT_READ;
+
+error_out:
+       memset((char *) buf+total, 0, size-actual);
+       if (channel->read_error)
+               retval = (channel->read_error)(channel, block, count, buf,
+                                              size, actual, retval);
+       return retval;
+}
+#endif
+
+static errcode_t raw_write_blk(io_channel channel,
+                              struct unix_private_data *data,
+                              unsigned long block,
+                              int count, const void *buf)
+{
+       ssize_t         size;
+       ext2_loff_t     location;
+       int             actual = 0;
+       errcode_t       retval;
+
+       if (count == 1)
+               size = channel->block_size;
+       else {
+               if (count < 0)
+                       size = -count;
+               else
+                       size = count * channel->block_size;
+       }
+
+       location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+       if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
+               retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
+               goto error_out;
+       }
+       
+       actual = write(data->dev, buf, size);
+       if (actual != size) {
+               retval = EXT2_ET_SHORT_WRITE;
+               goto error_out;
+       }
+       return 0;
+       
+error_out:
+       if (channel->write_error)
+               retval = (channel->write_error)(channel, block, count, buf,
+                                               size, actual, retval);
+       return retval;
+}
+
+
+/*
+ * Here we implement the cache functions
+ */
+
+/* Allocate the cache buffers */
+static errcode_t alloc_cache(io_channel channel,
+                            struct unix_private_data *data)
+{
+       errcode_t               retval;
+       struct unix_cache       *cache;
+       int                     i;
+       
+       data->access_time = 0;
+       for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+               cache->block = 0;
+               cache->access_time = 0;
+               cache->dirty = 0;
+               cache->in_use = 0;
+               if ((retval = ext2fs_get_mem(channel->block_size,
+                                            &cache->buf)))
+                       return retval;
+       }
+       return 0;
+}
+
+/* Free the cache buffers */
+static void free_cache(struct unix_private_data *data)
+{
+       struct unix_cache       *cache;
+       int                     i;
+       
+       data->access_time = 0;
+       for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+               cache->block = 0;
+               cache->access_time = 0;
+               cache->dirty = 0;
+               cache->in_use = 0;
+               if (cache->buf)
+                       ext2fs_free_mem(&cache->buf);
+               cache->buf = 0;
+       }
+}
+
+#ifndef NO_IO_CACHE
+/*
+ * Try to find a block in the cache.  If the block is not found, and
+ * eldest is a non-zero pointer, then fill in eldest with the cache
+ * entry to that should be reused.
+ */
+static struct unix_cache *find_cached_block(struct unix_private_data *data,
+                                           unsigned long block,
+                                           struct unix_cache **eldest)
+{
+       struct unix_cache       *cache, *unused_cache, *oldest_cache;
+       int                     i;
+       
+       unused_cache = oldest_cache = 0;
+       for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+               if (!cache->in_use) {
+                       if (!unused_cache)
+                               unused_cache = cache;
+                       continue;
+               }
+               if (cache->block == block) {
+                       cache->access_time = ++data->access_time;
+                       return cache;
+               }
+               if (!oldest_cache ||
+                   (cache->access_time < oldest_cache->access_time))
+                       oldest_cache = cache;
+       }
+       if (eldest)
+               *eldest = (unused_cache) ? unused_cache : oldest_cache;
+       return 0;
+}
+
+/*
+ * Reuse a particular cache entry for another block.
+ */
+static void reuse_cache(io_channel channel, struct unix_private_data *data,
+                struct unix_cache *cache, unsigned long block)
+{
+       if (cache->dirty && cache->in_use)
+               raw_write_blk(channel, data, cache->block, 1, cache->buf);
+
+       cache->in_use = 1;
+       cache->dirty = 0;
+       cache->block = block;
+       cache->access_time = ++data->access_time;
+}
+
+/*
+ * Flush all of the blocks in the cache
+ */
+static errcode_t flush_cached_blocks(io_channel channel,
+                                    struct unix_private_data *data,
+                                    int invalidate)
+
+{
+       struct unix_cache       *cache;
+       errcode_t               retval, retval2;
+       int                     i;
+       
+       retval2 = 0;
+       for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+               if (!cache->in_use)
+                       continue;
+               
+               if (invalidate)
+                       cache->in_use = 0;
+               
+               if (!cache->dirty)
+                       continue;
+               
+               retval = raw_write_blk(channel, data,
+                                      cache->block, 1, cache->buf);
+               if (retval)
+                       retval2 = retval;
+               else
+                       cache->dirty = 0;
+       }
+       return retval2;
+}
+#endif /* NO_IO_CACHE */
+
+static errcode_t unix_open(const char *name, int flags, io_channel *channel)
+{
+       io_channel      io = NULL;
+       struct unix_private_data *data = NULL;
+       errcode_t       retval;
+       int             open_flags;
+       struct stat     st;
+#ifdef __linux__
+       struct          utsname ut;
+#endif
+
+       if (name == 0)
+               return EXT2_ET_BAD_DEVICE_NAME;
+       retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
+       if (retval)
+               return retval;
+       memset(io, 0, sizeof(struct struct_io_channel));
+       io->magic = EXT2_ET_MAGIC_IO_CHANNEL;
+       retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data);
+       if (retval)
+               goto cleanup;
+
+       io->manager = unix_io_manager;
+       retval = ext2fs_get_mem(strlen(name)+1, &io->name);
+       if (retval)
+               goto cleanup;
+
+       strcpy(io->name, name);
+       io->private_data = data;
+       io->block_size = 1024;
+       io->read_error = 0;
+       io->write_error = 0;
+       io->refcount = 1;
+
+       memset(data, 0, sizeof(struct unix_private_data));
+       data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
+
+       if ((retval = alloc_cache(io, data)))
+               goto cleanup;
+
+       open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
+#ifdef CONFIG_LFS
+       data->dev = open64(io->name, open_flags);
+#else
+       data->dev = open(io->name, open_flags);
+#endif
+       if (data->dev < 0) {
+               retval = errno;
+               goto cleanup;
+       }
+
+#ifdef __linux__
+#undef RLIM_INFINITY
+#if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4)))
+#define RLIM_INFINITY  ((unsigned long)(~0UL>>1))
+#else
+#define RLIM_INFINITY  (~0UL)
+#endif
+       /*
+        * Work around a bug in 2.4.10-2.4.18 kernels where writes to
+        * block devices are wrongly getting hit by the filesize
+        * limit.  This workaround isn't perfect, since it won't work
+        * if glibc wasn't built against 2.2 header files.  (Sigh.)
+        * 
+        */
+       if ((flags & IO_FLAG_RW) &&
+           (uname(&ut) == 0) &&
+           ((ut.release[0] == '2') && (ut.release[1] == '.') &&
+            (ut.release[2] == '4') && (ut.release[3] == '.') &&
+            (ut.release[4] == '1') && (ut.release[5] >= '0') &&
+            (ut.release[5] < '8')) &&
+           (fstat(data->dev, &st) == 0) &&
+           (S_ISBLK(st.st_mode))) {
+               struct rlimit   rlim;
+               
+               rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY;
+               setrlimit(RLIMIT_FSIZE, &rlim);
+               getrlimit(RLIMIT_FSIZE, &rlim);
+               if (((unsigned long) rlim.rlim_cur) <
+                   ((unsigned long) rlim.rlim_max)) {
+                       rlim.rlim_cur = rlim.rlim_max;
+                       setrlimit(RLIMIT_FSIZE, &rlim);
+               }
+       }
+#endif
+       *channel = io;
+       return 0;
+
+cleanup:
+       if (data) {
+               free_cache(data);
+               ext2fs_free_mem(&data);
+       }
+       if (io)
+               ext2fs_free_mem(&io);
+       return retval;
+}
+
+static errcode_t unix_close(io_channel channel)
+{
+       struct unix_private_data *data;
+       errcode_t       retval = 0;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct unix_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+       if (--channel->refcount > 0)
+               return 0;
+
+#ifndef NO_IO_CACHE
+       retval = flush_cached_blocks(channel, data, 0);
+#endif
+
+       if (close(data->dev) < 0)
+               retval = errno;
+       free_cache(data);
+
+       ext2fs_free_mem(&channel->private_data);
+       if (channel->name)
+               ext2fs_free_mem(&channel->name);
+       ext2fs_free_mem(&channel);
+       return retval;
+}
+
+static errcode_t unix_set_blksize(io_channel channel, int blksize)
+{
+       struct unix_private_data *data;
+       errcode_t               retval;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct unix_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+       if (channel->block_size != blksize) {
+#ifndef NO_IO_CACHE
+               if ((retval = flush_cached_blocks(channel, data, 0)))
+                       return retval;
+#endif
+               
+               channel->block_size = blksize;
+               free_cache(data);
+               if ((retval = alloc_cache(channel, data)))
+                       return retval;
+       }
+       return 0;
+}
+
+
+static errcode_t unix_read_blk(io_channel channel, unsigned long block,
+                              int count, void *buf)
+{
+       struct unix_private_data *data;
+       struct unix_cache *cache, *reuse[READ_DIRECT_SIZE];
+       errcode_t       retval;
+       char            *cp;
+       int             i, j;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct unix_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifdef NO_IO_CACHE
+       return raw_read_blk(channel, data, block, count, buf);
+#else
+       /*
+        * If we're doing an odd-sized read or a very large read,
+        * flush out the cache and then do a direct read.
+        */
+       if (count < 0 || count > WRITE_DIRECT_SIZE) {
+               if ((retval = flush_cached_blocks(channel, data, 0)))
+                       return retval;
+               return raw_read_blk(channel, data, block, count, buf);
+       }
+
+       cp = buf;
+       while (count > 0) {
+               /* If it's in the cache, use it! */
+               if ((cache = find_cached_block(data, block, &reuse[0]))) {
+#ifdef DEBUG
+                       printf("Using cached block %d\n", block);
+#endif
+                       memcpy(cp, cache->buf, channel->block_size);
+                       count--;
+                       block++;
+                       cp += channel->block_size;
+                       continue;
+               }
+               /*
+                * Find the number of uncached blocks so we can do a
+                * single read request
+                */
+               for (i=1; i < count; i++)
+                       if (find_cached_block(data, block+i, &reuse[i]))
+                               break;
+#ifdef DEBUG
+               printf("Reading %d blocks starting at %d\n", i, block);
+#endif
+               if ((retval = raw_read_blk(channel, data, block, i, cp)))
+                       return retval;
+               
+               /* Save the results in the cache */
+               for (j=0; j < i; j++) {
+                       count--;
+                       cache = reuse[j];
+                       reuse_cache(channel, data, cache, block++);
+                       memcpy(cache->buf, cp, channel->block_size);
+                       cp += channel->block_size;
+               }
+       }
+       return 0;
+#endif /* NO_IO_CACHE */
+}
+
+static errcode_t unix_write_blk(io_channel channel, unsigned long block,
+                               int count, const void *buf)
+{
+       struct unix_private_data *data;
+       struct unix_cache *cache, *reuse;
+       errcode_t       retval = 0;
+       const char      *cp;
+       int             writethrough;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct unix_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifdef NO_IO_CACHE
+       return raw_write_blk(channel, data, block, count, buf);
+#else  
+       /*
+        * If we're doing an odd-sized write or a very large write,
+        * flush out the cache completely and then do a direct write.
+        */
+       if (count < 0 || count > WRITE_DIRECT_SIZE) {
+               if ((retval = flush_cached_blocks(channel, data, 1)))
+                       return retval;
+               return raw_write_blk(channel, data, block, count, buf);
+       }
+
+       /*
+        * For a moderate-sized multi-block write, first force a write
+        * if we're in write-through cache mode, and then fill the
+        * cache with the blocks.
+        */
+       writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH;
+       if (writethrough)
+               retval = raw_write_blk(channel, data, block, count, buf);
+       
+       cp = buf;
+       while (count > 0) {
+               cache = find_cached_block(data, block, &reuse);
+               if (!cache) {
+                       cache = reuse;
+                       reuse_cache(channel, data, cache, block);
+               }
+               memcpy(cache->buf, cp, channel->block_size);
+               cache->dirty = !writethrough;
+               count--;
+               block++;
+               cp += channel->block_size;
+       }
+       return retval;
+#endif /* NO_IO_CACHE */
+}
+
+static errcode_t unix_write_byte(io_channel channel, unsigned long offset,
+                                int size, const void *buf)
+{
+       struct unix_private_data *data;
+       errcode_t       retval = 0;
+       ssize_t         actual;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct unix_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifndef NO_IO_CACHE
+       /*
+        * Flush out the cache completely
+        */
+       if ((retval = flush_cached_blocks(channel, data, 1)))
+               return retval;
+#endif
+
+       if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0)
+               return errno;
+       
+       actual = write(data->dev, buf, size);
+       if (actual != size)
+               return EXT2_ET_SHORT_WRITE;
+
+       return 0;
+}
+
+/*
+ * Flush data buffers to disk.  
+ */
+static errcode_t unix_flush(io_channel channel)
+{
+       struct unix_private_data *data;
+       errcode_t retval = 0;
+       
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct unix_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+#ifndef NO_IO_CACHE
+       retval = flush_cached_blocks(channel, data, 0);
+#endif
+       fsync(data->dev);
+       return retval;
+}
+
+static errcode_t unix_set_option(io_channel channel, const char *option, 
+                                const char *arg)
+{
+       struct unix_private_data *data;
+       unsigned long tmp;
+       char *end;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct unix_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+       if (!strcmp(option, "offset")) {
+               if (!arg)
+                       return EXT2_ET_INVALID_ARGUMENT;
+
+               tmp = strtoul(arg, &end, 0);
+               if (*end)
+                       return EXT2_ET_INVALID_ARGUMENT;
+               data->offset = tmp;
+               return 0;
+       }
+       return EXT2_ET_INVALID_ARGUMENT;
+}
diff --git a/e2fsprogs/ext2fs/unlink.c b/e2fsprogs/ext2fs/unlink.c
new file mode 100644 (file)
index 0000000..e7b2182
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * unlink.c --- delete links in a ext2fs directory
+ * 
+ * Copyright (C) 1993, 1994, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+struct link_struct  {
+       const char      *name;
+       int             namelen;
+       ext2_ino_t      inode;
+       int             flags;
+       struct ext2_dir_entry *prev;
+       int             done;
+};     
+
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+static int unlink_proc(struct ext2_dir_entry *dirent,
+                    int        offset EXT2FS_ATTR((unused)),
+                    int        blocksize EXT2FS_ATTR((unused)),
+                    char       *buf EXT2FS_ATTR((unused)),
+                    void       *priv_data)
+{
+       struct link_struct *ls = (struct link_struct *) priv_data;
+       struct ext2_dir_entry *prev;
+
+       prev = ls->prev;
+       ls->prev = dirent;
+
+       if (ls->name) {
+               if ((dirent->name_len & 0xFF) != ls->namelen)
+                       return 0;
+               if (strncmp(ls->name, dirent->name, dirent->name_len & 0xFF))
+                       return 0;
+       }
+       if (ls->inode) {
+               if (dirent->inode != ls->inode)
+                       return 0;
+       } else {
+               if (!dirent->inode)
+                       return 0;
+       }
+
+       if (prev) 
+               prev->rec_len += dirent->rec_len;
+       else
+               dirent->inode = 0;
+       ls->done++;
+       return DIRENT_ABORT|DIRENT_CHANGED;
+}
+
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir,
+                       const char *name, ext2_ino_t ino,
+                       int flags EXT2FS_ATTR((unused)))
+{
+       errcode_t       retval;
+       struct link_struct ls;
+
+       EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
+
+       if (!name && !ino)
+               return EXT2_ET_INVALID_ARGUMENT;
+
+       if (!(fs->flags & EXT2_FLAG_RW))
+               return EXT2_ET_RO_FILSYS;
+
+       ls.name = name;
+       ls.namelen = name ? strlen(name) : 0;
+       ls.inode = ino;
+       ls.flags = 0;
+       ls.done = 0;
+       ls.prev = 0;
+
+       retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, 
+                                   0, unlink_proc, &ls);
+       if (retval)
+               return retval;
+
+       return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE;
+}
+
diff --git a/e2fsprogs/ext2fs/valid_blk.c b/e2fsprogs/ext2fs/valid_blk.c
new file mode 100644 (file)
index 0000000..29ff27a
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * valid_blk.c --- does the inode have valid blocks?
+ *
+ * Copyright 1997 by Theodore Ts'o
+ * 
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ * 
+ */
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+/*
+ * This function returns 1 if the inode's block entries actually
+ * contain block entries.
+ */
+int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode)
+{
+       /*
+        * Only directories, regular files, and some symbolic links
+        * have valid block entries.
+        */
+       if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) &&
+           !LINUX_S_ISLNK(inode->i_mode))
+               return 0;
+       
+       /*
+        * If the symbolic link is a "fast symlink", then the symlink
+        * target is stored in the block entries.
+        */
+       if (LINUX_S_ISLNK (inode->i_mode)) {
+               if (inode->i_file_acl == 0) {
+                       /* With no EA block, we can rely on i_blocks */
+                       if (inode->i_blocks == 0)
+                               return 0;
+               } else {
+                       /* With an EA block, life gets more tricky */
+                       if (inode->i_size >= EXT2_N_BLOCKS*4)
+                               return 1; /* definitely using i_block[] */
+                       if (inode->i_size > 4 && inode->i_block[1] == 0)
+                               return 1; /* definitely using i_block[] */
+                       return 0; /* Probably a fast symlink */
+               }
+       }
+       return 1;
+}
diff --git a/e2fsprogs/ext2fs/version.c b/e2fsprogs/ext2fs/version.c
new file mode 100644 (file)
index 0000000..f7026af
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * version.c --- Return the version of the ext2 library
+ *
+ * Copyright (C) 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+//#include "../../version.h"
+
+static const char *lib_version = E2FSPROGS_VERSION;
+static const char *lib_date = E2FSPROGS_DATE;
+
+int ext2fs_parse_version_string(const char *ver_string)
+{
+       const char *cp;
+       int version = 0;
+
+       for (cp = ver_string; *cp; cp++) {
+               if (*cp == '.')
+                       continue;
+               if (!isdigit(*cp))
+                       break;
+               version = (version * 10) + (*cp - '0');
+       }
+       return version;
+}
+
+
+int ext2fs_get_library_version(const char **ver_string,
+                              const char **date_string)
+{
+       if (ver_string)
+               *ver_string = lib_version;
+       if (date_string)
+               *date_string = lib_date;
+
+       return ext2fs_parse_version_string(lib_version);
+}
diff --git a/e2fsprogs/ext2fs/write_bb_file.c b/e2fsprogs/ext2fs/write_bb_file.c
new file mode 100644 (file)
index 0000000..269b576
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * write_bb_file.c --- write a list of bad blocks to a FILE *
+ *
+ * Copyright (C) 1994, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+
+#include "ext2_fs.h"
+#include "ext2fs.h"
+
+errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list,
+                              unsigned int flags EXT2FS_ATTR((unused)),
+                              FILE *f)
+{
+       badblocks_iterate       bb_iter;
+       blk_t                   blk;
+       errcode_t               retval;
+
+       retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter);
+       if (retval)
+               return retval;
+
+       while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) {
+               fprintf(f, "%d\n", blk);
+       }
+       ext2fs_badblocks_list_iterate_end(bb_iter);
+       return 0;
+}