/* Copyright (c) 2008-2013 Red Hat, Inc. This file is part of GlusterFS. This file is licensed to you under your choice of the GNU Lesser General Public License, version 3 or any later version (LGPLv3 or later), or the GNU General Public License, version 2 (GPLv2), in all cases as published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #define MAX_NUM_OPS (1 << 20) #define MAX_FILE_SIZE (1 << 30) typedef enum { READ_OP, WRITE_OP, TRUNC_OP, LAST_OP } frag_op; struct frag_ctx { int test_fd; int good_fd; char *test_buf; char *good_buf; char *content; int max_file_size; }; typedef int (*frag_op_t)(struct frag_ctx *ctx, off_t offset, size_t count); static int doread(int fd, off_t offset, size_t count, char *buf, int max_file_size) { int ret = 0; int was_read = 0; if (lseek(fd, offset, SEEK_SET) == -1) { perror("lseek failed"); return -1; } while (count) { ret = read(fd, buf + offset + was_read, count); if (ret < 0) return -1; if (ret == 0) break; if (ret > count) { fprintf(stderr, "READ: read more than asked\n"); return -1; } count -= ret; was_read += ret; } return ret; } static int dowrite(int fd, off_t offset, size_t count, char *buf) { int ret; ret = lseek(fd, offset, SEEK_SET); if (ret == -1) return ret; return write(fd, buf, count); } static int dotrunc(int fd, off_t offset) { int ret; ret = ftruncate(fd, offset); if (ret == -1) perror("truncate failed"); return ret; } static int prepare_file(char *filename, int *fd, char **buf, int max_file_size) { int ret; *buf = malloc(max_file_size); if (*buf == NULL) { perror("malloc failed"); return -1; } *fd = open(filename, O_CREAT | O_RDWR, S_IRWXU); if (*fd == -1) { perror("open failed"); free(*buf); *buf = NULL; return -1; } return 0; } /* * @offset, @count: random values from [0, max_file_size - 1] */ static int frag_write(struct frag_ctx *ctx, off_t offset, size_t count) { int ret; struct stat test_stbuf; struct stat good_stbuf; if (offset + count > ctx->max_file_size) offset = offset / 2; if (offset + count > ctx->max_file_size) count = count / 2; if (fstat(ctx->test_fd, &test_stbuf)) { fprintf(stderr, "WRITE: fstat of test file failed\n"); return -1; } if (offset > test_stbuf.st_size) printf("writing hole\n"); ret = dowrite(ctx->test_fd, offset, count, ctx->content); if (ret < 0 || ret != count){ fprintf(stderr, "WRITE: failed to write test file\n"); return -1; } ret = dowrite(ctx->good_fd, offset, count, ctx->content); if (ret < 0 || ret != count) { fprintf(stderr, "WRITE: failed to write test file\n"); return -1; } if (fstat(ctx->test_fd, &test_stbuf)) { fprintf(stderr, "WRITE: fstat of test file failed\n"); return -1; } if (fstat(ctx->good_fd, &good_stbuf)) { fprintf(stderr, "WRITE: fstat of good file failed\n"); return -1; } if (test_stbuf.st_size != good_stbuf.st_size) { fprintf(stderr, "READ: Bad file size %d (expected %d)\n", (int)test_stbuf.st_size, (int)good_stbuf.st_size); return -1; } return 0; } /* * @offset, @count: random values from [0, max_file_size - 1] */ static int frag_read(struct frag_ctx *ctx, off_t offset, size_t count) { ssize_t test_ret; ssize_t good_ret; test_ret = doread(ctx->test_fd, offset, count, ctx->test_buf, ctx->max_file_size); if (test_ret < 0) { fprintf(stderr, "READ: failed to read test file\n"); return -1; } good_ret = doread(ctx->good_fd, offset, count, ctx->good_buf, ctx->max_file_size); if (good_ret < 0) { fprintf(stderr, "READ: failed to read good file\n"); return -1; } if (test_ret != good_ret) { fprintf(stderr, "READ: Bad return value %d (expected %d\n)", test_ret, good_ret); return -1; } if (memcmp(ctx->test_buf + offset, ctx->good_buf + offset, good_ret)) { fprintf(stderr, "READ: bad data\n"); return -1; } return 0; } /* * @offset: random value from [0, max_file_size - 1] */ static int frag_truncate(struct frag_ctx *ctx, off_t offset, __attribute__((unused))size_t count) { int ret; struct stat test_stbuf; struct stat good_stbuf; if (fstat(ctx->test_fd, &test_stbuf)) { fprintf(stderr, "TRUNCATE: fstat of test file failed\n"); return -1; } if (offset > test_stbuf.st_size) printf("expanding truncate to %d\n", offset); else if (offset < test_stbuf.st_size) printf("shrinking truncate to %d\n", offset); else printf("trivial truncate\n"); ret = dotrunc(ctx->test_fd, offset); if (ret == -1) { fprintf(stderr, "TRUNCATE: failed for test file\n"); return -1; } ret = dotrunc(ctx->good_fd, offset); if (ret == -1) { fprintf(stderr, "TRUNCATE: failed for good file\n"); return -1; } if (fstat(ctx->test_fd, &test_stbuf)) { fprintf(stderr, "TRUNCATE: fstat of test file failed\n"); return -1; } if (fstat(ctx->good_fd, &good_stbuf)) { fprintf(stderr, "TRUNCATE: fstat of good file failed\n"); return -1; } if (test_stbuf.st_size != good_stbuf.st_size) { fprintf(stderr, "TRUNCATE: bad test file size %d (expected %d)\n", test_stbuf.st_size, good_stbuf.st_size); return -1; } return 0; } frag_op_t frag_ops[LAST_OP] = { [READ_OP] = frag_read, [WRITE_OP] = frag_write, [TRUNC_OP] = frag_truncate }; static void put_ctx(struct frag_ctx *ctx) { if (ctx->test_buf) free(ctx->test_buf); if (ctx->good_buf) free(ctx->good_buf); if (ctx->content) free(ctx->content); } main (int argc, char *argv[]) { int i; int ret = 0; struct frag_ctx ctx; char *test_filename = NULL; char *good_filename = NULL; int num_ops; int max_file_size; memset(&ctx, 0, sizeof(ctx)); if (argc != 5) { fprintf(stderr, "usage: %s \n", argv[0]); ret = -1; goto exit; } test_filename = argv[1]; good_filename = argv[2]; max_file_size = atoi(argv[3]); if (max_file_size > MAX_FILE_SIZE) max_file_size = MAX_FILE_SIZE; num_ops = atoi(argv[4]); if (num_ops > MAX_NUM_OPS) num_ops = MAX_NUM_OPS; ret = prepare_file(test_filename, &ctx.test_fd, &ctx.test_buf, max_file_size); if (ret) goto exit; ret = prepare_file(good_filename, &ctx.good_fd, &ctx.good_buf, max_file_size); if (ret) { if (close(ctx.test_fd) == -1) perror("close test_buf failed"); goto exit; } ctx.content = malloc(max_file_size); if (!ctx.content) { perror("malloc failed"); goto close; } ctx.max_file_size = max_file_size; for (i = 0; i < max_file_size; i++) ctx.content[i] = random() % 256; for (i = 0; i < num_ops; i++) { ret = frag_ops[random() % LAST_OP](&ctx, random() % max_file_size, /* offset */ random() % max_file_size /* count */); if (ret) break; } close: if (close(ctx.test_fd) == -1) perror("close test_fd failed"); if (close(ctx.good_fd) == -1) perror("close good_fd failed"); exit: put_ctx(&ctx); if (ret) exit(1); exit(0); } /* Local variables: c-indentation-style: "K&R" mode-name: "LC" c-basic-offset: 8 tab-width: 8 fill-column: 80 scroll-step: 1 End: */