xref: /freebsd/sbin/geom/misc/subr.c (revision d06955f9bdb1416d9196043ed781f9b36dae9adc)
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$");
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
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
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
86 g_lcm(unsigned int a, unsigned int b)
87 {
88 
89 	return ((a * b) / gcd(a, b));
90 }
91 
92 uint32_t
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
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
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
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
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 int
277 g_metadata_store(const char *name, const unsigned char *md, size_t size)
278 {
279 	unsigned char *sector;
280 	ssize_t sectorsize;
281 	off_t mediasize;
282 	int error, fd;
283 
284 	sector = NULL;
285 	error = 0;
286 
287 	fd = g_open(name, 1);
288 	if (fd == -1)
289 		return (errno);
290 	mediasize = g_mediasize(fd);
291 	if (mediasize == -1) {
292 		error = errno;
293 		goto out;
294 	}
295 	sectorsize = g_sectorsize(fd);
296 	if (sectorsize == -1) {
297 		error = errno;
298 		goto out;
299 	}
300 	assert(sectorsize >= (ssize_t)size);
301 	sector = malloc(sectorsize);
302 	if (sector == NULL) {
303 		error = ENOMEM;
304 		goto out;
305 	}
306 	bcopy(md, sector, size);
307 	bzero(sector + size, sectorsize - size);
308 	if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) !=
309 	    sectorsize) {
310 		error = errno;
311 		goto out;
312 	}
313 	(void)g_flush(fd);
314 out:
315 	if (sector != NULL)
316 		free(sector);
317 	(void)g_close(fd);
318 	return (error);
319 }
320 
321 int
322 g_metadata_clear(const char *name, const char *magic)
323 {
324 	struct std_metadata md;
325 	unsigned char *sector;
326 	ssize_t sectorsize;
327 	off_t mediasize;
328 	int error, fd;
329 
330 	sector = NULL;
331 	error = 0;
332 
333 	fd = g_open(name, 1);
334 	if (fd == -1)
335 		return (errno);
336 	mediasize = g_mediasize(fd);
337 	if (mediasize == 0) {
338 		error = errno;
339 		goto out;
340 	}
341 	sectorsize = g_sectorsize(fd);
342 	if (sectorsize <= 0) {
343 		error = errno;
344 		goto out;
345 	}
346 	sector = malloc(sectorsize);
347 	if (sector == NULL) {
348 		error = ENOMEM;
349 		goto out;
350 	}
351 	if (magic != NULL) {
352 		if (pread(fd, sector, sectorsize, mediasize - sectorsize) !=
353 		    sectorsize) {
354 			error = errno;
355 			goto out;
356 		}
357 		std_metadata_decode(sector, &md);
358 		if (strcmp(md.md_magic, magic) != 0) {
359 			error = EINVAL;
360 			goto out;
361 		}
362 	}
363 	bzero(sector, sectorsize);
364 	if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) !=
365 	    sectorsize) {
366 		error = errno;
367 		goto out;
368 	}
369 	(void)g_flush(fd);
370 out:
371 	free(sector);
372 	g_close(fd);
373 	return (error);
374 }
375 
376 /*
377  * Set an error message, if one does not already exist.
378  */
379 void
380 gctl_error(struct gctl_req *req, const char *error, ...)
381 {
382 	va_list ap;
383 
384 	if (req != NULL && req->error != NULL)
385 		return;
386 	va_start(ap, error);
387 	if (req != NULL) {
388 		vasprintf(&req->error, error, ap);
389 	} else {
390 		vfprintf(stderr, error, ap);
391 		fprintf(stderr, "\n");
392 	}
393 	va_end(ap);
394 }
395 
396 static void *
397 gctl_get_param(struct gctl_req *req, size_t len, const char *pfmt, va_list ap)
398 {
399 	struct gctl_req_arg *argp;
400 	char param[256];
401 	unsigned int i;
402 	void *p;
403 
404 	vsnprintf(param, sizeof(param), pfmt, ap);
405 	for (i = 0; i < req->narg; i++) {
406 		argp = &req->arg[i];
407 		if (strcmp(param, argp->name))
408 			continue;
409 		if (!(argp->flag & GCTL_PARAM_RD))
410 			continue;
411 		p = argp->value;
412 		if (len == 0) {
413 			/* We are looking for a string. */
414 			if (argp->len < 1) {
415 				fprintf(stderr, "No length argument (%s).\n",
416 				    param);
417 				abort();
418 			}
419 			if (((char *)p)[argp->len - 1] != '\0') {
420 				fprintf(stderr, "Unterminated argument (%s).\n",
421 				    param);
422 				abort();
423 			}
424 		} else if ((int)len != argp->len) {
425 			fprintf(stderr, "Wrong length %s argument.\n", param);
426 			abort();
427 		}
428 		return (p);
429 	}
430 	fprintf(stderr, "No such argument (%s).\n", param);
431 	abort();
432 }
433 
434 int
435 gctl_get_int(struct gctl_req *req, const char *pfmt, ...)
436 {
437 	int *p;
438 	va_list ap;
439 
440 	va_start(ap, pfmt);
441 	p = gctl_get_param(req, sizeof(int), pfmt, ap);
442 	va_end(ap);
443 	return (*p);
444 }
445 
446 intmax_t
447 gctl_get_intmax(struct gctl_req *req, const char *pfmt, ...)
448 {
449 	intmax_t *p;
450 	va_list ap;
451 
452 	va_start(ap, pfmt);
453 	p = gctl_get_param(req, sizeof(intmax_t), pfmt, ap);
454 	va_end(ap);
455 	return (*p);
456 }
457 
458 const char *
459 gctl_get_ascii(struct gctl_req *req, const char *pfmt, ...)
460 {
461 	const char *p;
462 	va_list ap;
463 
464 	va_start(ap, pfmt);
465 	p = gctl_get_param(req, 0, pfmt, ap);
466 	va_end(ap);
467 	return (p);
468 }
469 
470 int
471 gctl_change_param(struct gctl_req *req, const char *name, int len,
472     const void *value)
473 {
474 	struct gctl_req_arg *ap;
475 	unsigned int i;
476 
477 	if (req == NULL || req->error != NULL)
478 		return (EDOOFUS);
479 	for (i = 0; i < req->narg; i++) {
480 		ap = &req->arg[i];
481 		if (strcmp(ap->name, name) != 0)
482 			continue;
483 		ap->value = __DECONST(void *, value);
484 		if (len >= 0) {
485 			ap->flag &= ~GCTL_PARAM_ASCII;
486 			ap->len = len;
487 		} else if (len < 0) {
488 			ap->flag |= GCTL_PARAM_ASCII;
489 			ap->len = strlen(value) + 1;
490 		}
491 		return (0);
492 	}
493 	return (ENOENT);
494 }
495 
496 int
497 gctl_delete_param(struct gctl_req *req, const char *name)
498 {
499 	struct gctl_req_arg *ap;
500 	unsigned int i;
501 
502 	if (req == NULL || req->error != NULL)
503 		return (EDOOFUS);
504 
505 	i = 0;
506 	while (i < req->narg) {
507 		ap = &req->arg[i];
508 		if (strcmp(ap->name, name) == 0)
509 			break;
510 		i++;
511 	}
512 	if (i == req->narg)
513 		return (ENOENT);
514 
515 	free(ap->name);
516 	req->narg--;
517 	while (i < req->narg) {
518 		req->arg[i] = req->arg[i + 1];
519 		i++;
520 	}
521 	return (0);
522 }
523 
524 int
525 gctl_has_param(struct gctl_req *req, const char *name)
526 {
527 	struct gctl_req_arg *ap;
528 	unsigned int i;
529 
530 	if (req == NULL || req->error != NULL)
531 		return (0);
532 
533 	for (i = 0; i < req->narg; i++) {
534 		ap = &req->arg[i];
535 		if (strcmp(ap->name, name) == 0)
536 			return (1);
537 	}
538 	return (0);
539 }
540