#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
+#ifndef _GNU_SOURCE
#define _GNU_SOURCE
+#endif
+#include "config.h"
#include <stdio.h>
#include <string.h>
#if HAVE_UNISTD_H
#if HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
+#if HAVE_LINUX_FALLOC_H
+#include <linux/falloc.h>
+#endif
#if defined(__linux__) && defined(_IO) && !defined(BLKROGET)
#define BLKROGET _IO(0x12, 94) /* Get read-only status (0 = read_write). */
static errcode_t raw_read_blk(io_channel channel,
struct unix_private_data *data,
unsigned long long block,
- int count, void *buf)
+ int count, void *bufv)
{
errcode_t retval;
ssize_t size;
ext2_loff_t location;
int actual = 0;
+ unsigned char *buf = bufv;
size = (count < 0) ? -count : count * channel->block_size;
data->io_stats.bytes_read += size;
static errcode_t raw_write_blk(io_channel channel,
struct unix_private_data *data,
unsigned long long block,
- int count, const void *buf)
+ int count, const void *bufv)
{
ssize_t size;
ext2_loff_t location;
int actual = 0;
errcode_t retval;
+ const unsigned char *buf = bufv;
if (count == 1)
size = channel->block_size;
}
#endif /* NO_IO_CACHE */
+#ifdef __linux__
+#ifndef BLKDISCARDZEROES
+#define BLKDISCARDZEROES _IO(0x12,124)
+#endif
+#endif
+
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;
+ int open_flags, zeroes = 0;
+ int f_nocache = 0;
+ ext2fs_struct_stat st;
#ifdef __linux__
struct utsname ut;
#endif
return EXT2_ET_BAD_DEVICE_NAME;
retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io);
if (retval)
- return retval;
+ goto cleanup;
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);
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;
+#elif defined(F_NOCACHE)
+ if (flags & IO_FLAG_DIRECT_IO)
+ f_nocache = F_NOCACHE;
+#endif
data->flags = flags;
-#ifdef HAVE_OPEN64
- data->dev = open64(io->name, open_flags);
-#else
- data->dev = open(io->name, open_flags);
-#endif
+ 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 the device is really a block device, then set the
+ * appropriate flag, otherwise we can set DISCARD_ZEROES flag
+ * because we are going to use punch hole instead of discard
+ * and if it succeed, subsequent read from sparse area returns
+ * zero.
+ */
+ if (ext2fs_stat(io->name, &st) == 0) {
+ if (S_ISBLK(st.st_mode))
+ io->flags |= CHANNEL_FLAGS_BLOCK_DEVICE;
+ else
+ io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES;
+ }
#ifdef BLKSSZGET
if (flags & IO_FLAG_DIRECT_IO) {
}
#endif
+#ifdef BLKDISCARDZEROES
+ ioctl(data->dev, BLKDISCARDZEROES, &zeroes);
+ if (zeroes)
+ io->flags |= CHANNEL_FLAGS_DISCARD_ZEROES;
+#endif
+
#if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
/*
* Some operating systems require that the buffers be aligned,
(ut.release[2] == '4') && (ut.release[3] == '.') &&
(ut.release[4] == '1') && (ut.release[5] >= '0') &&
(ut.release[5] < '8')) &&
- (fstat(data->dev, &st) == 0) &&
+ (ext2fs_stat(io->name, &st) == 0) &&
(S_ISBLK(st.st_mode))) {
struct rlimit rlim;
}
#if defined(__linux__) && !defined(BLKDISCARD)
-#define BLKDISCARD _IO(0x12,119)
+#define BLKDISCARD _IO(0x12,119)
#endif
static errcode_t unix_discard(io_channel channel, unsigned long long block,
unsigned long long count)
{
-#ifdef BLKDISCARD
struct unix_private_data *data;
__uint64_t range[2];
int ret;
data = (struct unix_private_data *) channel->private_data;
EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
- range[0] = (__uint64_t)(block) * channel->block_size;
- range[1] = (__uint64_t)(count) * channel->block_size;
+ if (channel->flags & CHANNEL_FLAGS_BLOCK_DEVICE) {
+#ifdef BLKDISCARD
+ range[0] = (__uint64_t)(block) * channel->block_size;
+ range[1] = (__uint64_t)(count) * channel->block_size;
- ret = ioctl(data->dev, BLKDISCARD, &range);
- if (ret < 0)
+ ret = ioctl(data->dev, BLKDISCARD, &range);
+#else
+ goto unimplemented;
+#endif
+ } else {
+#if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_PUNCH_HOLE)
+ /*
+ * If we are not on block device, try to use punch hole
+ * to reclaim free space.
+ */
+ ret = fallocate(data->dev,
+ FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+ (off_t)(block) * channel->block_size,
+ (off_t)(count) * channel->block_size);
+#else
+ goto unimplemented;
+#endif
+ }
+ if (ret < 0) {
+ if (errno == EOPNOTSUPP)
+ goto unimplemented;
return errno;
+ }
return 0;
-#else
+unimplemented:
return EXT2_ET_UNIMPLEMENTED;
-#endif
}