libext2fs: add unixfd_io_manager

This new manager is similar to the unix_io_manager except it takes a
file descriptor as first argument instead of a filename.

Some programs may want libext2fs to directly use a fd instead of
letting it opening the file.
The use case for such a io_manager would be to let programs use
a fd even if the filename is unknown:
  - the fd comes from a temporary file (O_TMPFILE);
  - the fd comes from a unix socket...

Refactoring unix_open() also fix a bug when the IO_DIRECT flag was
specified: ext2fs_get_dio_alignment() was called before the file was
actually opened, resulting in an alignment of 0.

Signed-off-by: Adrien Schildknecht <adriens@google.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
next
Adrien Schildknecht 2016-08-10 17:08:16 -04:00 committed by Theodore Ts'o
parent d4e5abfb1b
commit 4ccf9e4fe1
2 changed files with 89 additions and 36 deletions

View File

@ -138,6 +138,7 @@ extern errcode_t io_channel_cache_readahead(io_channel io,
/* unix_io.c */
extern io_manager unix_io_manager;
extern io_manager unixfd_io_manager;
/* undo_io.c */
extern io_manager undo_io_manager;

View File

@ -482,20 +482,19 @@ int ext2fs_fstat(int fd, ext2fs_struct_stat *buf)
#endif
}
static errcode_t unix_open(const char *name, int flags, io_channel *channel)
static errcode_t unix_open_channel(const char *name, int fd,
int flags, io_channel *channel,
io_manager io_mgr)
{
io_channel io = NULL;
struct unix_private_data *data = NULL;
errcode_t retval;
int open_flags;
int f_nocache = 0;
ext2fs_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)
goto cleanup;
@ -505,7 +504,7 @@ static errcode_t unix_open(const char *name, int flags, io_channel *channel)
if (retval)
goto cleanup;
io->manager = unix_io_manager;
io->manager = io_mgr;
retval = ext2fs_get_mem(strlen(name)+1, &io->name);
if (retval)
goto cleanup;
@ -520,35 +519,16 @@ static errcode_t unix_open(const char *name, int flags, io_channel *channel)
memset(data, 0, sizeof(struct unix_private_data));
data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL;
data->io_stats.num_fields = 2;
data->dev = -1;
open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
if (flags & IO_FLAG_EXCLUSIVE)
open_flags |= O_EXCL;
#if defined(O_DIRECT)
if (flags & IO_FLAG_DIRECT_IO) {
open_flags |= O_DIRECT;
io->align = ext2fs_get_dio_alignment(data->dev);
}
#elif defined(F_NOCACHE)
if (flags & IO_FLAG_DIRECT_IO) {
f_nocache = F_NOCACHE;
io->align = 4096;
}
#endif
data->flags = flags;
data->dev = fd;
data->dev = ext2fs_open_file(io->name, open_flags, 0);
if (data->dev < 0) {
retval = errno;
goto cleanup;
}
if (f_nocache) {
if (fcntl(data->dev, f_nocache, 1) < 0) {
retval = errno;
goto cleanup;
}
}
#if defined(O_DIRECT)
if (flags & IO_FLAG_DIRECT_IO)
io->align = ext2fs_get_dio_alignment(data->dev);
#elif defined(F_NOCACHE)
if (flags & IO_FLAG_DIRECT_IO)
io->align = 4096;
#endif
/*
* If the device is really a block device, then set the
@ -557,7 +537,7 @@ static errcode_t unix_open(const char *name, int flags, io_channel *channel)
* and if it succeed, subsequent read from sparse area returns
* zero.
*/
if (ext2fs_stat(io->name, &st) == 0) {
if (ext2fs_fstat(data->dev, &st) == 0) {
if (S_ISBLK(st.st_mode))
io->flags |= CHANNEL_FLAGS_BLOCK_DEVICE;
else
@ -620,7 +600,7 @@ static errcode_t unix_open(const char *name, int flags, io_channel *channel)
(ut.release[2] == '4') && (ut.release[3] == '.') &&
(ut.release[4] == '1') && (ut.release[5] >= '0') &&
(ut.release[5] < '8')) &&
(ext2fs_stat(io->name, &st) == 0) &&
(ext2fs_fstat(data->dev, &st) == 0) &&
(S_ISBLK(st.st_mode))) {
struct rlimit rlim;
@ -653,6 +633,58 @@ cleanup:
return retval;
}
static errcode_t unixfd_open(const char *str_fd, int flags,
io_channel *channel)
{
int fd;
int fd_flags;
fd = atoi(str_fd);
fd_flags = fcntl(fd, F_GETFD);
if (fd_flags == -1)
return -EBADF;
flags = 0;
if (fd_flags & O_RDWR)
flags |= IO_FLAG_RW;
if (fd_flags & O_EXCL)
flags |= IO_FLAG_EXCLUSIVE;
#if defined(O_DIRECT)
if (fd_flags & O_DIRECT)
flags |= IO_FLAG_DIRECT_IO;
#endif
return unix_open_channel(str_fd, fd, flags, channel, unixfd_io_manager);
}
static errcode_t unix_open(const char *name, int flags,
io_channel *channel)
{
int fd = -1;
int open_flags;
if (name == 0)
return EXT2_ET_BAD_DEVICE_NAME;
open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY;
if (flags & IO_FLAG_EXCLUSIVE)
open_flags |= O_EXCL;
#if defined(O_DIRECT)
if (flags & IO_FLAG_DIRECT_IO)
open_flags |= O_DIRECT;
#endif
fd = ext2fs_open_file(name, open_flags, 0);
if (fd < 0)
return errno;
#if defined(F_NOCACHE) && !defined(IO_DIRECT)
if (flags & IO_FLAG_DIRECT_IO) {
if (fcntl(fd, F_NOCACHE, 1) < 0)
return errno;
}
#endif
return unix_open_channel(name, fd, flags, channel, unix_io_manager);
}
static errcode_t unix_close(io_channel channel)
{
struct unix_private_data *data;
@ -703,7 +735,6 @@ static errcode_t unix_set_blksize(io_channel channel, int blksize)
return 0;
}
static errcode_t unix_read_blk64(io_channel channel, unsigned long long block,
int count, void *buf)
{
@ -1089,3 +1120,24 @@ static struct struct_io_manager struct_unix_manager = {
};
io_manager unix_io_manager = &struct_unix_manager;
static struct struct_io_manager struct_unixfd_manager = {
.magic = EXT2_ET_MAGIC_IO_MANAGER,
.name = "Unix fd I/O Manager",
.open = unixfd_open,
.close = unix_close,
.set_blksize = unix_set_blksize,
.read_blk = unix_read_blk,
.write_blk = unix_write_blk,
.flush = unix_flush,
.write_byte = unix_write_byte,
.set_option = unix_set_option,
.get_stats = unix_get_stats,
.read_blk64 = unix_read_blk64,
.write_blk64 = unix_write_blk64,
.discard = unix_discard,
.cache_readahead = unix_cache_readahead,
.zeroout = unix_zeroout,
};
io_manager unixfd_io_manager = &struct_unixfd_manager;