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
usage(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
parse_options(int argc,char * argv[])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
main(int argc,char * argv[])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