subr.c revision 209392
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: head/sbin/geom/misc/subr.c 209392 2010-06-21 12:50:54Z ae $");
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
51struct std_metadata {
52	char		md_magic[16];
53	uint32_t	md_version;
54};
55
56static void
57std_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
64static void
65pathgen(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 */
77static unsigned
78gcd(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 */
93unsigned
94g_lcm(unsigned a, unsigned b)
95{
96
97	return ((a * b) / gcd(a, b));
98}
99
100uint32_t
101bitcount32(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 */
131int
132g_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;
175sfx:
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);
189done:
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
200off_t
201g_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
219unsigned
220g_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
238int
239g_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);
284out:
285	if (sector != NULL)
286		free(sector);
287	close(fd);
288	return (error);
289}
290
291int
292g_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);
330out:
331	if (sector != NULL)
332		free(sector);
333	close(fd);
334	return (error);
335}
336
337int
338g_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);
388out:
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 */
398void
399gctl_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
410static void *
411gctl_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
448int
449gctl_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
460intmax_t
461gctl_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
472const char *
473gctl_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
484int
485gctl_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
510int
511gctl_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
538int
539gctl_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