xref: /freebsd/sbin/geom/misc/subr.c (revision 3078531de10dcae44b253a35125c949ff4235284)
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 /*
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
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
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
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 	if (req != NULL && req->nerror == 0)
402 		req->nerror = EINVAL;
403 }
404 
405 static void *
406 gctl_get_param(struct gctl_req *req, size_t len, const char *pfmt, va_list ap)
407 {
408 	struct gctl_req_arg *argp;
409 	char param[256];
410 	unsigned int i;
411 	void *p;
412 
413 	vsnprintf(param, sizeof(param), pfmt, ap);
414 	for (i = 0; i < req->narg; i++) {
415 		argp = &req->arg[i];
416 		if (strcmp(param, argp->name))
417 			continue;
418 		if (!(argp->flag & GCTL_PARAM_RD))
419 			continue;
420 		p = argp->value;
421 		if (len == 0) {
422 			/* We are looking for a string. */
423 			if (argp->len < 1) {
424 				fprintf(stderr, "No length argument (%s).\n",
425 				    param);
426 				abort();
427 			}
428 			if (((char *)p)[argp->len - 1] != '\0') {
429 				fprintf(stderr, "Unterminated argument (%s).\n",
430 				    param);
431 				abort();
432 			}
433 		} else if ((int)len != argp->len) {
434 			fprintf(stderr, "Wrong length %s argument.\n", param);
435 			abort();
436 		}
437 		return (p);
438 	}
439 	fprintf(stderr, "No such argument (%s).\n", param);
440 	abort();
441 }
442 
443 int
444 gctl_get_int(struct gctl_req *req, const char *pfmt, ...)
445 {
446 	int *p;
447 	va_list ap;
448 
449 	va_start(ap, pfmt);
450 	p = gctl_get_param(req, sizeof(int), pfmt, ap);
451 	va_end(ap);
452 	return (*p);
453 }
454 
455 intmax_t
456 gctl_get_intmax(struct gctl_req *req, const char *pfmt, ...)
457 {
458 	intmax_t *p;
459 	va_list ap;
460 
461 	va_start(ap, pfmt);
462 	p = gctl_get_param(req, sizeof(intmax_t), pfmt, ap);
463 	va_end(ap);
464 	return (*p);
465 }
466 
467 const char *
468 gctl_get_ascii(struct gctl_req *req, const char *pfmt, ...)
469 {
470 	const char *p;
471 	va_list ap;
472 
473 	va_start(ap, pfmt);
474 	p = gctl_get_param(req, 0, pfmt, ap);
475 	va_end(ap);
476 	return (p);
477 }
478 
479 int
480 gctl_change_param(struct gctl_req *req, const char *name, int len,
481     const void *value)
482 {
483 	struct gctl_req_arg *ap;
484 	unsigned int i;
485 
486 	if (req == NULL || req->error != NULL)
487 		return (EDOOFUS);
488 	for (i = 0; i < req->narg; i++) {
489 		ap = &req->arg[i];
490 		if (strcmp(ap->name, name) != 0)
491 			continue;
492 		ap->value = __DECONST(void *, value);
493 		if (len >= 0) {
494 			ap->flag &= ~GCTL_PARAM_ASCII;
495 			ap->len = len;
496 		} else if (len < 0) {
497 			ap->flag |= GCTL_PARAM_ASCII;
498 			ap->len = strlen(value) + 1;
499 		}
500 		return (0);
501 	}
502 	return (ENOENT);
503 }
504 
505 int
506 gctl_delete_param(struct gctl_req *req, const char *name)
507 {
508 	struct gctl_req_arg *ap;
509 	unsigned int i;
510 
511 	if (req == NULL || req->error != NULL)
512 		return (EDOOFUS);
513 
514 	i = 0;
515 	while (i < req->narg) {
516 		ap = &req->arg[i];
517 		if (strcmp(ap->name, name) == 0)
518 			break;
519 		i++;
520 	}
521 	if (i == req->narg)
522 		return (ENOENT);
523 
524 	free(ap->name);
525 	req->narg--;
526 	while (i < req->narg) {
527 		req->arg[i] = req->arg[i + 1];
528 		i++;
529 	}
530 	return (0);
531 }
532 
533 int
534 gctl_has_param(struct gctl_req *req, const char *name)
535 {
536 	struct gctl_req_arg *ap;
537 	unsigned int i;
538 
539 	if (req == NULL || req->error != NULL)
540 		return (0);
541 
542 	for (i = 0; i < req->narg; i++) {
543 		ap = &req->arg[i];
544 		if (strcmp(ap->name, name) == 0)
545 			return (1);
546 	}
547 	return (0);
548 }
549