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