| // 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; | 
 | } |