/* * 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 Library * General Public License, version 2. * %End-Header% */ #include "config.h" #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 errcode_t inode_read_blk64(io_channel channel, unsigned long long block, int count, void *data); static errcode_t inode_write_blk64(io_channel channel, unsigned long long block, int count, const void *data); static struct struct_io_manager struct_inode_manager = { .magic = EXT2_ET_MAGIC_IO_MANAGER, .name = "Inode I/O Manager", .open = inode_open, .close = inode_close, .set_blksize = inode_set_blksize, .read_blk = inode_read_blk, .write_blk = inode_write_blk, .flush = inode_flush, .write_byte = inode_write_byte, .read_blk64 = inode_read_blk64, .write_blk64 = inode_write_blk64 }; 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 (io && io->name) ext2fs_free_mem(&io->name); 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_blk64(io_channel channel, unsigned long 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_read_blk(io_channel channel, unsigned long block, int count, void *buf) { return inode_read_blk64(channel, block, count, buf); } static errcode_t inode_write_blk64(io_channel channel, unsigned long 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_blk(io_channel channel, unsigned long block, int count, const void *buf) { return inode_write_blk64(channel, block, count, buf); } 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); }