xref: /freebsd/tests/sys/cddl/zfs/bin/mkfile.c (revision 07d397d74651435ff6f4d474876589e85967350e)
1 /*-
2  * Copyright (c) 2001-2013
3  *	HATANO Tomomi.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #ifndef	lint
30 static char rcsid[] = "$Id: mkfile.c,v 1.5 2013-10-26 10:11:34+09 hatanou Exp $";
31 #endif	/* !lint */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <fcntl.h>
38 #include <limits.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <sys/uio.h>
42 #include <unistd.h>
43 #include <ctype.h>
44 #include <errno.h>
45 
46 #define	MKFILE_WBUF	((size_t)(1048576))	/* Is 1M a reasonable value? */
47 
48 /* SunOS's mkfile(8) sets "sticky bit." */
49 #define	MKFILE_FLAG	(O_WRONLY | O_CREAT | O_TRUNC)
50 #define	MKFILE_MODE	(S_IRUSR | S_IWUSR | S_ISVTX)
51 
52 static char	buf[MKFILE_WBUF];
53 static int	nofill = 0;
54 static int	verbose = 0;
55 
56 static void
57 usage()
58 {
59 	fprintf(stderr,
60 	    "Usage: mkfile [-nv] <size>[e|p|t|g|m|k|b] <filename> ...\n");
61 }
62 
63 static unsigned long long
64 getsize(char *s)
65 {
66 	int sh;
67 	unsigned long long length;
68 	char *suffix;
69 
70 	/*
71 	 * NOTE: We don't handle 'Z' (zetta) or 'Y' (yotta) suffixes yet.
72 	 * These are too large to store in unsigned long long (64bits).
73 	 * In the future, we'll have to use larger type,
74 	 * something like uint128_t.
75 	 */
76 	length = strtoull(s, &suffix, 10);
77 	sh = 0;
78 	switch (tolower(*suffix)) {
79 	case 'e':	/* Exabytes. */
80 		sh = 60;
81 		break;
82 	case 'p':	/* Petabytes. */
83 		sh = 50;
84 		break;
85 	case 't':	/* Terabytes. */
86 		sh = 40;
87 		break;
88 	case 'g':	/* Gigabytes. */
89 		sh = 30;
90 		break;
91 	case 'm':	/* Megabytes. */
92 		sh = 20;
93 		break;
94 	case 'k':	/* Kilobytes. */
95 		sh = 10;
96 		break;
97 	case 'b':	/* Blocks. */
98 		sh = 9;
99 		break;
100 	case '\0':	/* Bytes. */
101 		break;
102 	default:	/* Unknown... */
103 		errno = EINVAL;
104 		return 0;
105 	}
106 	if (sh) {
107 		unsigned long long l;
108 
109 		l = length;
110 		length <<= sh;
111 		/* Check overflow. */
112 		if ((length >> sh) != l) {
113 			errno = ERANGE;
114 			return 0;
115 		}
116 	}
117 
118 	return length;
119 }
120 
121 static int
122 create_file(char *f, unsigned long long s)
123 {
124 	int fd;
125 	size_t w;
126 	ssize_t ws;
127 
128 	if (verbose) {
129 		fprintf(stdout, "%s %llu bytes\n", f, s);
130 		fflush(stdout);
131 	}
132 
133 	/* Open file to create. */
134 	if ((fd = open(f, MKFILE_FLAG, MKFILE_MODE)) < 0) {
135 		return -1;
136 	}
137 
138 	/* Seek to the end and write 1 byte. */
139 	if ((lseek(fd, (off_t)(s - 1LL), SEEK_SET) == (off_t)-1) ||
140 	    (write(fd, buf, (size_t)1) == (ssize_t)-1)) {
141 		/*
142 		 * We don't close(fd) here to avoid overwriting errno.
143 		 * This is fd-leak, but is not harmful
144 		 * because returning error causes mkfile(8) to exit.
145 		 */
146 		return -1;
147 	}
148 
149 	/* Fill. */
150 	if (!nofill) {
151 		if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
152 			/* Same as above. */
153 			return -1;
154 		}
155 		while (s) {
156 			w = (s > MKFILE_WBUF) ? MKFILE_WBUF : s;
157 			if ((ws = write(fd, buf, w)) == (ssize_t)-1) {
158 				/* Same as above. */
159 				return -1;
160 			}
161 			s -= ws;
162 		}
163 	}
164 	close(fd);
165 
166 	return 0;
167 }
168 
169 int
170 main(int argc, char *argv[])
171 {
172 	unsigned long long fsize;
173 	char ch;
174 
175 	/* We have at least 2 arguments. */
176 	if (argc < 3) {
177 		usage();
178 		return EXIT_FAILURE;
179 	}
180 
181 	/* Options. */
182 	while ((ch = getopt(argc, argv, "nv")) != -1) {
183 		switch (ch) {
184 		case 'n':
185 			nofill = 1;
186 			break;
187 		case 'v':
188 			verbose = 1;
189 			break;
190 		default:
191 			usage();
192 			return EXIT_FAILURE;
193 		}
194 	}
195 	argc -= optind;
196 	argv += optind;
197 
198 	/* File size to create. */
199 	if ((fsize = getsize(*argv)) == 0) {
200 		perror(*argv);
201 		return EXIT_FAILURE;
202 	}
203 
204 	/* Filenames to create. */
205 	bzero(buf, MKFILE_WBUF);
206 	while (++argv, --argc) {
207 		if (create_file(*argv, fsize) == -1) {
208 			perror(*argv);
209 			return EXIT_FAILURE;
210 		}
211 	}
212 
213 	return EXIT_SUCCESS;
214 }
215