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