+ return err;
+}
+#endif /* SEEK_DATA and SEEK_HOLE */
+
+#if defined(FS_IOC_FIEMAP)
+static errcode_t try_fiemap_copy(ext2_filsys fs, int fd, ext2_file_t e2_file,
+ char *buf, char *zerobuf)
+{
+#define EXTENT_MAX_COUNT 512
+ struct fiemap *fiemap_buf;
+ struct fiemap_extent *ext_buf, *ext;
+ int ext_buf_size, fie_buf_size;
+ off_t pos = 0;
+ unsigned int i;
+ errcode_t err;
+
+ ext_buf_size = EXTENT_MAX_COUNT * sizeof(struct fiemap_extent);
+ fie_buf_size = sizeof(struct fiemap) + ext_buf_size;
+
+ err = ext2fs_get_memzero(fie_buf_size, &fiemap_buf);
+ if (err)
+ return err;
+
+ ext_buf = fiemap_buf->fm_extents;
+ memset(fiemap_buf, 0, fie_buf_size);
+ fiemap_buf->fm_length = FIEMAP_MAX_OFFSET;
+ fiemap_buf->fm_flags |= FIEMAP_FLAG_SYNC;
+ fiemap_buf->fm_extent_count = EXTENT_MAX_COUNT;
+
+ do {
+ fiemap_buf->fm_start = pos;
+ memset(ext_buf, 0, ext_buf_size);
+ err = ioctl(fd, FS_IOC_FIEMAP, fiemap_buf);
+ if (err < 0 && (errno == EOPNOTSUPP || errno == ENOTTY)) {
+ err = EXT2_ET_UNIMPLEMENTED;
+ goto out;
+ } else if (err < 0) {
+ err = errno;
+ goto out;
+ } else if (fiemap_buf->fm_mapped_extents == 0)
+ goto out;
+ for (i = 0, ext = ext_buf; i < fiemap_buf->fm_mapped_extents;
+ i++, ext++) {
+ err = copy_file_chunk(fs, fd, e2_file, ext->fe_logical,
+ ext->fe_logical + ext->fe_length,
+ buf, zerobuf);
+ if (err)
+ goto out;
+ }
+
+ ext--;
+ /* Record file's logical offset this time */
+ pos = ext->fe_logical + ext->fe_length;
+ /*
+ * If fm_extents array has been filled and
+ * there are extents left, continue to cycle.
+ */
+ } while (fiemap_buf->fm_mapped_extents == EXTENT_MAX_COUNT &&
+ !(ext->fe_flags & FIEMAP_EXTENT_LAST));
+out:
+ ext2fs_free_mem(&fiemap_buf);
+ return err;
+}
+#endif /* FS_IOC_FIEMAP */
+
+static errcode_t copy_file(ext2_filsys fs, int fd, struct stat *statbuf,
+ ext2_ino_t ino)
+{
+ ext2_file_t e2_file;
+ char *buf = NULL, *zerobuf = NULL;
+ errcode_t err, close_err;
+
+ err = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file);
+ if (err)
+ return err;
+
+ err = ext2fs_get_mem(COPY_FILE_BUFLEN, &buf);
+ if (err)
+ goto out;
+
+ err = ext2fs_get_memzero(fs->blocksize, &zerobuf);
+ if (err)
+ goto out;
+
+#if defined(SEEK_DATA) && defined(SEEK_HOLE)
+ err = try_lseek_copy(fs, fd, statbuf, e2_file, buf, zerobuf);
+ if (err != EXT2_ET_UNIMPLEMENTED)
+ goto out;
+#endif
+
+#if defined(FS_IOC_FIEMAP)
+ err = try_fiemap_copy(fs, fd, e2_file, buf, zerobuf);
+ if (err != EXT2_ET_UNIMPLEMENTED)
+ goto out;
+#endif
+
+ err = copy_file_chunk(fs, fd, e2_file, 0, statbuf->st_size, buf,
+ zerobuf);
+out:
+ ext2fs_free_mem(&zerobuf);