xref: /dflybsd-src/lib/libc/gen/getdevpath.c (revision 44b64581151dc6c20bfd11d7ed6d52f7f1c9b2e8)
16507240bSMatthew Dillon /*
26507240bSMatthew Dillon  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
36507240bSMatthew Dillon  *
46507240bSMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
56507240bSMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
66507240bSMatthew Dillon  *
76507240bSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
86507240bSMatthew Dillon  * modification, are permitted provided that the following conditions
96507240bSMatthew Dillon  * are met:
106507240bSMatthew Dillon  *
116507240bSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
126507240bSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
136507240bSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
146507240bSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
156507240bSMatthew Dillon  *    the documentation and/or other materials provided with the
166507240bSMatthew Dillon  *    distribution.
176507240bSMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
186507240bSMatthew Dillon  *    contributors may be used to endorse or promote products derived
196507240bSMatthew Dillon  *    from this software without specific, prior written permission.
206507240bSMatthew Dillon  *
216507240bSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
226507240bSMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
236507240bSMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
246507240bSMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
256507240bSMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
266507240bSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
276507240bSMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
286507240bSMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
296507240bSMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
306507240bSMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
316507240bSMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
326507240bSMatthew Dillon  * SUCH DAMAGE.
336507240bSMatthew Dillon  */
346507240bSMatthew Dillon 
356507240bSMatthew Dillon #include <sys/types.h>
366507240bSMatthew Dillon #include <sys/stat.h>
376507240bSMatthew Dillon 
386507240bSMatthew Dillon #include <stdio.h>
396507240bSMatthew Dillon #include <stdlib.h>
406507240bSMatthew Dillon #include <string.h>
416507240bSMatthew Dillon #include <unistd.h>
426507240bSMatthew Dillon #include <errno.h>
436507240bSMatthew Dillon #include <paths.h>
446507240bSMatthew Dillon #include <limits.h>
45376ccb5fSMatthew Dillon #include <fstab.h>
466507240bSMatthew Dillon 
476507240bSMatthew Dillon static void finddevlabel(char **pathp, const char *devname);
486507240bSMatthew Dillon static int xlatedevpath(char **pathp, struct stat *st);
496507240bSMatthew Dillon static char *dodequote(char *base);
506507240bSMatthew Dillon 
516507240bSMatthew Dillon /*
526507240bSMatthew Dillon  * Acquire device path.
536507240bSMatthew Dillon  *
546507240bSMatthew Dillon  */
556507240bSMatthew Dillon char *
getdevpath(const char * devname,int flags)566507240bSMatthew Dillon getdevpath(const char *devname, int flags)
576507240bSMatthew Dillon {
586507240bSMatthew Dillon 	struct stat st;
596507240bSMatthew Dillon 	char *path = NULL;
606507240bSMatthew Dillon 	int stgood = 0;
616507240bSMatthew Dillon 
626507240bSMatthew Dillon 	if (devname[0] == '/' || devname[0] == '.') {
636507240bSMatthew Dillon 		asprintf(&path, "%s", devname);
646507240bSMatthew Dillon 	} else {
656507240bSMatthew Dillon 		asprintf(&path, "/dev/%s", devname);
666507240bSMatthew Dillon 		if (lstat(path, &st) < 0) {
676507240bSMatthew Dillon 			free(path);
686507240bSMatthew Dillon 			path = NULL;
696507240bSMatthew Dillon 			finddevlabel(&path, devname);
706507240bSMatthew Dillon 			if (path == NULL)
716507240bSMatthew Dillon 				asprintf(&path, "%s", devname);
726507240bSMatthew Dillon 		} else {
736507240bSMatthew Dillon 			stgood = 1;
746507240bSMatthew Dillon 		}
756507240bSMatthew Dillon 	}
766507240bSMatthew Dillon 
776507240bSMatthew Dillon 	/*
786507240bSMatthew Dillon 	 * Translate softlinks if requested.  If the lstat() of the
796507240bSMatthew Dillon 	 * pre-translated path fails NULL is expected to be returned.
806507240bSMatthew Dillon 	 * lstat() is not called on the post-translated path.
816507240bSMatthew Dillon 	 */
826507240bSMatthew Dillon 	if ((flags & GETDEVPATH_RAWDEV) && path) {
836507240bSMatthew Dillon 		if (stgood == 0 && lstat(path, &st) == 0)
846507240bSMatthew Dillon 			stgood = 1;
856507240bSMatthew Dillon 		if (stgood)
866507240bSMatthew Dillon 			stgood = xlatedevpath(&path, &st);
876507240bSMatthew Dillon 		if (stgood == 0) {
886507240bSMatthew Dillon 			free(path);
896507240bSMatthew Dillon 			path = NULL;
906507240bSMatthew Dillon 		}
916507240bSMatthew Dillon 
926507240bSMatthew Dillon 	}
936507240bSMatthew Dillon 	if (path == NULL)
946507240bSMatthew Dillon 		errno = ENOENT;
956507240bSMatthew Dillon 	return(path);
966507240bSMatthew Dillon }
976507240bSMatthew Dillon 
986507240bSMatthew Dillon static void
finddevlabel(char ** pathp,const char * devname)996507240bSMatthew Dillon finddevlabel(char **pathp, const char *devname)
1006507240bSMatthew Dillon {
1016507240bSMatthew Dillon 	const char *prefix = _PATH_DEVTAB_PATHS;
1026507240bSMatthew Dillon 	const char *ptr1;
1036507240bSMatthew Dillon 	const char *trailer;
1046507240bSMatthew Dillon 	char *label;
1056507240bSMatthew Dillon 	char *ptr2;
1066507240bSMatthew Dillon 	char *ptr3;
1076507240bSMatthew Dillon 	char *dtpath;
1086507240bSMatthew Dillon 	char *bufp;
1096507240bSMatthew Dillon 	char buf[256];
1106507240bSMatthew Dillon 	FILE *fp;
1116507240bSMatthew Dillon 	size_t len;	/* directory prefix length */
1126507240bSMatthew Dillon 	size_t tlen;	/* devname length without trailer */
1136507240bSMatthew Dillon 
1146507240bSMatthew Dillon 	if ((trailer = strrchr(devname, '.')) != NULL)
1156507240bSMatthew Dillon 		tlen = trailer - devname;
1166507240bSMatthew Dillon 	else
1176507240bSMatthew Dillon 		tlen = 0;
1186507240bSMatthew Dillon 
1196507240bSMatthew Dillon 	while (*prefix && *pathp == NULL) {
1206507240bSMatthew Dillon 		/*
1216507240bSMatthew Dillon 		 * Directory search path
1226507240bSMatthew Dillon 		 */
1236507240bSMatthew Dillon 		ptr1 = strchr(prefix, ':');
1246507240bSMatthew Dillon 		len = (ptr1) ? (size_t)(ptr1 - prefix) : strlen(prefix);
125*0a227237SSascha Wildner 		asprintf(&dtpath, "%*.*s/devtab", (int)len, (int)len, prefix);
1266507240bSMatthew Dillon 
1276507240bSMatthew Dillon 		/*
1286507240bSMatthew Dillon 		 * Each devtab file
1296507240bSMatthew Dillon 		 */
1306507240bSMatthew Dillon 		if ((fp = fopen(dtpath, "r")) != NULL) {
1316507240bSMatthew Dillon 			while (fgets(buf, sizeof(buf), fp) != NULL) {
1326507240bSMatthew Dillon 				/*
1336507240bSMatthew Dillon 				 * Extract label field, check degenerate
1346507240bSMatthew Dillon 				 * cases.
1356507240bSMatthew Dillon 				 */
1366507240bSMatthew Dillon 				label = strtok_r(buf, " \t\r\n", &bufp);
1376507240bSMatthew Dillon 				if (label == NULL || *label == 0 ||
1386507240bSMatthew Dillon 				    *label == '#') {
1396507240bSMatthew Dillon 					continue;
1406507240bSMatthew Dillon 				}
1416507240bSMatthew Dillon 
1426507240bSMatthew Dillon 				/*
1436507240bSMatthew Dillon 				 * Match label, with or without the
1446507240bSMatthew Dillon 				 * trailer (aka ".s1a").  The trailer
1456507240bSMatthew Dillon 				 * is tacked on if the match is without
1466507240bSMatthew Dillon 				 * the trailer.
1476507240bSMatthew Dillon 				 */
1486507240bSMatthew Dillon 				if (strcmp(devname, label) == 0) {
1496507240bSMatthew Dillon 					trailer = "";
1506507240bSMatthew Dillon 				} else if (tlen && strlen(label) == tlen &&
1516507240bSMatthew Dillon 					   strncmp(devname, label, tlen) == 0) {
1526507240bSMatthew Dillon 					trailer = devname + tlen;
1536507240bSMatthew Dillon 				} else {
1546507240bSMatthew Dillon 					continue;
1556507240bSMatthew Dillon 				}
1566507240bSMatthew Dillon 
1576507240bSMatthew Dillon 				/*
1586507240bSMatthew Dillon 				 * Match, extract and process remaining fields.
1596507240bSMatthew Dillon 				 */
1606507240bSMatthew Dillon 				ptr2 = strtok_r(NULL, " \t\r\n", &bufp);
1616507240bSMatthew Dillon 				ptr3 = strtok_r(NULL, " \t\r\n", &bufp);
1626507240bSMatthew Dillon 				if (ptr2 == NULL || ptr3 == NULL)
1636507240bSMatthew Dillon 					continue;
1646507240bSMatthew Dillon 				if (*ptr2 == 0 || *ptr3 == 0)
1656507240bSMatthew Dillon 					continue;
1666507240bSMatthew Dillon 				ptr3 = dodequote(ptr3);
1676507240bSMatthew Dillon 				if (strcmp(ptr2, "path") == 0) {
1686507240bSMatthew Dillon 					asprintf(pathp, "%s%s", ptr3, trailer);
1696507240bSMatthew Dillon 				} else {
1706507240bSMatthew Dillon 					asprintf(pathp, "/dev/%s/%s%s",
1716507240bSMatthew Dillon 						 ptr2, ptr3, trailer);
1726507240bSMatthew Dillon 				}
1736507240bSMatthew Dillon 				break;
1746507240bSMatthew Dillon 			}
1756507240bSMatthew Dillon 			fclose(fp);
1766507240bSMatthew Dillon 		}
1776507240bSMatthew Dillon 		free(dtpath);
1786507240bSMatthew Dillon 		prefix += len;
1796507240bSMatthew Dillon 		if (*prefix == ':')
1806507240bSMatthew Dillon 			++prefix;
1816507240bSMatthew Dillon 	}
1826507240bSMatthew Dillon }
1836507240bSMatthew Dillon 
1846507240bSMatthew Dillon static int
xlatedevpath(char ** pathp,struct stat * st)1856507240bSMatthew Dillon xlatedevpath(char **pathp, struct stat *st)
1866507240bSMatthew Dillon {
1876507240bSMatthew Dillon 	char *path;
1886507240bSMatthew Dillon 	int n;
1896507240bSMatthew Dillon 	int len;
1906507240bSMatthew Dillon 
1916507240bSMatthew Dillon 	/*
1926507240bSMatthew Dillon 	 * If not a softlink return unchanged.
1936507240bSMatthew Dillon 	 */
1946507240bSMatthew Dillon 	if (!S_ISLNK(st->st_mode))
1956507240bSMatthew Dillon 		return(1);
1966507240bSMatthew Dillon 
1976507240bSMatthew Dillon 	/*
1986507240bSMatthew Dillon 	 * If the softlink isn't reasonable return bad (0)
1996507240bSMatthew Dillon 	 */
2006507240bSMatthew Dillon 	len = (int)st->st_size;
2016507240bSMatthew Dillon 	if (len < 0 || len > PATH_MAX)
2026507240bSMatthew Dillon 		return(0);
2036507240bSMatthew Dillon 
2046507240bSMatthew Dillon 	/*
2056507240bSMatthew Dillon 	 * Read the link, return if the result is not what we expected.
2066507240bSMatthew Dillon 	 */
2076507240bSMatthew Dillon 	path = malloc(len + 1);
2086507240bSMatthew Dillon 	n = readlink(*pathp, path, len);
2096507240bSMatthew Dillon 	if (n < 0 || n > len) {
2106507240bSMatthew Dillon 		free(path);
2116507240bSMatthew Dillon 		return(0);
2126507240bSMatthew Dillon 	}
2136507240bSMatthew Dillon 
2146507240bSMatthew Dillon 	/*
2156507240bSMatthew Dillon 	 * Success, replace (*pathp).
2166507240bSMatthew Dillon 	 */
2176507240bSMatthew Dillon 	path[n] = 0;
2186507240bSMatthew Dillon 	free(*pathp);
2196507240bSMatthew Dillon 	*pathp = path;
2206507240bSMatthew Dillon 	return(1);
2216507240bSMatthew Dillon }
2226507240bSMatthew Dillon 
2236507240bSMatthew Dillon static char *
dodequote(char * base)2246507240bSMatthew Dillon dodequote(char *base)
2256507240bSMatthew Dillon {
2266507240bSMatthew Dillon 	int len = strlen(base);
2276507240bSMatthew Dillon 
2286507240bSMatthew Dillon 	if (len && base[0] == '\"' && base[len-1] == '\"') {
2296507240bSMatthew Dillon 		base[len - 1] = 0;
2306507240bSMatthew Dillon 		++base;
2316507240bSMatthew Dillon 	}
2326507240bSMatthew Dillon 	return(base);
2336507240bSMatthew Dillon }
234