xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/cmd/file/file_append.c (revision b670c9bafc0e31c7609969bf374b2e80bdc00211)
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