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