xref: /freebsd/sbin/geom/misc/subr.c (revision 1d386b48)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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 #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 
51 struct std_metadata {
52 	char		md_magic[16];
53 	uint32_t	md_version;
54 };
55 
56 static void
57 std_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  */
67 static unsigned int
68 gcd(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  */
83 unsigned int
84 g_lcm(unsigned int a, unsigned int b)
85 {
86 
87 	return ((a * b) / gcd(a, b));
88 }
89 
90 uint32_t
91 bitcount32(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  */
121 int
122 g_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;
165 sfx:
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);
179 done:
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 
190 off_t
191 g_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 
206 unsigned int
207 g_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 
222 int
223 g_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);
267 out:
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  */
281 int
282 g_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);
319 out:
320 	if (sector != NULL)
321 		free(sector);
322 	(void)g_close(fd);
323 	return (error);
324 }
325 
326 int
327 g_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);
375 out:
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  */
384 void
385 gctl_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 	if (req != NULL && req->nerror == 0)
400 		req->nerror = EINVAL;
401 }
402 
403 static void *
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
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
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 *
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
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
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
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