|  | /* vi: set sw=4 ts=4: */ | 
|  | /* | 
|  | * 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); | 
|  | } |