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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * 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 2005 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 */
27
28#include <stdio.h>
29#include <ctype.h>
30#include <unistd.h>
31#include <sys/stat.h>
32#include <fcntl.h>
33#include <stdlib.h>
34#include <string.h>
35#include <libintl.h>
36#include <errno.h>
37#include <sys/stdtypes.h>
38#include <sys/sysmacros.h>
39
40#define	BLOCKSIZE	512		/* bytes */
41#define	KILOBYTE	1024
42#define	MEGABYTE	(KILOBYTE * KILOBYTE)
43#define	GIGABYTE	(KILOBYTE * MEGABYTE)
44
45#define	FILE_MODE	(S_ISVTX + S_IRUSR + S_IWUSR)
46
47static __attribute__((noreturn)) void
48usage(void)
49{
50	(void) fprintf(stderr, gettext(
51	    "Usage: mkfile [-nv] <size>[g|k|b|m] <name1> [<name2>] ...\n"));
52	exit(1);
53}
54
55int
56main(int argc, char **argv)
57{
58	char	*opts;
59	off_t	size;
60	size_t	len;
61	size_t	mult = 1;
62	char	*buf = NULL;
63	size_t	bufsz = 0;
64	int	errors = 0;
65	int	i;
66	int	verbose = 0;	/* option variable */
67	int	nobytes = 0;	/* option variable */
68	int	saverr;
69
70	if (argc == 1)
71		usage();
72
73	while (argv[1] && argv[1][0] == '-') {
74		opts = &argv[1][0];
75		while (*(++opts)) {
76			switch (*opts) {
77			case 'v':
78				verbose++;
79				break;
80			case 'n':
81				nobytes++;
82				break;
83			default:
84				usage();
85			}
86		}
87		argc--;
88		argv++;
89	}
90	if (argc < 3)
91		usage();
92
93	len = strlen(argv[1]);
94	if (len && isalpha(argv[1][len-1])) {
95		switch (argv[1][len-1]) {
96		case 'k':
97		case 'K':
98			mult = KILOBYTE;
99			break;
100		case 'b':
101		case 'B':
102			mult = BLOCKSIZE;
103			break;
104		case 'm':
105		case 'M':
106			mult = MEGABYTE;
107			break;
108		case 'g':
109		case 'G':
110			mult = GIGABYTE;
111			break;
112		default:
113			(void) fprintf(stderr,
114			    gettext("unknown size %s\n"), argv[1]);
115			usage();
116		}
117
118		for (i = 0; i <= (len-2); i++) {
119			if (!isdigit(argv[1][i])) {
120				(void) fprintf(stderr,
121				    gettext("unknown size %s\n"), argv[1]);
122				usage();
123			}
124		}
125		argv[1][len-1] = '\0';
126	}
127	size = ((off_t)atoll(argv[1]) * (off_t)mult);
128
129	argv++;
130	argc--;
131
132	while (argc > 1) {
133		int fd;
134
135		if (verbose)
136			(void) fprintf(stdout, gettext("%s %lld bytes\n"),
137			    argv[1], (offset_t)size);
138		fd = open(argv[1], O_CREAT|O_TRUNC|O_RDWR, FILE_MODE);
139		if (fd < 0) {
140			saverr = errno;
141			(void) fprintf(stderr,
142			    gettext("Could not open %s: %s\n"),
143			    argv[1], strerror(saverr));
144			errors++;
145			argv++;
146			argc--;
147			continue;
148		} else if (fchown(fd, getuid(), getgid()) < 0) {
149			saverr = errno;
150			(void) fprintf(stderr, gettext(
151			    "Could not set owner/group of %s: %s\n"),
152			    argv[1], strerror(saverr));
153			(void) close(fd);
154			errors++;
155			argv++;
156			argc--;
157			continue;
158		} else if (lseek(fd, (off_t)size-1, SEEK_SET) < 0) {
159			saverr = errno;
160			(void) fprintf(stderr, gettext(
161			    "Could not seek to offset %ld in %s: %s\n"),
162			    (unsigned long)size-1, argv[1], strerror(saverr));
163			(void) close(fd);
164			errors++;
165			argv++;
166			argc--;
167			continue;
168		} else if (write(fd, "", 1) != 1) {
169			saverr = errno;
170			(void) fprintf(stderr, gettext(
171			    "Could not set length of %s: %s\n"),
172			    argv[1], strerror(saverr));
173			(void) close(fd);
174			errors++;
175			argv++;
176			argc--;
177			continue;
178		}
179
180		if (!nobytes) {
181			off_t written = 0;
182			struct stat64 st;
183
184			if (lseek(fd, (off_t)0, SEEK_SET) < 0) {
185				saverr = errno;
186				(void) fprintf(stderr, gettext(
187				    "Could not seek to beginning of %s: %s\n"),
188				    argv[1], strerror(saverr));
189				(void) close(fd);
190				errors++;
191				argv++;
192				argc--;
193				continue;
194			}
195			if (fstat64(fd, &st) < 0) {
196				saverr = errno;
197				(void) fprintf(stderr, gettext(
198				    "Could not fstat64 %s: %s\n"),
199				    argv[1], strerror(saverr));
200				(void) close(fd);
201				errors++;
202				argv++;
203				argc--;
204				continue;
205			}
206			if (bufsz != st.st_blksize) {
207				if (buf)
208					free(buf);
209				bufsz = (size_t)st.st_blksize;
210				buf = calloc(1, bufsz);
211				if (buf == NULL) {
212					(void) fprintf(stderr, gettext(
213					    "Could not allocate buffer of"
214					    " size %d\n"), (int)bufsz);
215					(void) close(fd);
216					bufsz = 0;
217					errors++;
218					argv++;
219					argc--;
220					continue;
221				}
222			}
223			while (written < size) {
224				ssize_t result;
225				size_t bytes = (size_t)MIN(bufsz, size-written);
226
227				if ((result = write(fd, buf, bytes)) !=
228				    (ssize_t)bytes) {
229					saverr = errno;
230					if (result < 0)
231						result = 0;
232					written += result;
233					(void) fprintf(stderr, gettext(
234			    "%s: initialized %lu of %lu bytes: %s\n"),
235					    argv[1], (unsigned long)written,
236					    (unsigned long)size,
237					    strerror(saverr));
238					errors++;
239					break;
240				}
241				written += bytes;
242			}
243
244			/*
245			 * A write(2) call in the above loop failed so
246			 * close out this file and go on (error was
247			 * already incremented when the write(2) failed).
248			 */
249			if (written < size) {
250				(void) close(fd);
251				argv++;
252				argc--;
253				continue;
254			}
255		}
256		if (close(fd) < 0) {
257			saverr = errno;
258			(void) fprintf(stderr, gettext(
259			    "Error encountered when closing %s: %s\n"),
260			    argv[1], strerror(saverr));
261			errors++;
262			argv++;
263			argc--;
264			continue;
265		}
266
267		/*
268		 * Only set the modes (including the sticky bit) if we
269		 * had no problems.  It is not an error for the chmod(2)
270		 * to fail, but do issue a warning.
271		 */
272		if (chmod(argv[1], FILE_MODE) < 0)
273			(void) fprintf(stderr, gettext(
274			    "warning: couldn't set mode to %#o\n"), FILE_MODE);
275
276		argv++;
277		argc--;
278	}
279
280	if (buf)
281		free(buf);
282
283	return (errors);
284}
285