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 
47 static __attribute__((noreturn)) void
48 usage(void)
49 {
50 	(void) fprintf(stderr, gettext(
51 	    "Usage: mkfile [-nv] <size>[g|k|b|m] <name1> [<name2>] ...\n"));
52 	exit(1);
53 }
54 
55 int
56 main(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