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