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