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
30static char *filename = NULL;
31static int expected_offset = -1;
32static int blocksize = 131072; /* 128KiB */
33static int numblocks = 8;
34static const char *execname = "file_append";
35static int use_odirect = 0;
36
37static void
38usage(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
71static void
72parse_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
132int
133main(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