1169299Spjd/*-
2169299Spjd * Copyright (c) 2007 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3169299Spjd * All rights reserved.
4169299Spjd *
5169299Spjd * Redistribution and use in source and binary forms, with or without
6169299Spjd * modification, are permitted provided that the following conditions
7169299Spjd * are met:
8169299Spjd * 1. Redistributions of source code must retain the above copyright
9169299Spjd *    notice, this list of conditions and the following disclaimer.
10169299Spjd * 2. Redistributions in binary form must reproduce the above copyright
11169299Spjd *    notice, this list of conditions and the following disclaimer in the
12169299Spjd *    documentation and/or other materials provided with the distribution.
13169299Spjd *
14169299Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15169299Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16169299Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17169299Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18169299Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19169299Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20169299Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21169299Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22169299Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23169299Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24169299Spjd * SUCH DAMAGE.
25169299Spjd */
26169299Spjd
27169299Spjd#include <sys/cdefs.h>
28169299Spjd__FBSDID("$FreeBSD$");
29169299Spjd
30169299Spjd#include <sys/param.h>
31169299Spjd#include <sys/disk.h>
32169299Spjd#include <sys/stat.h>
33169299Spjd
34169299Spjd#include <stdio.h>
35169299Spjd#include <fcntl.h>
36169299Spjd#include <errno.h>
37169299Spjd#include <stdint.h>
38169299Spjd#include <unistd.h>
39169299Spjd#include <string.h>
40169299Spjd#include <stdlib.h>
41169299Spjd#include <paths.h>
42169299Spjd
43169299Spjd#include <libgeom.h>
44169299Spjd
45182843Slulfstatic char	*g_device_path_open(const char *, int *, int);
46182843Slulf
47169299Spjd/*
48169299Spjd * Open the given provider and at least check if this is a block device.
49169299Spjd */
50169299Spjdint
51179152Spjdg_open(const char *name, int dowrite)
52169299Spjd{
53182843Slulf	char *path;
54169299Spjd	int fd;
55169299Spjd
56182843Slulf	path = g_device_path_open(name, &fd, dowrite);
57182843Slulf	if (path != NULL)
58182843Slulf		free(path);
59169299Spjd	if (fd == -1)
60169299Spjd		return (-1);
61169299Spjd	return (fd);
62169299Spjd}
63169299Spjd
64169299Spjdint
65169299Spjdg_close(int fd)
66169299Spjd{
67169299Spjd
68169299Spjd	return (close(fd));
69169299Spjd}
70169299Spjd
71169299Spjdstatic int
72169299Spjdg_ioctl_arg(int fd, unsigned long cmd, void *arg)
73169299Spjd{
74169299Spjd	int ret;
75169299Spjd
76169299Spjd	if (arg != NULL)
77169299Spjd		ret = ioctl(fd, cmd, arg);
78169299Spjd	else
79169299Spjd		ret = ioctl(fd, cmd);
80169299Spjd	return (ret >= 0 ? 0 : -1);
81169299Spjd}
82169299Spjd
83169299Spjdstatic int
84169299Spjdg_ioctl(int fd, unsigned long cmd)
85169299Spjd{
86169299Spjd
87169299Spjd	return (g_ioctl_arg(fd, cmd, NULL));
88169299Spjd}
89169299Spjd
90169299Spjd/*
91169299Spjd * Return media size of the given provider.
92169299Spjd */
93169299Spjdoff_t
94169299Spjdg_mediasize(int fd)
95169299Spjd{
96169299Spjd	off_t mediasize;
97169299Spjd
98169299Spjd	if (g_ioctl_arg(fd, DIOCGMEDIASIZE, &mediasize) == -1)
99169299Spjd		mediasize = -1;
100169299Spjd	return (mediasize);
101169299Spjd}
102169299Spjd
103169299Spjd/*
104169299Spjd * Return sector size of the given provider.
105169299Spjd */
106169299Spjdssize_t
107169299Spjdg_sectorsize(int fd)
108169299Spjd{
109169299Spjd	u_int sectorsize;
110169299Spjd
111169299Spjd	if (g_ioctl_arg(fd, DIOCGSECTORSIZE, &sectorsize) == -1)
112169299Spjd		return (-1);
113169299Spjd	return ((ssize_t)sectorsize);
114169299Spjd}
115169299Spjd
116169299Spjd/*
117202454Sdelphij * Return stripe size of the given provider.
118202454Sdelphij */
119202454Sdelphijoff_t
120202454Sdelphijg_stripesize(int fd)
121202454Sdelphij{
122202454Sdelphij	off_t stripesize;
123202454Sdelphij
124202454Sdelphij	if (g_ioctl_arg(fd, DIOCGSTRIPESIZE, &stripesize) == -1)
125202454Sdelphij		return (-1);
126202454Sdelphij	return (stripesize);
127202454Sdelphij}
128202454Sdelphij
129202454Sdelphij/*
130202454Sdelphij * Return stripe size of the given provider.
131202454Sdelphij */
132202454Sdelphijoff_t
133202454Sdelphijg_stripeoffset(int fd)
134202454Sdelphij{
135202454Sdelphij	off_t stripeoffset;
136202454Sdelphij
137202454Sdelphij	if (g_ioctl_arg(fd, DIOCGSTRIPEOFFSET, &stripeoffset) == -1)
138202454Sdelphij		return (-1);
139202454Sdelphij	return (stripeoffset);
140202454Sdelphij}
141202454Sdelphij
142202454Sdelphij/*
143182843Slulf * Return the correct provider name.
144182843Slulf */
145182843Slulfchar *
146182843Slulfg_providername(int fd)
147182843Slulf{
148182843Slulf	char name[MAXPATHLEN];
149182843Slulf
150182843Slulf	if (g_ioctl_arg(fd, DIOCGPROVIDERNAME, name) == -1)
151182843Slulf		return (NULL);
152182843Slulf	return (strdup(name));
153182843Slulf}
154182843Slulf
155182843Slulf/*
156169299Spjd * Call BIO_FLUSH for the given provider.
157169299Spjd */
158169299Spjdint
159169299Spjdg_flush(int fd)
160169299Spjd{
161169299Spjd
162169299Spjd	return (g_ioctl(fd, DIOCGFLUSH));
163169299Spjd}
164169299Spjd
165169299Spjd/*
166169299Spjd * Call BIO_DELETE for the given range.
167169299Spjd */
168169299Spjdint
169169299Spjdg_delete(int fd, off_t offset, off_t length)
170169299Spjd{
171169299Spjd	off_t arg[2];
172169299Spjd
173169299Spjd	arg[0] = offset;
174169299Spjd	arg[1] = length;
175169299Spjd	return (g_ioctl_arg(fd, DIOCGDELETE, arg));
176169299Spjd}
177169299Spjd
178169299Spjd/*
179169299Spjd * Return ID of the given provider.
180169299Spjd */
181169299Spjdint
182169299Spjdg_get_ident(int fd, char *ident, size_t size)
183169299Spjd{
184169299Spjd	char lident[DISK_IDENT_SIZE];
185169299Spjd
186169299Spjd	if (g_ioctl_arg(fd, DIOCGIDENT, lident) == -1)
187169299Spjd		return (-1);
188169299Spjd	if (lident[0] == '\0') {
189169299Spjd		errno = ENOENT;
190169299Spjd		return (-1);
191169299Spjd	}
192169299Spjd	if (strlcpy(ident, lident, size) >= size) {
193169299Spjd		errno = ENAMETOOLONG;
194169299Spjd		return (-1);
195169299Spjd	}
196169299Spjd	return (0);
197169299Spjd}
198169299Spjd
199169299Spjd/*
200169299Spjd * Return name of the provider, which has the given ID.
201169299Spjd */
202169299Spjdint
203169299Spjdg_get_name(const char *ident, char *name, size_t size)
204169299Spjd{
205169299Spjd	int fd;
206169299Spjd
207169299Spjd	fd = g_open_by_ident(ident, 0, name, size);
208169299Spjd	if (fd == -1)
209169299Spjd		return (-1);
210169299Spjd	g_close(fd);
211169299Spjd	return (0);
212169299Spjd}
213169299Spjd
214169299Spjd/*
215169299Spjd * Find provider name by the given ID.
216169299Spjd */
217169299Spjdint
218179152Spjdg_open_by_ident(const char *ident, int dowrite, char *name, size_t size)
219169299Spjd{
220169299Spjd	char lident[DISK_IDENT_SIZE];
221169299Spjd	struct gmesh mesh;
222169299Spjd	struct gclass *mp;
223169299Spjd	struct ggeom *gp;
224169299Spjd	struct gprovider *pp;
225169299Spjd	int error, fd;
226169299Spjd
227169299Spjd	error = geom_gettree(&mesh);
228169299Spjd	if (error != 0) {
229169299Spjd		errno = error;
230169299Spjd		return (-1);
231169299Spjd	}
232169299Spjd
233169299Spjd	error = ENOENT;
234169299Spjd	fd = -1;
235169299Spjd
236169299Spjd	LIST_FOREACH(mp, &mesh.lg_class, lg_class) {
237169299Spjd		LIST_FOREACH(gp, &mp->lg_geom, lg_geom) {
238169299Spjd			LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
239179152Spjd				fd = g_open(pp->lg_name, dowrite);
240169299Spjd				if (fd == -1)
241169299Spjd					continue;
242169299Spjd				if (g_get_ident(fd, lident,
243169299Spjd				    sizeof(lident)) == -1) {
244169299Spjd					g_close(fd);
245169299Spjd					continue;
246169299Spjd				}
247169299Spjd				if (strcmp(ident, lident) != 0) {
248169299Spjd					g_close(fd);
249169299Spjd					continue;
250169299Spjd				}
251169299Spjd				error = 0;
252169299Spjd				if (name != NULL && strlcpy(name, pp->lg_name,
253169299Spjd				    size) >= size) {
254169299Spjd					error = ENAMETOOLONG;
255169299Spjd					g_close(fd);
256169299Spjd				}
257169299Spjd				goto end;
258169299Spjd			}
259169299Spjd		}
260169299Spjd	}
261169299Spjdend:
262169299Spjd	geom_deletetree(&mesh);
263169299Spjd	if (error != 0) {
264169299Spjd		errno = error;
265169299Spjd		return (-1);
266169299Spjd	}
267169299Spjd	return (fd);
268169299Spjd}
269182843Slulf
270182843Slulf/*
271182843Slulf * Return the device path device given a partial or full path to its node.
272182843Slulf * A pointer can be provided, which will be set to an opened file descriptor of
273182843Slulf * not NULL.
274182843Slulf */
275182843Slulfstatic char *
276182843Slulfg_device_path_open(const char *devpath, int *fdp, int dowrite)
277182843Slulf{
278182843Slulf	char *path;
279182843Slulf	int fd;
280182843Slulf
281182843Slulf	/* Make sure that we can fail. */
282182843Slulf	if (fdp != NULL)
283182843Slulf		*fdp = -1;
284182843Slulf	/* Use the device node if we're able to open it. */
285182843Slulf	do {
286182843Slulf		fd = open(devpath, dowrite ? O_RDWR : O_RDONLY);
287182843Slulf		if (fd == -1)
288182843Slulf			break;
289182843Slulf		/*
290182843Slulf		 * Let try to get sectorsize, which will prove it is a GEOM
291182843Slulf		 * provider.
292182843Slulf		 */
293182843Slulf		if (g_sectorsize(fd) == -1) {
294182843Slulf			close(fd);
295182843Slulf			errno = EFTYPE;
296182843Slulf			return (NULL);
297182843Slulf		}
298182843Slulf		if ((path = strdup(devpath)) == NULL) {
299182843Slulf			close(fd);
300182843Slulf			return (NULL);
301182843Slulf		}
302182843Slulf		if (fdp != NULL)
303182843Slulf			*fdp = fd;
304182843Slulf		else
305182843Slulf			close(fd);
306182843Slulf		return (path);
307182843Slulf	} while (0);
308182843Slulf
309182843Slulf	/* If we're not given an absolute path, assume /dev/ prefix. */
310182843Slulf	if (*devpath != '/') {
311182843Slulf		asprintf(&path, "%s%s", _PATH_DEV, devpath);
312182843Slulf		if (path == NULL)
313182843Slulf			return (NULL);
314182843Slulf		fd = open(path, dowrite ? O_RDWR : O_RDONLY);
315182843Slulf		if (fd == -1) {
316182843Slulf			free(path);
317182843Slulf			return (NULL);
318182843Slulf		}
319182843Slulf		/*
320182843Slulf		 * Let try to get sectorsize, which will prove it is a GEOM
321182843Slulf		 * provider.
322182843Slulf		 */
323182843Slulf		if (g_sectorsize(fd) == -1) {
324182843Slulf			free(path);
325182843Slulf			close(fd);
326182843Slulf			errno = EFTYPE;
327182843Slulf			return (NULL);
328182843Slulf		}
329182843Slulf		if (fdp != NULL)
330182843Slulf			*fdp = fd;
331182843Slulf		else
332182843Slulf			close(fd);
333182843Slulf		return (path);
334182843Slulf	}
335182843Slulf	return (NULL);
336182843Slulf}
337182843Slulf
338182843Slulfchar *
339182843Slulfg_device_path(const char *devpath)
340182843Slulf{
341182843Slulf	return (g_device_path_open(devpath, NULL, 0));
342182843Slulf}
343