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