subr.c revision 206666
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 206666 2010-04-15 16:35:34Z pjd $");
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 <stdarg.h>
40#include <string.h>
41#include <strings.h>
42#include <unistd.h>
43#include <assert.h>
44#include <libgeom.h>
45
46#include "misc/subr.h"
47
48
49struct std_metadata {
50	char		md_magic[16];
51	uint32_t	md_version;
52};
53
54static void
55std_metadata_decode(const u_char *data, struct std_metadata *md)
56{
57
58        bcopy(data, md->md_magic, sizeof(md->md_magic));
59        md->md_version = le32dec(data + 16);
60}
61
62static void
63pathgen(const char *name, char *path, size_t size)
64{
65
66	if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) != 0)
67		snprintf(path, size, "%s%s", _PATH_DEV, name);
68	else
69		strlcpy(path, name, size);
70}
71
72/*
73 * Greatest Common Divisor.
74 */
75static unsigned
76gcd(unsigned a, unsigned b)
77{
78	u_int c;
79
80	while (b != 0) {
81		c = a;
82		a = b;
83		b = (c % b);
84	}
85	return (a);
86}
87
88/*
89 * Least Common Multiple.
90 */
91unsigned
92g_lcm(unsigned a, unsigned b)
93{
94
95	return ((a * b) / gcd(a, b));
96}
97
98uint32_t
99bitcount32(uint32_t x)
100{
101
102	x = (x & 0x55555555) + ((x & 0xaaaaaaaa) >> 1);
103	x = (x & 0x33333333) + ((x & 0xcccccccc) >> 2);
104	x = (x & 0x0f0f0f0f) + ((x & 0xf0f0f0f0) >> 4);
105	x = (x & 0x00ff00ff) + ((x & 0xff00ff00) >> 8);
106	x = (x & 0x0000ffff) + ((x & 0xffff0000) >> 16);
107	return (x);
108}
109
110off_t
111g_get_mediasize(const char *name)
112{
113	char path[MAXPATHLEN];
114	off_t mediasize;
115	int fd;
116
117	pathgen(name, path, sizeof(path));
118	fd = open(path, O_RDONLY);
119	if (fd == -1)
120		return (0);
121	if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) {
122		close(fd);
123		return (0);
124	}
125	close(fd);
126	return (mediasize);
127}
128
129unsigned
130g_get_sectorsize(const char *name)
131{
132	char path[MAXPATHLEN];
133	unsigned sectorsize;
134	int fd;
135
136	pathgen(name, path, sizeof(path));
137	fd = open(path, O_RDONLY);
138	if (fd == -1)
139		return (0);
140	if (ioctl(fd, DIOCGSECTORSIZE, &sectorsize) < 0) {
141		close(fd);
142		return (0);
143	}
144	close(fd);
145	return (sectorsize);
146}
147
148int
149g_metadata_read(const char *name, u_char *md, size_t size, const char *magic)
150{
151	struct std_metadata stdmd;
152	char path[MAXPATHLEN];
153	unsigned sectorsize;
154	off_t mediasize;
155	u_char *sector;
156	int error, fd;
157
158	pathgen(name, path, sizeof(path));
159	sector = NULL;
160	error = 0;
161
162	fd = open(path, O_RDONLY);
163	if (fd == -1)
164		return (errno);
165	mediasize = g_get_mediasize(name);
166	if (mediasize == 0) {
167		error = errno;
168		goto out;
169	}
170	sectorsize = g_get_sectorsize(name);
171	if (sectorsize == 0) {
172		error = errno;
173		goto out;
174	}
175	assert(sectorsize >= size);
176	sector = malloc(sectorsize);
177	if (sector == NULL) {
178		error = ENOMEM;
179		goto out;
180	}
181	if (pread(fd, sector, sectorsize, mediasize - sectorsize) !=
182	    (ssize_t)sectorsize) {
183		error = errno;
184		goto out;
185	}
186	if (magic != NULL) {
187		std_metadata_decode(sector, &stdmd);
188		if (strcmp(stdmd.md_magic, magic) != 0) {
189			error = EINVAL;
190			goto out;
191		}
192	}
193	bcopy(sector, md, size);
194out:
195	if (sector != NULL)
196		free(sector);
197	close(fd);
198	return (error);
199}
200
201int
202g_metadata_store(const char *name, u_char *md, size_t size)
203{
204	char path[MAXPATHLEN];
205	unsigned sectorsize;
206	off_t mediasize;
207	u_char *sector;
208	int error, fd;
209
210	pathgen(name, path, sizeof(path));
211	sector = NULL;
212	error = 0;
213
214	fd = open(path, O_WRONLY);
215	if (fd == -1)
216		return (errno);
217	mediasize = g_get_mediasize(name);
218	if (mediasize == 0) {
219		error = errno;
220		goto out;
221	}
222	sectorsize = g_get_sectorsize(name);
223	if (sectorsize == 0) {
224		error = errno;
225		goto out;
226	}
227	assert(sectorsize >= size);
228	sector = malloc(sectorsize);
229	if (sector == NULL) {
230		error = ENOMEM;
231		goto out;
232	}
233	bcopy(md, sector, size);
234	if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) !=
235	    (ssize_t)sectorsize) {
236		error = errno;
237		goto out;
238	}
239	(void)ioctl(fd, DIOCGFLUSH, NULL);
240out:
241	if (sector != NULL)
242		free(sector);
243	close(fd);
244	return (error);
245}
246
247int
248g_metadata_clear(const char *name, const char *magic)
249{
250	struct std_metadata md;
251	char path[MAXPATHLEN];
252	unsigned sectorsize;
253	off_t mediasize;
254	u_char *sector;
255	int error, fd;
256
257	pathgen(name, path, sizeof(path));
258	sector = NULL;
259	error = 0;
260
261	fd = open(path, O_RDWR);
262	if (fd == -1)
263		return (errno);
264	mediasize = g_get_mediasize(name);
265	if (mediasize == 0) {
266		error = errno;
267		goto out;
268	}
269	sectorsize = g_get_sectorsize(name);
270	if (sectorsize == 0) {
271		error = errno;
272		goto out;
273	}
274	sector = malloc(sectorsize);
275	if (sector == NULL) {
276		error = ENOMEM;
277		goto out;
278	}
279	if (magic != NULL) {
280		if (pread(fd, sector, sectorsize, mediasize - sectorsize) !=
281		    (ssize_t)sectorsize) {
282			error = errno;
283			goto out;
284		}
285		std_metadata_decode(sector, &md);
286		if (strcmp(md.md_magic, magic) != 0) {
287			error = EINVAL;
288			goto out;
289		}
290	}
291	bzero(sector, sectorsize);
292	if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) !=
293	    (ssize_t)sectorsize) {
294		error = errno;
295		goto out;
296	}
297	(void)ioctl(fd, DIOCGFLUSH, NULL);
298out:
299	if (sector != NULL)
300		free(sector);
301	close(fd);
302	return (error);
303}
304
305/*
306 * Set an error message, if one does not already exist.
307 */
308void
309gctl_error(struct gctl_req *req, const char *error, ...)
310{
311	va_list ap;
312
313	if (req->error != NULL)
314		return;
315	va_start(ap, error);
316	vasprintf(&req->error, error, ap);
317	va_end(ap);
318}
319
320static void *
321gctl_get_param(struct gctl_req *req, size_t len, const char *pfmt, va_list ap)
322{
323	struct gctl_req_arg *argp;
324	char param[256];
325	void *p;
326	unsigned i;
327
328	vsnprintf(param, sizeof(param), pfmt, ap);
329	for (i = 0; i < req->narg; i++) {
330		argp = &req->arg[i];
331		if (strcmp(param, argp->name))
332			continue;
333		if (!(argp->flag & GCTL_PARAM_RD))
334			continue;
335		p = argp->value;
336		if (len == 0) {
337			/* We are looking for a string. */
338			if (argp->len < 1) {
339				fprintf(stderr, "No length argument (%s).\n",
340				    param);
341				abort();
342			}
343			if (((char *)p)[argp->len - 1] != '\0') {
344				fprintf(stderr, "Unterminated argument (%s).\n",
345				    param);
346				abort();
347			}
348		} else if ((int)len != argp->len) {
349			fprintf(stderr, "Wrong length %s argument.\n", param);
350			abort();
351		}
352		return (p);
353	}
354	fprintf(stderr, "No such argument (%s).\n", param);
355	abort();
356}
357
358int
359gctl_get_int(struct gctl_req *req, const char *pfmt, ...)
360{
361	int *p;
362	va_list ap;
363
364	va_start(ap, pfmt);
365	p = gctl_get_param(req, sizeof(int), pfmt, ap);
366	va_end(ap);
367	return (*p);
368}
369
370intmax_t
371gctl_get_intmax(struct gctl_req *req, const char *pfmt, ...)
372{
373	intmax_t *p;
374	va_list ap;
375
376	va_start(ap, pfmt);
377	p = gctl_get_param(req, sizeof(intmax_t), pfmt, ap);
378	va_end(ap);
379	return (*p);
380}
381
382const char *
383gctl_get_ascii(struct gctl_req *req, const char *pfmt, ...)
384{
385	const char *p;
386	va_list ap;
387
388	va_start(ap, pfmt);
389	p = gctl_get_param(req, 0, pfmt, ap);
390	va_end(ap);
391	return (p);
392}
393
394int
395gctl_change_param(struct gctl_req *req, const char *name, int len,
396    const void *value)
397{
398	struct gctl_req_arg *ap;
399	unsigned i;
400
401	if (req == NULL || req->error != NULL)
402		return (EDOOFUS);
403	for (i = 0; i < req->narg; i++) {
404		ap = &req->arg[i];
405		if (strcmp(ap->name, name) != 0)
406			continue;
407		ap->value = __DECONST(void *, value);
408		if (len >= 0) {
409			ap->flag &= ~GCTL_PARAM_ASCII;
410			ap->len = len;
411		} else if (len < 0) {
412			ap->flag |= GCTL_PARAM_ASCII;
413			ap->len = strlen(value) + 1;
414		}
415		return (0);
416	}
417	return (ENOENT);
418}
419
420int
421gctl_delete_param(struct gctl_req *req, const char *name)
422{
423	struct gctl_req_arg *ap;
424	unsigned int i;
425
426	if (req == NULL || req->error != NULL)
427		return (EDOOFUS);
428
429	i = 0;
430	while (i < req->narg) {
431		ap = &req->arg[i];
432		if (strcmp(ap->name, name) == 0)
433			break;
434		i++;
435	}
436	if (i == req->narg)
437		return (ENOENT);
438
439	req->narg--;
440	while (i < req->narg) {
441		req->arg[i] = req->arg[i + 1];
442		i++;
443	}
444	return (0);
445}
446
447int
448gctl_has_param(struct gctl_req *req, const char *name)
449{
450	struct gctl_req_arg *ap;
451	unsigned int i;
452
453	if (req == NULL || req->error != NULL)
454		return (0);
455
456	for (i = 0; i < req->narg; i++) {
457		ap = &req->arg[i];
458		if (strcmp(ap->name, name) == 0)
459			return (1);
460	}
461	return (0);
462}
463