|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright (c) 2013 SGI.  All Rights Reserved. | 
|  | */ | 
|  |  | 
|  | /* dxm - 28/2/2 */ | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <malloc.h> | 
|  | #include <fcntl.h> | 
|  | #include <errno.h> | 
|  | #include <string.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/socket.h> | 
|  |  | 
|  | #include <unistd.h> | 
|  | #include <sys/types.h> | 
|  |  | 
|  | FILE    *fp = NULL; | 
|  |  | 
|  | #define LL                  "ll" | 
|  |  | 
|  | #define PERROR(a,b)         perror(a) | 
|  | #define GET_LAST_ERROR      errno | 
|  |  | 
|  | #define HANDLE              int | 
|  | #define INVALID_HANDLE      -1 | 
|  | #define TRUNCATE_ERROR      -1 | 
|  | #define FLUSH_ERROR         EOF | 
|  |  | 
|  | #define FILE_BEGIN          SEEK_SET | 
|  | #define FILE_CURRENT        SEEK_CUR | 
|  |  | 
|  | #define OPEN(N, F)          open(N, O_CREAT|O_RDWR|F, 0644); fp = fdopen(f, "r+") | 
|  | #define SEEK(H, O, F)       (lseek(H, O, F)) | 
|  | #define READ(H, B, L)       (read(H, B, L)) | 
|  | #define WRITE(H, B, L)      (write(H, B, L)) | 
|  | #define CLOSE(H)            (close(H)) | 
|  | #define DELETE_FILE(F)      (unlink(F)) | 
|  | #define FLUSH(F)            (fflush(fp)) | 
|  | #define TRUNCATE(F)         (ftruncate(F, 0)) | 
|  |  | 
|  | #define ALLOC_ALIGNED(S)    (memalign(65536, S)) | 
|  | #define FREE_ALIGNED(P)     (free(P)) | 
|  |  | 
|  | #define DIRECT_IO_FLAG      O_DIRECT | 
|  |  | 
|  | enum { | 
|  | FLAG_OPENCLOSE  = 1, | 
|  | FLAG_READ       = 2, | 
|  | FLAG_WRITE      = 4, | 
|  | FLAG_VERBOSE    = 8, | 
|  | FLAG_TRUNCATE   = 16, | 
|  | FLAG_SEQUENTIAL = 32, | 
|  | FLAG_FLUSH      = 64, | 
|  | FLAG_DELETE     = 128, | 
|  | FLAG_DIRECT     = 256, | 
|  | }; | 
|  |  | 
|  | void | 
|  | usage(char *argv0) | 
|  | { | 
|  | printf( | 
|  | "Usage: %s [switches] <filename>\n" | 
|  | "              -i <count>   = repeat count (default forever)\n" | 
|  | "              -o           = open/close\n" | 
|  | "              -r           = read\n" | 
|  | "              -w           = write\n" | 
|  | "              -t           = truncate\n" | 
|  | "              -d           = delete\n" | 
|  | "              -b <size>    = buffer size\n" | 
|  | "              -v           = verbose\n" | 
|  | "              -s           = sequential\n" | 
|  | "              -f           = flush\n" | 
|  | "              -D           = direct-IO\n" | 
|  | "              -h           = usage\n", | 
|  | argv0); | 
|  | } | 
|  |  | 
|  | extern int optind; | 
|  | extern char *optarg; | 
|  |  | 
|  | int | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | HANDLE              f 		= INVALID_HANDLE; | 
|  | char                *filename; | 
|  | int                 i; | 
|  | int                 c; | 
|  | int 		count           = -1; | 
|  | int                 bufsize         = 4096; | 
|  | int                 flags           = 0; | 
|  | char                *buf            = NULL; | 
|  | int64_t 		seek_to 	= 0; | 
|  |  | 
|  | while ((c = getopt(argc, argv, "i:orwb:svthfFDd?")) != EOF) { | 
|  | switch (c) { | 
|  | case 'i': | 
|  | count = atoi(optarg); | 
|  | break; | 
|  | case 'o': | 
|  | flags |= FLAG_OPENCLOSE; | 
|  | break; | 
|  | case 'r': | 
|  | flags |= FLAG_READ; | 
|  | break; | 
|  | case 'w': | 
|  | flags |= FLAG_WRITE; | 
|  | break; | 
|  | case 't': | 
|  | flags |= FLAG_TRUNCATE; | 
|  | break; | 
|  | case 'v': | 
|  | flags |= FLAG_VERBOSE; | 
|  | break; | 
|  | case 'b': | 
|  | bufsize = atoi(optarg); | 
|  | break; | 
|  | case 's': | 
|  | flags |= FLAG_SEQUENTIAL; | 
|  | break; | 
|  | case 'f': | 
|  | flags |= FLAG_FLUSH; | 
|  | break; | 
|  | case 'D': | 
|  | flags |= FLAG_DIRECT; | 
|  | break; | 
|  | case 'd': | 
|  | flags |= FLAG_DELETE; | 
|  | break; | 
|  | case '?': | 
|  | case 'h': | 
|  | default: | 
|  | usage(argv[0]); | 
|  | return 1; | 
|  | } | 
|  | } | 
|  | if (optind != argc - 1) { | 
|  | usage(argv[0]); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | filename = argv[optind]; | 
|  | if (!flags) { | 
|  | fprintf(stderr, "nothing to do!\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | if (flags & FLAG_DIRECT) | 
|  | buf = (char *)ALLOC_ALIGNED(bufsize); | 
|  | else | 
|  | buf = (char *)malloc(bufsize); | 
|  |  | 
|  | if (!buf) | 
|  | PERROR("malloc", GET_LAST_ERROR); | 
|  |  | 
|  | for (i = 0; i < bufsize; i++) { | 
|  | buf[i] = i & 127; | 
|  | } | 
|  |  | 
|  | for (i = 0; count < 0 || i < count; i++) { | 
|  |  | 
|  | if ((flags & FLAG_OPENCLOSE) || !i) { | 
|  | int fileflags; | 
|  |  | 
|  | if (flags & FLAG_VERBOSE) | 
|  | printf("open %s\n", filename); | 
|  |  | 
|  | fileflags = 0; | 
|  | if (flags & FLAG_DIRECT) | 
|  | fileflags |= DIRECT_IO_FLAG; | 
|  |  | 
|  | f = OPEN(filename, fileflags); | 
|  | if (f == INVALID_HANDLE) | 
|  | PERROR("OPEN", GET_LAST_ERROR); | 
|  |  | 
|  | } | 
|  |  | 
|  | if ((flags & FLAG_OPENCLOSE) && (flags & FLAG_SEQUENTIAL)) { | 
|  | if (flags & FLAG_VERBOSE) | 
|  | printf("seek %" LL "d\n", (long long)seek_to); | 
|  | if (SEEK(f, seek_to, FILE_BEGIN) < 0) | 
|  | PERROR("SEEK", GET_LAST_ERROR); | 
|  | } | 
|  |  | 
|  | if (flags & FLAG_WRITE) { | 
|  | int sizewritten; | 
|  |  | 
|  | if (!(flags & FLAG_SEQUENTIAL)) { | 
|  | if (flags & FLAG_VERBOSE) | 
|  | printf("seek %" LL "d\n", (long long)seek_to); | 
|  | if (SEEK(f, seek_to, FILE_BEGIN) < 0) | 
|  | PERROR("SEEK", GET_LAST_ERROR); | 
|  | } | 
|  |  | 
|  | if (flags & FLAG_VERBOSE) | 
|  | printf("write %d\n", bufsize); | 
|  | if ((sizewritten = WRITE(f, buf, bufsize)) != bufsize) { | 
|  | if (sizewritten < 0) | 
|  | PERROR("WRITE", GET_LAST_ERROR); | 
|  | else | 
|  | fprintf(stderr, "short write: %d of %d\n", | 
|  | sizewritten, bufsize); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (flags & FLAG_READ) { | 
|  | int sizeread; | 
|  |  | 
|  | if (!(flags & FLAG_SEQUENTIAL) || (flags & FLAG_WRITE)) { | 
|  | if (flags & FLAG_VERBOSE) | 
|  | printf("seek %" LL "d\n", (long long)seek_to); | 
|  | if (SEEK(f, seek_to, FILE_BEGIN) < 0) | 
|  | PERROR("SEEK", GET_LAST_ERROR); | 
|  | } | 
|  |  | 
|  | if (flags & FLAG_VERBOSE) | 
|  | printf("read %d\n", bufsize); | 
|  | if ((sizeread = READ(f, buf, bufsize)) != bufsize) { | 
|  | if (sizeread < 0) | 
|  | PERROR("READ", GET_LAST_ERROR); | 
|  | else if (sizeread) | 
|  | fprintf(stderr, "short read: %d of %d\n", | 
|  | sizeread, bufsize); | 
|  | else { | 
|  | fprintf(stderr, "Read past EOF\n"); | 
|  | exit(0); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (flags & FLAG_TRUNCATE) { | 
|  | if (flags & FLAG_VERBOSE) | 
|  | printf("seek 0\n"); | 
|  |  | 
|  | if (SEEK(f, 0, FILE_BEGIN) < 0) | 
|  | PERROR("SEEK", GET_LAST_ERROR); | 
|  |  | 
|  | if (flags & FLAG_VERBOSE) | 
|  | printf("truncate\n"); | 
|  |  | 
|  | if (TRUNCATE(f) == TRUNCATE_ERROR) | 
|  | PERROR("TRUNCATE", GET_LAST_ERROR); | 
|  | } | 
|  |  | 
|  | if (flags & FLAG_FLUSH) { | 
|  | if (flags & FLAG_VERBOSE) | 
|  | printf("flush\n"); | 
|  |  | 
|  | if (FLUSH(f) == FLUSH_ERROR) | 
|  | PERROR("FLUSH", GET_LAST_ERROR); | 
|  | } | 
|  |  | 
|  | if (flags & FLAG_SEQUENTIAL) { | 
|  | seek_to += bufsize; | 
|  | if (flags & FLAG_TRUNCATE) { | 
|  | if (flags & FLAG_VERBOSE) | 
|  | printf("seek %" LL "d\n", (long long)seek_to); | 
|  | if (SEEK(f, seek_to, FILE_BEGIN) < 0) | 
|  | PERROR("SEEK", GET_LAST_ERROR); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (flags & FLAG_OPENCLOSE) { | 
|  | if (flags & FLAG_VERBOSE) | 
|  | printf("close %s\n", filename); | 
|  | CLOSE(f); | 
|  | } | 
|  |  | 
|  | if (flags & FLAG_DELETE) { | 
|  | if (flags & FLAG_VERBOSE) | 
|  | printf("delete %s\n", filename); | 
|  | DELETE_FILE(filename); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (buf) { | 
|  | if (flags & FLAG_DIRECT) | 
|  | FREE_ALIGNED(buf); | 
|  | else | 
|  | free(buf); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } |