Add new inode I/O abstraction interface which exports an inode as
an I/O object.

Export ext2_file_flush as a public interface.

Also minor cleanups to tighten code in other I/O abstractions, and to
mark a void * pointer as const in the ext2_file_write interface.

diff --git a/lib/ext2fs/ChangeLog b/lib/ext2fs/ChangeLog
index f229390..e32db2d 100644
--- a/lib/ext2fs/ChangeLog
+++ b/lib/ext2fs/ChangeLog
@@ -1,3 +1,14 @@
+2002-02-20  Theodore Tso  <tytso@valinux.com>
+
+	* Makefile.in, inode_io.c, ext2fs.h, ext2_err.et.in: Add new io
+		abstraction interface which exports an ext2 inode.
+
+	* fileio.c (ext2fs_file_flush): Export ext2fs_file_flush as a
+		public interface.
+
+	* test_io.c (test_close), unix_io.c (unix_close): Remove unneeded
+		conditional; save a few bytes.
+	
 2002-02-12  Theodore Tso  <tytso@valinux.com>
 
 	* Makefile.in (tst_badblocks): Add some extra .o files when
diff --git a/lib/ext2fs/Makefile.in b/lib/ext2fs/Makefile.in
index 2c2c94c..54dc160 100644
--- a/lib/ext2fs/Makefile.in
+++ b/lib/ext2fs/Makefile.in
@@ -43,6 +43,7 @@
 	initialize.o \
 	inline.o \
 	inode.o \
+	inode_io.o \
 	ismounted.o \
 	link.o \
 	llseek.o \
@@ -94,6 +95,7 @@
 	$(srcdir)/initialize.c \
 	$(srcdir)/inline.c \
 	$(srcdir)/inode.c \
+	$(srcdir)/inode_io.c \
 	$(srcdir)/ismounted.c \
 	$(srcdir)/link.c \
 	$(srcdir)/llseek.c \
@@ -377,6 +379,10 @@
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
  $(srcdir)/ext2fs.h $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
  $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/bitops.h $(srcdir)/e2image.h
+inode_io.o: $(srcdir)/inode_io.c $(srcdir)/ext2_fs.h \
+ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
+ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/bitops.h
 ismounted.o: $(srcdir)/ismounted.c $(srcdir)/ext2_fs.h \
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
  $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \
diff --git a/lib/ext2fs/ext2_err.et.in b/lib/ext2fs/ext2_err.et.in
index 235f75d..ab36c9d 100644
--- a/lib/ext2fs/ext2_err.et.in
+++ b/lib/ext2fs/ext2_err.et.in
@@ -59,8 +59,8 @@
 ec	EXT2_ET_MAGIC_E2IMAGE,
 	"Wrong magic number for Ext2 Image Header"
 
-ec	EXT2_ET_MAGIC_RESERVED_8,
-	"Wrong magic number --- RESERVED_8"
+ec	EXT2_ET_MAGIC_INODE_IO_CHANNEL,
+	"Wrong magic number for inode io_channel structure"
 
 ec	EXT2_ET_MAGIC_RESERVED_9,
 	"Wrong magic number --- RESERVED_9"
@@ -272,5 +272,8 @@
 ec	EXT2_ET_LOAD_EXT_JOURNAL,
 	"Error loading external journal"
 
+ec	EXT2_ET_NO_JOURNAL,
+	"Journal not found"
+
 	end
 
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 75512d1..e37b817 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -638,9 +638,10 @@
 				  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, void *buf,
+extern errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
 				   unsigned int nbytes, unsigned int *written);
 extern errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset,
 				   int whence, ext2_off_t *ret_pos);
@@ -678,6 +679,23 @@
 				   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, int size,
+				       ext2_icount_t hint, ext2_icount_t *ret);
+extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, 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_open_inode_scan(ext2_filsys fs, int buffer_blocks,
@@ -703,23 +721,9 @@
 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);
 
