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