1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or https://opensource.org/licenses/CDDL-1.0. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2022 by Triad National Security, LLC 24 */ 25 26 #include "file_common.h" 27 #include <unistd.h> 28 #include <sys/sysmacros.h> 29 30 static char *filename = NULL; 31 static int expected_offset = -1; 32 static int blocksize = 131072; /* 128KiB */ 33 static int numblocks = 8; 34 static const char *execname = "file_append"; 35 static int use_odirect = 0; 36 37 static void 38 usage(void) 39 { 40 (void) fprintf(stderr, 41 "usage %s -f filename -e expected_offset [-b blocksize] \n" 42 " [-n numblocks] [-d use_odirect] [-h help]\n" 43 "\n" 44 "Opens a file using O_APPEND and writes numblocks blocksize\n" 45 "blocks to filename.\n" 46 "Checks if expected_offst == lseek(fd, 0, SEEK_CUR)).\n" 47 "\n" 48 " filename: File to open with O_APPEND and write to.\n" 49 " expected_offset: Expected file offset after writing\n" 50 " blocksize numblocks to filename\n" 51 " blocksize: Size of each block to writei (must be at\n" 52 " least >= 512). If using use_odirect (-d)\n" 53 " must be a mutltiple of _SC_PAGE_SIZE\n" 54 " numblocks: Total number of blocksized blocks to\n" 55 " write.\n" 56 " use_odirect: Open file using O_DIRECT.\n" 57 " help: Print usage information and exit.\n" 58 "\n" 59 " Required parameters:\n" 60 " filename\n" 61 " expected_offset\n" 62 "\n" 63 " Default values:\n" 64 " blocksize -> 131072 (128 KiB)\n" 65 " numblocks -> 8\n" 66 " use_odirect -> False\n", 67 execname); 68 (void) exit(1); 69 } 70 71 static void 72 parse_options(int argc, char *argv[]) 73 { 74 int c; 75 int errflag = 0; 76 extern char *optarg; 77 extern int optind, optopt; 78 79 while ((c = getopt(argc, argv, "b:de:f:hn:")) != -1) { 80 switch (c) { 81 case 'b': 82 blocksize = atoi(optarg); 83 break; 84 case 'd': 85 use_odirect = 1; 86 break; 87 case 'e': 88 expected_offset = atoi(optarg); 89 break; 90 case 'f': 91 filename = optarg; 92 break; 93 case 'h': 94 (void) usage(); 95 break; 96 case 'n': 97 numblocks = atoi(optarg); 98 break; 99 case ':': 100 (void) fprintf(stderr, 101 "Option -%c requires an operand\n", 102 optopt); 103 errflag++; 104 break; 105 case '?': 106 default: 107 (void) fprintf(stderr, 108 "Unrecognized option: -%c\n", optopt); 109 errflag++; 110 break; 111 } 112 } 113 114 if (errflag) 115 (void) usage(); 116 117 if (use_odirect && ((blocksize % sysconf(_SC_PAGE_SIZE)) != 0)) { 118 (void) fprintf(stderr, 119 "blocksize parameter invalid when using O_DIRECT.\n"); 120 (void) usage(); 121 } 122 123 if (blocksize < 512 || expected_offset < 0 || filename == NULL || 124 numblocks <= 0) { 125 (void) fprintf(stderr, 126 "Required parameters(s) missing or invalid value for " 127 "parameter.\n"); 128 (void) usage(); 129 } 130 } 131 132 int 133 main(int argc, char *argv[]) 134 { 135 int err; 136 const char *datapattern = "0xf00ba3"; 137 int fd = -1; 138 int fd_flags = O_WRONLY | O_CREAT | O_APPEND; 139 int buf_offset = 0; 140 char *buf; 141 142 parse_options(argc, argv); 143 144 if (use_odirect) 145 fd_flags |= O_DIRECT; 146 147 fd = open(filename, fd_flags, 0666); 148 if (fd == -1) { 149 (void) fprintf(stderr, "%s: %s: ", execname, filename); 150 perror("open"); 151 (void) exit(2); 152 } 153 154 err = posix_memalign((void **)&buf, sysconf(_SC_PAGE_SIZE), 155 blocksize); 156 157 if (err != 0) { 158 (void) fprintf(stderr, 159 "%s: %s\n", execname, strerror(err)); 160 (void) exit(2); 161 } 162 163 /* Putting known data pattern in buffer */ 164 int left = blocksize; 165 while (left) { 166 size_t amt = MIN(strlen(datapattern), left); 167 memcpy(&buf[buf_offset], datapattern, amt); 168 buf_offset += amt; 169 left -= amt; 170 } 171 172 for (int i = 0; i < numblocks; i++) { 173 int wrote = write(fd, buf, blocksize); 174 175 if (wrote != blocksize) { 176 if (wrote < 0) { 177 perror("write"); 178 } else { 179 (void) fprintf(stderr, 180 "%s: unexpected short write, wrote %d " 181 "byte, expected %d\n", execname, wrote, 182 blocksize); 183 } 184 (void) exit(2); 185 } 186 } 187 188 /* Getting current file offset */ 189 off_t off = lseek(fd, 0, SEEK_CUR); 190 191 if (off == -1) { 192 perror("output seek"); 193 (void) exit(2); 194 } else if (off != expected_offset) { 195 (void) fprintf(stderr, 196 "%s: expected offset %d but current offset in %s is set " 197 "to %ld\n", execname, expected_offset, filename, 198 (long int)off); 199 (void) exit(2); 200 } 201 202 (void) close(fd); 203 free(buf); 204 205 return (0); 206 } 207