-/* icount.c */
-extern void ext2fs_free_icount(ext2_icount_t icount);
-extern errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, int size,
-				       ext2_icount_t hint, ext2_icount_t *ret);
-extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, 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_io.c */
+extern io_manager inode_io_manager;
+	
 /* 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,
diff --git a/lib/ext2fs/fileio.c b/lib/ext2fs/fileio.c
index e2f4795..f0b216e 100644
--- a/lib/ext2fs/fileio.c
+++ b/lib/ext2fs/fileio.c
@@ -88,7 +88,7 @@
  * This function flushes the dirty block buffer out to disk if
  * necessary.
  */
-static errcode_t ext2fs_file_flush(ext2_file_t file)
+errcode_t ext2fs_file_flush(ext2_file_t file)
 {
 	errcode_t	retval;
 	ext2_filsys fs;
@@ -235,7 +235,7 @@
 }
 
 
-errcode_t ext2fs_file_write(ext2_file_t file, void *buf,
+errcode_t ext2fs_file_write(ext2_file_t file, const void *buf,
 			    unsigned int nbytes, unsigned int *written)
 {
 	ext2_filsys	fs;
diff --git a/lib/ext2fs/inode_io.c b/lib/ext2fs/inode_io.c
new file mode 100644
index 0000000..bb72403
--- /dev/null
+++ b/lib/ext2fs/inode_io.c
@@ -0,0 +1,264 @@
+/*
+ * 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;
+	int				flags;
+	struct inode_private_data	*next;
+};
+
+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_intern(ext2_filsys fs, ext2_ino_t ino,
+				 char **name)
+{
+	struct inode_private_data 	*data;
+	errcode_t			retval;
+
+	if ((retval = ext2fs_get_mem(sizeof(struct inode_private_data),
+				     (void **) &data)))
+		return retval;
+	data->magic = EXT2_ET_MAGIC_INODE_IO_CHANNEL;
+	sprintf(data->name, "%lu:%d", ino, ino_unique++);
+	data->file = 0;
+	data->fs = fs;
+	data->ino = ino;
+	data->flags = 0;
+	data->next = top_intern;
+	top_intern = data;
+	*name = data->name;
+	return 0;
+}
+
+
+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),
+				(void **) &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, (void **) &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_open(data->fs, data->ino, open_flags,
+				  &data->file);
+	if (retval)
+		goto cleanup;
+		
+	*channel = io;
+	return 0;
+
+cleanup:
+	if (data) {
+		ext2fs_free_mem((void **) &data);
+	}
+	if (io)
+		ext2fs_free_mem((void **) &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((void **) &channel->private_data);
+	if (channel->name)
+		ext2fs_free_mem((void **) &channel->name);
+	ext2fs_free_mem((void **) &channel);
+	return retval;
+}
+
+static errcode_t inode_set_blksize(io_channel channel, int blksize)
+{
+	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);
+
+	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;
+	struct inode_cache *cache;
+	errcode_t	retval;
+	char		*cp;
+	int		i, j;
+
+	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;
+	struct inode_cache *cache;
+	errcode_t	retval = 0, retval2;
+	const char	*cp;
+	int		writethrough;
+
+	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;
+	size_t		actual;
+
+	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;
+	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);
+
+	return ext2fs_file_flush(data->file);
+}
+
diff --git a/lib/ext2fs/test_io.c b/lib/ext2fs/test_io.c
index b259d0f..baca786 100644
--- a/lib/ext2fs/test_io.c
+++ b/lib/ext2fs/test_io.c
@@ -154,8 +154,7 @@
 	if (data->real)
 		retval = io_channel_close(data->real);
 	
-	if (channel->private_data)
-		ext2fs_free_mem((void **) &channel->private_data);
+	ext2fs_free_mem((void **) &channel->private_data);
 	if (channel->name)
 		ext2fs_free_mem((void **) &channel->name);
 	ext2fs_free_mem((void **) &channel);
diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
index 7fa4b1a..0f993e4 100644
--- a/lib/ext2fs/unix_io.c
+++ b/lib/ext2fs/unix_io.c
@@ -385,8 +385,8 @@
 	if (close(data->dev) < 0)
 		retval = errno;
 	free_cache(channel, data);
-	if (channel->private_data)
-		ext2fs_free_mem((void **) &channel->private_data);
+
+	ext2fs_free_mem((void **) &channel->private_data);
 	if (channel->name)
 		ext2fs_free_mem((void **) &channel->name);
 	ext2fs_free_mem((void **) &channel);