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
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <strings.h>
32#include <fcntl.h>
33#include <limits.h>
34#include <sys/stat.h>
35#include <sys/types.h>
36#include <sys/uio.h>
37#include <unistd.h>
38#include <ctype.h>
39#include <errno.h>
40
41#define	MKFILE_WBUF	((size_t)(1048576))	/* Is 1M a reasonable value? */
42
43/* SunOS's mkfile(8) sets "sticky bit." */
44#define	MKFILE_FLAG	(O_WRONLY | O_CREAT | O_TRUNC)
45#define	MKFILE_MODE	(S_IRUSR | S_IWUSR | S_ISVTX)
46
47static char	buf[MKFILE_WBUF];
48static int	nofill = 0;
49static int	verbose = 0;
50
51static void
52usage()
53{
54	fprintf(stderr,
55	    "Usage: mkfile [-nv] <size>[e|p|t|g|m|k|b] <filename> ...\n");
56}
57
58static unsigned long long
59getsize(char *s)
60{
61	int sh;
62	unsigned long long length;
63	char *suffix;
64
65	/*
66	 * NOTE: We don't handle 'Z' (zetta) or 'Y' (yotta) suffixes yet.
67	 * These are too large to store in unsigned long long (64bits).
68	 * In the future, we'll have to use larger type,
69	 * something like uint128_t.
70	 */
71	length = strtoull(s, &suffix, 10);
72	sh = 0;
73	switch (tolower(*suffix)) {
74	case 'e':	/* Exabytes. */
75		sh = 60;
76		break;
77	case 'p':	/* Petabytes. */
78		sh = 50;
79		break;
80	case 't':	/* Terabytes. */
81		sh = 40;
82		break;
83	case 'g':	/* Gigabytes. */
84		sh = 30;
85		break;
86	case 'm':	/* Megabytes. */
87		sh = 20;
88		break;
89	case 'k':	/* Kilobytes. */
90		sh = 10;
91		break;
92	case 'b':	/* Blocks. */
93		sh = 9;
94		break;
95	case '\0':	/* Bytes. */
96		break;
97	default:	/* Unknown... */
98		errno = EINVAL;
99		return 0;
100	}
101	if (sh) {
102		unsigned long long l;
103
104		l = length;
105		length <<= sh;
106		/* Check overflow. */
107		if ((length >> sh) != l) {
108			errno = ERANGE;
109			return 0;
110		}
111	}
112
113	return length;
114}
115
116static int
117create_file(char *f, unsigned long long s)
118{
119	int fd;
120	size_t w;
121	ssize_t ws;
122
123	if (verbose) {
124		fprintf(stdout, "%s %llu bytes\n", f, s);
125		fflush(stdout);
126	}
127
128	/* Open file to create. */
129	if ((fd = open(f, MKFILE_FLAG, MKFILE_MODE)) < 0) {
130		return -1;
131	}
132
133	/* Seek to the end and write 1 byte. */
134	if ((lseek(fd, (off_t)(s - 1LL), SEEK_SET) == (off_t)-1) ||
135	    (write(fd, buf, (size_t)1) == (ssize_t)-1)) {
136		/*
137		 * We don't close(fd) here to avoid overwriting errno.
138		 * This is fd-leak, but is not harmful
139		 * because returning error causes mkfile(8) to exit.
140		 */
141		return -1;
142	}
143
144	/* Fill. */
145	if (!nofill) {
146		if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
147			/* Same as above. */
148			return -1;
149		}
150		while (s) {
151			w = (s > MKFILE_WBUF) ? MKFILE_WBUF : s;
152			if ((ws = write(fd, buf, w)) == (ssize_t)-1) {
153				/* Same as above. */
154				return -1;
155			}
156			s -= ws;
157		}
158	}
159	close(fd);
160
161	return 0;
162}
163
164int
165main(int argc, char *argv[])
166{
167	unsigned long long fsize;
168	int ch;
169
170	/* We have at least 2 arguments. */
171	if (argc < 3) {
172		usage();
173		return EXIT_FAILURE;
174	}
175
176	/* Options. */
177	while ((ch = getopt(argc, argv, "nv")) != -1) {
178		switch (ch) {
179		case 'n':
180			nofill = 1;
181			break;
182		case 'v':
183			verbose = 1;
184			break;
185		default:
186			usage();
187			return EXIT_FAILURE;
188		}
189	}
190	argc -= optind;
191	argv += optind;
192
193	/* File size to create. */
194	if ((fsize = getsize(*argv)) == 0) {
195		perror(*argv);
196		return EXIT_FAILURE;
197	}
198
199	/* Filenames to create. */
200	bzero(buf, MKFILE_WBUF);
201	while (++argv, --argc) {
202		if (create_file(*argv, fsize) == -1) {
203			perror(*argv);
204			return EXIT_FAILURE;
205		}
206	}
207
208	return EXIT_SUCCESS;
209}
210