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