1/*- 2 * Copyright (c) 2004-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3 * 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 AUTHORS 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 AUTHORS 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#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: stable/10/sbin/geom/misc/subr.c 330737 2018-03-10 04:17:01Z asomers $"); 29 30#include <sys/param.h> 31#include <sys/disk.h> 32#include <sys/endian.h> 33#include <sys/uio.h> 34#include <errno.h> 35#include <fcntl.h> 36#include <paths.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <limits.h> 40#include <inttypes.h> 41#include <stdarg.h> 42#include <string.h> 43#include <strings.h> 44#include <unistd.h> 45#include <assert.h> 46#include <libgeom.h> 47 48#include "misc/subr.h" 49 50 51struct std_metadata { 52 char md_magic[16]; 53 uint32_t md_version; 54}; 55 56static void 57std_metadata_decode(const unsigned char *data, struct std_metadata *md) 58{ 59 60 bcopy(data, md->md_magic, sizeof(md->md_magic)); 61 md->md_version = le32dec(data + 16); 62} 63 64/* 65 * Greatest Common Divisor. 66 */ 67static unsigned int 68gcd(unsigned int a, unsigned int b) 69{ 70 unsigned int c; 71 72 while (b != 0) { 73 c = a; 74 a = b; 75 b = (c % b); 76 } 77 return (a); 78} 79 80/* 81 * Least Common Multiple. 82 */ 83unsigned int 84g_lcm(unsigned int a, unsigned int b) 85{ 86 87 return ((a * b) / gcd(a, b)); 88} 89 90uint32_t 91bitcount32(uint32_t x) 92{ 93 94 x = (x & 0x55555555) + ((x & 0xaaaaaaaa) >> 1); 95 x = (x & 0x33333333) + ((x & 0xcccccccc) >> 2); 96 x = (x & 0x0f0f0f0f) + ((x & 0xf0f0f0f0) >> 4); 97 x = (x & 0x00ff00ff) + ((x & 0xff00ff00) >> 8); 98 x = (x & 0x0000ffff) + ((x & 0xffff0000) >> 16); 99 return (x); 100} 101 102/* 103 * The size of a sector is context specific (i.e. determined by the 104 * media). But when users enter a value with a SI unit, they really 105 * mean the byte-size or byte-offset and not the size or offset in 106 * sectors. We should map the byte-oriented value into a sector-oriented 107 * value when we already know the sector size in bytes. At this time 108 * we can use g_parse_lba() function. It converts user specified 109 * value into sectors with following conditions: 110 * o Sectors size taken as argument from caller. 111 * o When no SI unit is specified the value is in sectors. 112 * o With an SI unit the value is in bytes. 113 * o The 'b' suffix forces byte interpretation and the 's' 114 * suffix forces sector interpretation. 115 * 116 * Thus: 117 * o 2 and 2s mean 2 sectors, and 2b means 2 bytes. 118 * o 4k and 4kb mean 4096 bytes, and 4ks means 4096 sectors. 119 * 120 */ 121int 122g_parse_lba(const char *lbastr, unsigned int sectorsize, off_t *sectors) 123{ 124 off_t number, mult, unit; 125 char *s; 126 127 assert(lbastr != NULL); 128 assert(sectorsize > 0); 129 assert(sectors != NULL); 130 131 number = (off_t)strtoimax(lbastr, &s, 0); 132 if (s == lbastr || number < 0) 133 return (EINVAL); 134 135 mult = 1; 136 unit = sectorsize; 137 if (*s == '\0') 138 goto done; 139 switch (*s) { 140 case 'e': case 'E': 141 mult *= 1024; 142 /* FALLTHROUGH */ 143 case 'p': case 'P': 144 mult *= 1024; 145 /* FALLTHROUGH */ 146 case 't': case 'T': 147 mult *= 1024; 148 /* FALLTHROUGH */ 149 case 'g': case 'G': 150 mult *= 1024; 151 /* FALLTHROUGH */ 152 case 'm': case 'M': 153 mult *= 1024; 154 /* FALLTHROUGH */ 155 case 'k': case 'K': 156 mult *= 1024; 157 break; 158 default: 159 goto sfx; 160 } 161 unit = 1; /* bytes */ 162 s++; 163 if (*s == '\0') 164 goto done; 165sfx: 166 switch (*s) { 167 case 's': case 'S': 168 unit = sectorsize; /* sector */ 169 break; 170 case 'b': case 'B': 171 unit = 1; /* bytes */ 172 break; 173 default: 174 return (EINVAL); 175 } 176 s++; 177 if (*s != '\0') 178 return (EINVAL); 179done: 180 if ((OFF_MAX / unit) < mult || (OFF_MAX / mult / unit) < number) 181 return (ERANGE); 182 number *= mult * unit; 183 if (number % sectorsize) 184 return (EINVAL); 185 number /= sectorsize; 186 *sectors = number; 187 return (0); 188} 189 190off_t 191g_get_mediasize(const char *name) 192{ 193 off_t mediasize; 194 int fd; 195 196 fd = g_open(name, 0); 197 if (fd == -1) 198 return (0); 199 mediasize = g_mediasize(fd); 200 if (mediasize == -1) 201 mediasize = 0; 202 (void)g_close(fd); 203 return (mediasize); 204} 205 206unsigned int 207g_get_sectorsize(const char *name) 208{ 209 ssize_t sectorsize; 210 int fd; 211 212 fd = g_open(name, 0); 213 if (fd == -1) 214 return (0); 215 sectorsize = g_sectorsize(fd); 216 if (sectorsize == -1) 217 sectorsize = 0; 218 (void)g_close(fd); 219 return ((unsigned int)sectorsize); 220} 221 222int 223g_metadata_read(const char *name, unsigned char *md, size_t size, 224 const char *magic) 225{ 226 struct std_metadata stdmd; 227 unsigned char *sector; 228 ssize_t sectorsize; 229 off_t mediasize; 230 int error, fd; 231 232 sector = NULL; 233 error = 0; 234 235 fd = g_open(name, 0); 236 if (fd == -1) 237 return (errno); 238 mediasize = g_mediasize(fd); 239 if (mediasize == -1) { 240 error = errno; 241 goto out; 242 } 243 sectorsize = g_sectorsize(fd); 244 if (sectorsize == -1) { 245 error = errno; 246 goto out; 247 } 248 assert(sectorsize >= (ssize_t)size); 249 sector = malloc(sectorsize); 250 if (sector == NULL) { 251 error = ENOMEM; 252 goto out; 253 } 254 if (pread(fd, sector, sectorsize, mediasize - sectorsize) != 255 sectorsize) { 256 error = errno; 257 goto out; 258 } 259 if (magic != NULL) { 260 std_metadata_decode(sector, &stdmd); 261 if (strcmp(stdmd.md_magic, magic) != 0) { 262 error = EINVAL; 263 goto out; 264 } 265 } 266 bcopy(sector, md, size); 267out: 268 if (sector != NULL) 269 free(sector); 270 g_close(fd); 271 return (error); 272} 273 274/* 275 * Actually write the GEOM label to the provider 276 * 277 * @param name GEOM provider's name (ie "ada0") 278 * @param md Pointer to the label data to write 279 * @param size Size of the data pointed to by md 280 */ 281int 282g_metadata_store(const char *name, const unsigned char *md, size_t size) 283{ 284 unsigned char *sector; 285 ssize_t sectorsize; 286 off_t mediasize; 287 int error, fd; 288 289 sector = NULL; 290 error = 0; 291 292 fd = g_open(name, 1); 293 if (fd == -1) 294 return (errno); 295 mediasize = g_mediasize(fd); 296 if (mediasize == -1) { 297 error = errno; 298 goto out; 299 } 300 sectorsize = g_sectorsize(fd); 301 if (sectorsize == -1) { 302 error = errno; 303 goto out; 304 } 305 assert(sectorsize >= (ssize_t)size); 306 sector = malloc(sectorsize); 307 if (sector == NULL) { 308 error = ENOMEM; 309 goto out; 310 } 311 bcopy(md, sector, size); 312 bzero(sector + size, sectorsize - size); 313 if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) != 314 sectorsize) { 315 error = errno; 316 goto out; 317 } 318 (void)g_flush(fd); 319out: 320 if (sector != NULL) 321 free(sector); 322 (void)g_close(fd); 323 return (error); 324} 325 326int 327g_metadata_clear(const char *name, const char *magic) 328{ 329 struct std_metadata md; 330 unsigned char *sector; 331 ssize_t sectorsize; 332 off_t mediasize; 333 int error, fd; 334 335 sector = NULL; 336 error = 0; 337 338 fd = g_open(name, 1); 339 if (fd == -1) 340 return (errno); 341 mediasize = g_mediasize(fd); 342 if (mediasize == 0) { 343 error = errno; 344 goto out; 345 } 346 sectorsize = g_sectorsize(fd); 347 if (sectorsize <= 0) { 348 error = errno; 349 goto out; 350 } 351 sector = malloc(sectorsize); 352 if (sector == NULL) { 353 error = ENOMEM; 354 goto out; 355 } 356 if (magic != NULL) { 357 if (pread(fd, sector, sectorsize, mediasize - sectorsize) != 358 sectorsize) { 359 error = errno; 360 goto out; 361 } 362 std_metadata_decode(sector, &md); 363 if (strcmp(md.md_magic, magic) != 0) { 364 error = EINVAL; 365 goto out; 366 } 367 } 368 bzero(sector, sectorsize); 369 if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) != 370 sectorsize) { 371 error = errno; 372 goto out; 373 } 374 (void)g_flush(fd); 375out: 376 free(sector); 377 g_close(fd); 378 return (error); 379} 380 381/* 382 * Set an error message, if one does not already exist. 383 */ 384void 385gctl_error(struct gctl_req *req, const char *error, ...) 386{ 387 va_list ap; 388 389 if (req != NULL && req->error != NULL) 390 return; 391 va_start(ap, error); 392 if (req != NULL) { 393 vasprintf(&req->error, error, ap); 394 } else { 395 vfprintf(stderr, error, ap); 396 fprintf(stderr, "\n"); 397 } 398 va_end(ap); 399} 400 401static void * 402gctl_get_param(struct gctl_req *req, size_t len, const char *pfmt, va_list ap) 403{ 404 struct gctl_req_arg *argp; 405 char param[256]; 406 unsigned int i; 407 void *p; 408 409 vsnprintf(param, sizeof(param), pfmt, ap); 410 for (i = 0; i < req->narg; i++) { 411 argp = &req->arg[i]; 412 if (strcmp(param, argp->name)) 413 continue; 414 if (!(argp->flag & GCTL_PARAM_RD)) 415 continue; 416 p = argp->value; 417 if (len == 0) { 418 /* We are looking for a string. */ 419 if (argp->len < 1) { 420 fprintf(stderr, "No length argument (%s).\n", 421 param); 422 abort(); 423 } 424 if (((char *)p)[argp->len - 1] != '\0') { 425 fprintf(stderr, "Unterminated argument (%s).\n", 426 param); 427 abort(); 428 } 429 } else if ((int)len != argp->len) { 430 fprintf(stderr, "Wrong length %s argument.\n", param); 431 abort(); 432 } 433 return (p); 434 } 435 fprintf(stderr, "No such argument (%s).\n", param); 436 abort(); 437} 438 439int 440gctl_get_int(struct gctl_req *req, const char *pfmt, ...) 441{ 442 int *p; 443 va_list ap; 444 445 va_start(ap, pfmt); 446 p = gctl_get_param(req, sizeof(int), pfmt, ap); 447 va_end(ap); 448 return (*p); 449} 450 451intmax_t 452gctl_get_intmax(struct gctl_req *req, const char *pfmt, ...) 453{ 454 intmax_t *p; 455 va_list ap; 456 457 va_start(ap, pfmt); 458 p = gctl_get_param(req, sizeof(intmax_t), pfmt, ap); 459 va_end(ap); 460 return (*p); 461} 462 463const char * 464gctl_get_ascii(struct gctl_req *req, const char *pfmt, ...) 465{ 466 const char *p; 467 va_list ap; 468 469 va_start(ap, pfmt); 470 p = gctl_get_param(req, 0, pfmt, ap); 471 va_end(ap); 472 return (p); 473} 474 475int 476gctl_change_param(struct gctl_req *req, const char *name, int len, 477 const void *value) 478{ 479 struct gctl_req_arg *ap; 480 unsigned int i; 481 482 if (req == NULL || req->error != NULL) 483 return (EDOOFUS); 484 for (i = 0; i < req->narg; i++) { 485 ap = &req->arg[i]; 486 if (strcmp(ap->name, name) != 0) 487 continue; 488 ap->value = __DECONST(void *, value); 489 if (len >= 0) { 490 ap->flag &= ~GCTL_PARAM_ASCII; 491 ap->len = len; 492 } else if (len < 0) { 493 ap->flag |= GCTL_PARAM_ASCII; 494 ap->len = strlen(value) + 1; 495 } 496 return (0); 497 } 498 return (ENOENT); 499} 500 501int 502gctl_delete_param(struct gctl_req *req, const char *name) 503{ 504 struct gctl_req_arg *ap; 505 unsigned int i; 506 507 if (req == NULL || req->error != NULL) 508 return (EDOOFUS); 509 510 i = 0; 511 while (i < req->narg) { 512 ap = &req->arg[i]; 513 if (strcmp(ap->name, name) == 0) 514 break; 515 i++; 516 } 517 if (i == req->narg) 518 return (ENOENT); 519 520 free(ap->name); 521 req->narg--; 522 while (i < req->narg) { 523 req->arg[i] = req->arg[i + 1]; 524 i++; 525 } 526 return (0); 527} 528 529int 530gctl_has_param(struct gctl_req *req, const char *name) 531{ 532 struct gctl_req_arg *ap; 533 unsigned int i; 534 535 if (req == NULL || req->error != NULL) 536 return (0); 537 538 for (i = 0; i < req->narg; i++) { 539 ap = &req->arg[i]; 540 if (strcmp(ap->name, name) == 0) 541 return (1); 542 } 543 return (0); 544} 545