subr.c revision 155175
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 155175 2006-02-01 12:11:37Z 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	}
239out:
240	if (sector != NULL)
241		free(sector);
242	close(fd);
243	return (error);
244}
245
246int
247g_metadata_clear(const char *name, const char *magic)
248{
249	struct std_metadata md;
250	char path[MAXPATHLEN];
251	unsigned sectorsize;
252	off_t mediasize;
253	u_char *sector;
254	int error, fd;
255
256	pathgen(name, path, sizeof(path));
257	sector = NULL;
258	error = 0;
259
260	fd = open(path, O_RDWR);
261	if (fd == -1)
262		return (errno);
263	mediasize = g_get_mediasize(name);
264	if (mediasize == 0) {
265		error = errno;
266		goto out;
267	}
268	sectorsize = g_get_sectorsize(name);
269	if (sectorsize == 0) {
270		error = errno;
271		goto out;
272	}
273	sector = malloc(sectorsize);
274	if (sector == NULL) {
275		error = ENOMEM;
276		goto out;
277	}
278	if (magic != NULL) {
279		if (pread(fd, sector, sectorsize, mediasize - sectorsize) !=
280		    (ssize_t)sectorsize) {
281			error = errno;
282			goto out;
283		}
284		std_metadata_decode(sector, &md);
285		if (strcmp(md.md_magic, magic) != 0) {
286			error = EINVAL;
287			goto out;
288		}
289	}
290	bzero(sector, sectorsize);
291	if (pwrite(fd, sector, sectorsize, mediasize - sectorsize) !=
292	    (ssize_t)sectorsize) {
293		error = errno;
294		goto out;
295	}
296out:
297	if (sector != NULL)
298		free(sector);
299	close(fd);
300	return (error);
301}
302
303/*
304 * Set an error message, if one does not already exist.
305 */
306void
307gctl_error(struct gctl_req *req, const char *error, ...)
308{
309	va_list ap;
310
311	if (req->error != NULL)
312		return;
313	va_start(ap, error);
314	vasprintf(&req->error, error, ap);
315	va_end(ap);
316}
317
318static void *
319gctl_get_param(struct gctl_req *req, size_t len, const char *pfmt, va_list ap)
320{
321	struct gctl_req_arg *argp;
322	char param[256];
323	void *p;
324	unsigned i;
325
326	vsnprintf(param, sizeof(param), pfmt, ap);
327	for (i = 0; i < req->narg; i++) {
328		argp = &req->arg[i];
329		if (strcmp(param, argp->name))
330			continue;
331		if (!(argp->flag & GCTL_PARAM_RD))
332			continue;
333		p = argp->value;
334		if (len == 0) {
335			/* We are looking for a string. */
336			if (argp->len < 1) {
337				fprintf(stderr, "No length argument (%s).\n",
338				    param);
339				abort();
340			}
341			if (((char *)p)[argp->len - 1] != '\0') {
342				fprintf(stderr, "Unterminated argument (%s).\n",
343				    param);
344				abort();
345			}
346		} else if ((int)len != argp->len) {
347			fprintf(stderr, "Wrong length %s argument.\n", param);
348			abort();
349		}
350		return (p);
351	}
352	fprintf(stderr, "No such argument (%s).\n", param);
353	abort();
354}
355
356int
357gctl_get_int(struct gctl_req *req, const char *pfmt, ...)
358{
359	int *p;
360	va_list ap;
361
362	va_start(ap, pfmt);
363	p = gctl_get_param(req, sizeof(int), pfmt, ap);
364	va_end(ap);
365	return (*p);
366}
367
368intmax_t
369gctl_get_intmax(struct gctl_req *req, const char *pfmt, ...)
370{
371	intmax_t *p;
372	va_list ap;
373
374	va_start(ap, pfmt);
375	p = gctl_get_param(req, sizeof(intmax_t), pfmt, ap);
376	va_end(ap);
377	return (*p);
378}
379
380const char *
381gctl_get_ascii(struct gctl_req *req, const char *pfmt, ...)
382{
383	const char *p;
384	va_list ap;
385
386	va_start(ap, pfmt);
387	p = gctl_get_param(req, 0, pfmt, ap);
388	va_end(ap);
389	return (p);
390}
391