/* $Id: seek_hole.c,v 1.1 2012/03/27 17:57:45 kostik Exp kostik $ */ #include #include #include #include #include #include #include #include #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 const int block_size = 4096 * 8; int main(int argc, char **argv) { char buf[block_size * 4]; 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: %d\n", errno); return 1; } /* 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; }