Whamcloud - gitweb
Add fallocate.c to lib/exte2fs/Android.mk
[tools/e2fsprogs.git] / lib / ext2fs / unix_io.c
index c3185b6..3fce5c0 100644 (file)
@@ -15,6 +15,9 @@
  * %End-Header%
  */
 
+#define _XOPEN_SOURCE 600
+#define _DARWIN_C_SOURCE
+#define _FILE_OFFSET_BITS 64
 #define _LARGEFILE_SOURCE
 #define _LARGEFILE64_SOURCE
 #ifndef _GNU_SOURCE
@@ -35,6 +38,9 @@
 #ifdef __linux__
 #include <sys/utsname.h>
 #endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
 #ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
 #endif
@@ -44,9 +50,6 @@
 #if HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
 #if HAVE_SYS_RESOURCE_H
 #include <sys/resource.h>
 #endif
@@ -130,6 +133,28 @@ static errcode_t raw_read_blk(io_channel channel,
        size = (count < 0) ? -count : count * channel->block_size;
        data->io_stats.bytes_read += size;
        location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+
+#ifdef HAVE_PREAD64
+       /* Try an aligned pread */
+       if ((channel->align == 0) ||
+           (IS_ALIGNED(buf, channel->align) &&
+            IS_ALIGNED(size, channel->align))) {
+               actual = pread64(data->dev, buf, size, location);
+               if (actual == size)
+                       return 0;
+       }
+#elif HAVE_PREAD
+       /* Try an aligned pread */
+       if ((sizeof(off_t) >= sizeof(ext2_loff_t)) &&
+           ((channel->align == 0) ||
+            (IS_ALIGNED(buf, channel->align) &&
+             IS_ALIGNED(size, channel->align)))) {
+               actual = pread(data->dev, buf, size, location);
+               if (actual == size)
+                       return 0;
+       }
+#endif /* HAVE_PREAD */
+
        if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
                retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
                goto error_out;
@@ -200,6 +225,28 @@ static errcode_t raw_write_blk(io_channel channel,
        data->io_stats.bytes_written += size;
 
        location = ((ext2_loff_t) block * channel->block_size) + data->offset;
+
+#ifdef HAVE_PWRITE64
+       /* Try an aligned pwrite */
+       if ((channel->align == 0) ||
+           (IS_ALIGNED(buf, channel->align) &&
+            IS_ALIGNED(size, channel->align))) {
+               actual = pwrite64(data->dev, buf, size, location);
+               if (actual == size)
+                       return 0;
+       }
+#elif HAVE_PWRITE
+       /* Try an aligned pwrite */
+       if ((sizeof(off_t) >= sizeof(ext2_loff_t)) &&
+           ((channel->align == 0) ||
+            (IS_ALIGNED(buf, channel->align) &&
+             IS_ALIGNED(size, channel->align)))) {
+               actual = pwrite(data->dev, buf, size, location);
+               if (actual == size)
+                       return 0;
+       }
+#endif /* HAVE_PWRITE */
+
        if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) {
                retval = errno ? errno : EXT2_ET_LLSEEK_FAILED;
                goto error_out;
@@ -776,7 +823,8 @@ static errcode_t unix_write_blk64(io_channel channel, unsigned long long block,
                        cache = reuse;
                        reuse_cache(channel, data, cache, block);
                }
-               memcpy(cache->buf, cp, channel->block_size);
+               if (cache->buf != cp)
+                       memcpy(cache->buf, cp, channel->block_size);
                cache->dirty = !writethrough;
                count--;
                block++;
@@ -786,6 +834,23 @@ static errcode_t unix_write_blk64(io_channel channel, unsigned long long block,
 #endif /* NO_IO_CACHE */
 }
 
+static errcode_t unix_cache_readahead(io_channel channel,
+                                     unsigned long long block,
+                                     unsigned long long count)
+{
+#ifdef POSIX_FADV_WILLNEED
+       struct unix_private_data *data;
+
+       data = (struct unix_private_data *)channel->private_data;
+       return posix_fadvise(data->dev,
+                            (ext2_loff_t)block * channel->block_size,
+                            (ext2_loff_t)count * channel->block_size,
+                            POSIX_FADV_WILLNEED);
+#else
+       return EXT2_ET_OP_NOT_SUPPORTED;
+#endif
+}
+
 static errcode_t unix_write_blk(io_channel channel, unsigned long block,
                                int count, const void *buf)
 {
@@ -922,21 +987,89 @@ unimplemented:
        return EXT2_ET_UNIMPLEMENTED;
 }
 
+static errcode_t unix_zeroout(io_channel channel, unsigned long long block,
+                             unsigned long long count)
+{
+       struct unix_private_data *data;
+       int             ret;
+
+       EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL);
+       data = (struct unix_private_data *) channel->private_data;
+       EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL);
+
+       if (getenv("UNIX_IO_NOZEROOUT"))
+               goto unimplemented;
+
+       if (channel->flags & CHANNEL_FLAGS_BLOCK_DEVICE) {
+               /* Not implemented until the BLKZEROOUT mess is fixed */
+               goto unimplemented;
+       } else {
+               /* Regular file, try to use truncate/punch/zero. */
+#if defined(HAVE_FALLOCATE) && (defined(FALLOC_FL_ZERO_RANGE) || \
+       (defined(FALLOC_FL_PUNCH_HOLE) && defined(FALLOC_FL_KEEP_SIZE)))
+               struct stat statbuf;
+
+               if (count == 0)
+                       return 0;
+               /*
+                * If we're trying to zero a range past the end of the file,
+                * extend the file size, then punch (or zero_range) everything.
+                */
+               ret = fstat(data->dev, &statbuf);
+               if (ret)
+                       goto err;
+               if (statbuf.st_size < (block + count) * channel->block_size) {
+                       ret = ftruncate(data->dev,
+                                       (block + count) * channel->block_size);
+                       if (ret)
+                               goto err;
+               }
+#if defined(FALLOC_FL_PUNCH_HOLE) && defined(FALLOC_FL_KEEP_SIZE)
+               ret = fallocate(data->dev,
+                               FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+                               (off_t)(block) * channel->block_size,
+                               (off_t)(count) * channel->block_size);
+               if (ret == 0)
+                       goto err;
+#endif
+#ifdef FALLOC_FL_ZERO_RANGE
+               ret = fallocate(data->dev,
+                               FALLOC_FL_ZERO_RANGE,
+                               (off_t)(block) * channel->block_size,
+                               (off_t)(count) * channel->block_size);
+#endif
+#else
+               goto unimplemented;
+#endif /* HAVE_FALLOCATE && (ZERO_RANGE || (PUNCH_HOLE && KEEP_SIZE)) */
+       }
+err:
+       if (ret < 0) {
+               if (errno == EOPNOTSUPP)
+                       goto unimplemented;
+               return errno;
+       }
+       return 0;
+unimplemented:
+       return EXT2_ET_UNIMPLEMENTED;
+}
+
 static struct struct_io_manager struct_unix_manager = {
-       EXT2_ET_MAGIC_IO_MANAGER,
-       "Unix I/O Manager",
-       unix_open,
-       unix_close,
-       unix_set_blksize,
-       unix_read_blk,
-       unix_write_blk,
-       unix_flush,
-       unix_write_byte,
-       unix_set_option,
-       unix_get_stats,
-       unix_read_blk64,
-       unix_write_blk64,
-       unix_discard,
+       .magic          = EXT2_ET_MAGIC_IO_MANAGER,
+       .name           = "Unix I/O Manager",
+       .open           = unix_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 unix_io_manager = &struct_unix_manager;