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