xref: /openbsd-src/sbin/fdisk/misc.c (revision d0fc3bb68efd6c434b4053cd7adb29023cbec341)
1 /*	$OpenBSD: misc.c,v 1.68 2021/06/20 18:44:19 krw Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Tobias Weingartner
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/disklabel.h>
21 
22 #include <ctype.h>
23 #include <err.h>
24 #include <errno.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <uuid.h>
30 
31 #include "disk.h"
32 #include "misc.h"
33 #include "part.h"
34 
35 struct unit_type unit_types[] = {
36 	{ "b"	, 1LL				, "Bytes"	},
37 	{ " "	, 0LL				, "Sectors"	},
38 	{ "K"	, 1024LL			, "Kilobytes"	},
39 	{ "M"	, 1024LL * 1024			, "Megabytes"	},
40 	{ "G"	, 1024LL * 1024 *1024		, "Gigabytes"	},
41 	{ "T"	, 1024LL * 1024 * 1024 * 1024	, "Terabytes"	},
42 	{ NULL	, 0				, NULL		},
43 };
44 
45 int
46 unit_lookup(char *units)
47 {
48 	int i = 0;
49 
50 	if (units == NULL)
51 		return (SECTORS);
52 
53 	while (unit_types[i].abbr != NULL) {
54 		if (strncasecmp(unit_types[i].abbr, units, 1) == 0)
55 			break;
56 		i++;
57 	}
58 	/* default */
59 	if (unit_types[i].abbr == NULL)
60 		return (SECTORS);
61 
62 	return (i);
63 }
64 
65 int
66 string_from_line(char *buf, size_t buflen)
67 {
68 	static char *line;
69 	static size_t sz;
70 	ssize_t len;
71 
72 	len = getline(&line, &sz, stdin);
73 	if (len == -1)
74 		return (1);
75 
76 	if (line[len - 1] == '\n')
77 		line[len - 1] = '\0';
78 
79 	strlcpy(buf, line, buflen);
80 
81 	return (0);
82 }
83 
84 void
85 ask_cmd(char **cmd, char **arg)
86 {
87 	static char lbuf[100];
88 	size_t cmdstart, cmdend, argstart;
89 
90 	/* Get NUL terminated string from stdin. */
91 	if (string_from_line(lbuf, sizeof(lbuf)))
92 		errx(1, "eof");
93 
94 	cmdstart = strspn(lbuf, " \t");
95 	cmdend = cmdstart + strcspn(&lbuf[cmdstart], " \t");
96 	argstart = cmdend + strspn(&lbuf[cmdend], " \t");
97 
98 	/* *cmd and *arg may be set to point at final NUL! */
99 	*cmd = &lbuf[cmdstart];
100 	lbuf[cmdend] = '\0';
101 	*arg = &lbuf[argstart];
102 }
103 
104 int
105 ask_num(const char *str, int dflt, int low, int high)
106 {
107 	char lbuf[100];
108 	const char *errstr;
109 	int num;
110 
111 	if (dflt < low)
112 		dflt = low;
113 	else if (dflt > high)
114 		dflt = high;
115 
116 	do {
117 		printf("%s [%d - %d]: [%d] ", str, low, high, dflt);
118 
119 		if (string_from_line(lbuf, sizeof(lbuf)))
120 			errx(1, "eof");
121 
122 		if (lbuf[0] == '\0') {
123 			num = dflt;
124 			errstr = NULL;
125 		} else {
126 			num = (int)strtonum(lbuf, low, high, &errstr);
127 			if (errstr)
128 				printf("%s is %s: %s.\n", str, errstr, lbuf);
129 		}
130 	} while (errstr);
131 
132 	return (num);
133 }
134 
135 int
136 ask_pid(int dflt, struct uuid *guid)
137 {
138 	char lbuf[100], *cp;
139 	int num = -1, status;
140 
141 	do {
142 		printf("Partition id ('0' to disable) [01 - FF]: [%X] ", dflt);
143 		printf("(? for help) ");
144 
145 		if (string_from_line(lbuf, sizeof(lbuf)))
146 			errx(1, "eof");
147 
148 		if (lbuf[0] == '?') {
149 			PRT_printall();
150 			continue;
151 		}
152 
153 		if (guid && strlen(lbuf) == UUID_STR_LEN) {
154 			uuid_from_string(lbuf, guid, &status);
155 			if (status == uuid_s_ok)
156 				return (0x100);
157 		}
158 
159 		/* Convert */
160 		cp = lbuf;
161 		num = strtol(lbuf, &cp, 16);
162 
163 		/* Make sure only number present */
164 		if (cp == lbuf)
165 			num = dflt;
166 		if (*cp != '\0') {
167 			printf("'%s' is not a valid number.\n", lbuf);
168 			num = -1;
169 		} else if (num == 0) {
170 			break;
171 		} else if (num < 0 || num > 0xff) {
172 			printf("'%x' is out of range.\n", num);
173 		}
174 	} while (num < 0 || num > 0xff);
175 
176 	return (num);
177 }
178 
179 int
180 ask_yn(const char *str)
181 {
182 	int ch, first;
183 	extern int y_flag;
184 
185 	if (y_flag)
186 		return (1);
187 
188 	printf("%s [n] ", str);
189 	fflush(stdout);
190 
191 	first = ch = getchar();
192 	while (ch != '\n' && ch != EOF)
193 		ch = getchar();
194 
195 	if (ch == EOF || first == EOF)
196 		errx(1, "eof");
197 
198 	return (first == 'y' || first == 'Y');
199 }
200 
201 /*
202  * adapted from sbin/disklabel/editor.c
203  */
204 uint64_t
205 getuint64(char *prompt, uint64_t oval, uint64_t minval, uint64_t maxval)
206 {
207 	const int secsize = unit_types[SECTORS].conversion;
208 	char buf[BUFSIZ], *endptr, *p, operator = '\0';
209 	size_t n;
210 	int64_t mult = 1;
211 	double d, d2;
212 	int rslt, secpercyl, saveerr;
213 	char unit;
214 
215 	if (oval > maxval)
216 		oval = maxval;
217 	if (oval < minval)
218 		oval = minval;
219 
220 	secpercyl = disk.sectors * disk.heads;
221 
222 	do {
223 		printf("%s [%llu - %llu]: [%llu] ", prompt, minval, maxval,
224 		    oval);
225 
226 		if (string_from_line(buf, sizeof(buf)))
227 			errx(1, "eof");
228 
229 		if (buf[0] == '\0') {
230 			rslt = snprintf(buf, sizeof(buf), "%llu", oval);
231 			if (rslt < 0 || rslt >= sizeof(buf))
232 				errx(1, "default value too long");
233 		} else if (buf[0] == '*' && buf[1] == '\0') {
234 			return (maxval);
235 		}
236 
237 		/* deal with units */
238 		n = strlen(buf);
239 		switch (tolower((unsigned char)buf[n-1])) {
240 		case 'c':
241 			unit = 'c';
242 			mult = secpercyl;
243 			buf[--n] = '\0';
244 			break;
245 		case 'b':
246 			unit = 'b';
247 			mult = -(int64_t)secsize;
248 			buf[--n] = '\0';
249 			break;
250 		case 's':
251 			unit = 's';
252 			mult = 1;
253 			buf[--n] = '\0';
254 			break;
255 		case 'k':
256 			unit = 'k';
257 			if (secsize > 1024)
258 				mult = -(int64_t)secsize / 1024LL;
259 			else
260 				mult = 1024LL / secsize;
261 			buf[--n] = '\0';
262 			break;
263 		case 'm':
264 			unit = 'm';
265 			mult = (1024LL * 1024) / secsize;
266 			buf[--n] = '\0';
267 			break;
268 		case 'g':
269 			unit = 'g';
270 			mult = (1024LL * 1024 * 1024) / secsize;
271 			buf[--n] = '\0';
272 			break;
273 		case 't':
274 			unit = 't';
275 			mult = (1024LL * 1024 * 1024 * 1024) / secsize;
276 			buf[--n] = '\0';
277 			break;
278 		default:
279 			unit = ' ';
280 			mult = 1;
281 			break;
282 		}
283 
284 		/* deal with the operator */
285 		p = &buf[0];
286 		if (*p == '+' || *p == '-')
287 			operator = *p++;
288 		else
289 			operator = ' ';
290 
291 		endptr = p;
292 		errno = 0;
293 		d = strtod(p, &endptr);
294 		saveerr = errno;
295 		d2 = d;
296 		if (mult > 0)
297 			d *= mult;
298 		else {
299 			d /= (-mult);
300 			d2 = d;
301 		}
302 
303 		/* Apply the operator */
304 		if (operator == '+')
305 			d = oval + d;
306 		else if (operator == '-') {
307 			d = oval - d;
308 			d2 = d;
309 		}
310 
311 		if (saveerr == ERANGE || d > maxval || d < minval || d < d2) {
312 			printf("%s is out of range: %c%s%c\n", prompt, operator,
313 			    p, unit);
314 		} else if (*endptr != '\0') {
315 			printf("%s is invalid: %c%s%c\n", prompt, operator,
316 			    p, unit);
317 		} else {
318 			break;
319 		}
320 	} while (1);
321 
322 	return((uint64_t)d);
323 }
324 
325 char *
326 ask_string(const char *prompt, const char *oval)
327 {
328 	static char buf[UUID_STR_LEN + 1];
329 
330 	buf[0] = '\0';
331 	printf("%s: [%s] ", prompt, oval ? oval : "");
332 	if (string_from_line(buf, sizeof(buf)))
333 		errx(1, "eof");
334 
335 	if (buf[0] == '\0' && oval)
336 		strlcpy(buf, oval, sizeof(buf));
337 
338 	return(buf);
339 }
340 
341 /*
342  * Adapted from Hacker's Delight crc32b().
343  *
344  * To quote http://www.hackersdelight.org/permissions.htm :
345  *
346  * "You are free to use, copy, and distribute any of the code on
347  *  this web site, whether modified by you or not. You need not give
348  *  attribution. This includes the algorithms (some of which appear
349  *  in Hacker's Delight), the Hacker's Assistant, and any code submitted
350  *  by readers. Submitters implicitly agree to this."
351  */
352 uint32_t
353 crc32(const u_char *buf, const uint32_t size)
354 {
355 	int j;
356 	uint32_t i, byte, crc, mask;
357 
358 	crc = 0xFFFFFFFF;
359 
360 	for (i = 0; i < size; i++) {
361 		byte = buf[i];			/* Get next byte. */
362 		crc = crc ^ byte;
363 		for (j = 7; j >= 0; j--) {	/* Do eight times. */
364 			mask = -(crc & 1);
365 			crc = (crc >> 1) ^ (0xEDB88320 & mask);
366 		}
367 	}
368 
369 	return ~crc;
370 }
371 
372 char *
373 utf16le_to_string(const uint16_t *utf)
374 {
375 	static char name[GPTPARTNAMESIZE];
376 	int i;
377 
378 	for (i = 0; i < GPTPARTNAMESIZE; i++) {
379 		name[i] = letoh16(utf[i]) & 0x7F;
380 		if (name[i] == '\0')
381 			break;
382 	}
383 	if (i == GPTPARTNAMESIZE)
384 		name[i - 1] = '\0';
385 
386 	return (name);
387 }
388 
389 uint16_t *
390 string_to_utf16le(const char *ch)
391 {
392 	static uint16_t utf[GPTPARTNAMESIZE];
393 	int i;
394 
395 	for (i = 0; i < GPTPARTNAMESIZE; i++) {
396 		utf[i] = htole16((unsigned int)ch[i]);
397 		if (utf[i] == 0)
398 			break;
399 	}
400 	if (i == GPTPARTNAMESIZE)
401 		utf[i - 1] = 0;
402 
403 	return (utf);
404 }
405 
406 void
407 parse_b(const char *arg, uint32_t *blocks, uint32_t *offset, uint8_t *type)
408 {
409 	const char *errstr;
410 	char *poffset, *ptype;
411 	uint32_t blockcount, blockoffset;
412 	uint8_t partitiontype;
413 
414 	blockoffset = 64;
415 	partitiontype = DOSPTYP_EFISYS;
416 	ptype = NULL;
417 
418 	/* First number: # of sectors in boot partition. */
419 	poffset = strchr(arg, '@');
420 	if (poffset != NULL)
421 		*poffset++ = '\0';
422 	if (poffset != NULL) {
423 		ptype = strchr(poffset, ':');
424 		if (ptype != NULL)
425 			*ptype++ = '\0';
426 	}
427 
428 	blockcount = strtonum(arg, 64, UINT32_MAX, &errstr);
429 	if (errstr)
430 		errx(1, "Block argument %s [64..%u].",
431 		    errstr, UINT32_MAX);
432 
433 	if (poffset == NULL)
434 		goto done;
435 
436 	blockoffset = strtonum(poffset, 64, UINT32_MAX, &errstr);
437 	if (errstr)
438 		errx(1, "Block offset argument %s "
439 		    "[64..%u].", errstr, UINT32_MAX);
440 
441 	if (ptype == NULL)
442 		goto done;
443 
444 	if (strlen(ptype) != 2 || !(isxdigit(*ptype) && isxdigit(*(ptype + 1))))
445 		errx(1, "Block type is not 2 digit hex value");
446 
447 	partitiontype = strtol(ptype, NULL, 16);
448 
449  done:
450 	*blocks = blockcount;
451 	*offset = blockoffset;
452 	*type = partitiontype;
453 }
454