Skip to content

Instantly share code, notes, and snippets.

@kostikbel
Created March 29, 2019 16:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kostikbel/5319d0d7d52e0f9199ddf2b5a75a6438 to your computer and use it in GitHub Desktop.
Save kostikbel/5319d0d7d52e0f9199ddf2b5a75a6438 to your computer and use it in GitHub Desktop.
/* $Id: seek_hole.c,v 1.2 2012/05/26 04:50:22 kostik Exp kostik $ */
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define ERROR(str) \
fprintf(stderr, "%s: pos=%ju, errno=%d\n", str, (uintmax_t)pos, errno)
static int
reset_file(int fd)
{
int ret;
ret = ftruncate(fd, 0);
if (ret < 0) {
fprintf(stderr, "Truncate failed: %d\n", errno);
return 1;
}
return 0;
}
static int block_size;
int
main(int argc, char **argv)
{
char *buf;
ssize_t bytes;
off_t pos;
int prealloc_is_hole = 0;
int whole_file_is_data = 0;
int ret;
int i;
int fd;
fd = open("testfile", O_RDWR|O_CREAT|O_TRUNC, 0644);
if (fd < 0) {
fprintf(stderr, "Failed to open testfile: %s\n",
strerror(errno));
return 1;
}
block_size = fpathconf(fd, _PC_MIN_HOLE_SIZE);
if (block_size == -1) {
fprintf(stderr, "Cannot get hole size: %s\n", strerror(errno));
return (1);
}
buf = malloc(block_size * 4);
/* Empty file */
printf("Testing an empty file\n");
pos = lseek(fd, 0, SEEK_DATA);
if (pos != -1) {
if (errno == EINVAL) {
fprintf(stderr, "Kernel does not support seek "
"hole/data\n");
close(fd);
return 1;
}
if (errno != ENXIO)
ERROR("Seek data did not return a proper error");
close(fd);
return 1;
}
pos = lseek(fd, 0, SEEK_HOLE);
if (pos != -1 && errno != ENXIO) {
ERROR("Seek hole did not return a proper error");
close(fd);
return 1;
}
memset(buf, 'a', block_size * 4);
/*
* All data file
*/
printf("Testing a normal data filled file\n");
for (i = 0; i < 4; i++) {
bytes = write(fd, buf, block_size);
if (bytes < block_size) {
fprintf(stderr, "Failed to write to testfile: %d\n",
errno);
close(fd);
return 1;
}
}
pos = lseek(fd, 0, SEEK_HOLE);
if (pos != (block_size * 4) || pos == -1) {
ERROR("Seek hole failed to dump us out at the end of the file");
close(fd);
return 1;
}
pos = lseek(fd, 0, SEEK_DATA);
if (pos != 0) {
ERROR("Seek data failed to dump us out at the beginning of the"
" file");
close(fd);
return 1;
}
/*
* File with a hole at the front and data at the end
*/
printf("Testing file with hole at the start and data in the rest\n");
if (reset_file(fd)) {
close(fd);
return 1;
}
bytes = pwrite(fd, buf, block_size * 3, block_size);
if (bytes < (block_size * 3)) {
fprintf(stderr, "Failed to write to testfile\n");
close(fd);
return 1;
}
pos = lseek(fd, 0, SEEK_HOLE);
if (pos != 0 && pos != (block_size * 4)) {
ERROR("Seek hole failed to return 0");
close(fd);
return 1;
} else if (pos == (block_size * 4)) {
whole_file_is_data = 1;
printf("Current file system views treats the entire file as "
"data\n");
}
pos = lseek(fd, 0, SEEK_DATA);
if (pos != block_size && (pos != 0 && whole_file_is_data)) {
if (whole_file_is_data)
ERROR("Seek data failed to return 0");
else
ERROR("Seek data failed to return block_size");
close(fd);
return 1;
}
if (whole_file_is_data) {
pos = lseek(fd, 1, SEEK_DATA);
if (pos != -1 && errno != ENXIO) {
ERROR("Seek data failed to return an error");
close(fd);
return 1;
}
}
#if 0
/*
* File with a hole at the end and data at the beginning
*/
printf("Testing file with hole at the end and data at the beginning\n");
if (reset_file(fd)) {
close(fd);
return 1;
}
ret = ftruncate(fd, block_size * 4);
if (ret < 0) {
fprintf(stderr, "Truncate failed: %d\n", errno);
close(fd);
return 1;
}
pwrite(fd, buf, block_size * 3, 0);
if (bytes < (block_size * 3)) {
fprintf(stderr, "Failed to write to testfile: %d\n", errno);
close(fd);
return 1;
}
pos = lseek(fd, 0, SEEK_HOLE);
if (pos != (block_size * 3) && (pos != (block_size * 4) && whole_file_is_data)) {
ERROR("Seeking hole didn't work right");
close(fd);
return 1;
}
if (whole_file_is_data) {
pos = lseek(fd, pos, SEEK_HOLE);
if (pos != -1 && errno != ENXIO) {
ERROR("Seeking hole didn't return error");
close(fd);
return 1;
}
printf("No more tests to run since we treat the whole file as "
"data\n");
goto out;
}
pos = lseek(fd, pos, SEEK_HOLE);
if (pos != (block_size * 3)) {
ERROR("Seek hole didn't return same position");
close(fd);
return 1;
}
pos = lseek(fd, pos+1, SEEK_HOLE);
if (pos != (block_size * 4)) {
ERROR("Seek hole didn't return the end of the file");
close(fd);
return 1;
}
pos = lseek(fd, pos, SEEK_DATA);
if (pos != -1 && errno != ENXIO) {
ERROR("Seek data didn't return ENXIO");
close(fd);
return 1;
}
#endif
/*
* Hole - Data - Hole - Data file
*/
printf("Testing file [Hole][Data][Hole][Data]\n");
if (reset_file(fd)) {
close(fd);
return 1;
}
ret = ftruncate(fd, block_size * 4);
if (ret < 0) {
fprintf(stderr, "ftruncate failed: %d\n", errno);
close(fd);
return 1;
}
bytes = pwrite(fd, buf, block_size, block_size);
if (bytes < block_size) {
fprintf(stderr, "Failed to write: %d\n", errno);
close(fd);
return 1;
}
bytes = pwrite(fd, buf, block_size, block_size * 3);
if (bytes < block_size) {
fprintf(stderr, "Failed to write: %d\n", errno);
close(fd);
return 1;
}
pos = lseek(fd, 0, SEEK_DATA);
if (pos != block_size) {
ERROR("Seek data did not return block_size");
close(fd);
return 1;
}
pos = lseek(fd, pos, SEEK_HOLE);
if (pos != (block_size * 2)) {
ERROR("Seek hole did not return block_size*2");
close(fd);
return 1;
}
pos = lseek(fd, pos, SEEK_DATA);
if (pos != (block_size * 3)) {
ERROR("Seek data did not return block_size*3");
close(fd);
return 1;
}
out:
close(fd);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment