devno.c revision 18a1444b4f1e6a0948fd38fa0de382d86cfe04de
1/*
2 * devno.c - find a particular device by its device number (major/minor)
3 *
4 * Copyright (C) 2000, 2001, 2003 Theodore Ts'o
5 * Copyright (C) 2001 Andreas Dilger
6 *
7 * %Begin-Header%
8 * This file may be redistributed under the terms of the
9 * GNU Lesser General Public License.
10 * %End-Header%
11 */
12
13#include <stdio.h>
14#include <string.h>
15#if HAVE_UNISTD_H
16#include <unistd.h>
17#endif
18#include <stdlib.h>
19#include <string.h>
20#if HAVE_SYS_TYPES_H
21#include <sys/types.h>
22#endif
23#if HAVE_SYS_STAT_H
24#include <sys/stat.h>
25#endif
26#include <dirent.h>
27#if HAVE_ERRNO_H
28#include <errno.h>
29#endif
30#if HAVE_SYS_MKDEV_H
31#include <sys/mkdev.h>
32#endif
33
34#include "blkidP.h"
35
36char *blkid_strndup(const char *s, int length)
37{
38	char *ret;
39
40	if (!s)
41		return NULL;
42
43	if (!length)
44		length = strlen(s);
45
46	ret = malloc(length + 1);
47	if (ret) {
48		strncpy(ret, s, length);
49		ret[length] = '\0';
50	}
51	return ret;
52}
53
54char *blkid_strdup(const char *s)
55{
56	return blkid_strndup(s, 0);
57}
58
59/*
60 * This function adds an entry to the directory list
61 */
62static void add_to_dirlist(const char *name, struct dir_list **list)
63{
64	struct dir_list *dp;
65
66	dp = malloc(sizeof(struct dir_list));
67	if (!dp)
68		return;
69	dp->name = blkid_strdup(name);
70	if (!dp->name) {
71		free(dp);
72		return;
73	}
74	dp->next = *list;
75	*list = dp;
76}
77
78/*
79 * This function frees a directory list
80 */
81static void free_dirlist(struct dir_list **list)
82{
83	struct dir_list *dp, *next;
84
85	for (dp = *list; dp; dp = next) {
86		next = dp->next;
87		free(dp->name);
88		free(dp);
89	}
90	*list = NULL;
91}
92
93void blkid__scan_dir(char *dirname, dev_t devno, struct dir_list **list,
94		     char **devname)
95{
96	DIR	*dir;
97	struct dirent *dp;
98	char	path[1024];
99	int	dirlen;
100	struct stat st;
101
102	if ((dir = opendir(dirname)) == NULL)
103		return;
104	dirlen = strlen(dirname) + 2;
105	while ((dp = readdir(dir)) != 0) {
106		if (dirlen + strlen(dp->d_name) >= sizeof(path))
107			continue;
108
109		if (dp->d_name[0] == '.' &&
110		    ((dp->d_name[1] == 0) ||
111		     ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
112			continue;
113
114		sprintf(path, "%s/%s", dirname, dp->d_name);
115		if (stat(path, &st) < 0)
116			continue;
117
118		if (S_ISBLK(st.st_mode) && st.st_rdev == devno) {
119			*devname = blkid_strdup(path);
120			DBG(DEBUG_DEVNO,
121			    printf("found 0x%llx at %s (%p)\n", (long long)devno,
122				   path, *devname));
123			break;
124		}
125		if (list && S_ISDIR(st.st_mode) && !lstat(path, &st) &&
126		    S_ISDIR(st.st_mode))
127			add_to_dirlist(path, list);
128	}
129	closedir(dir);
130	return;
131}
132
133/* Directories where we will try to search for device numbers */
134static const char *devdirs[] = { "/devices", "/devfs", "/dev", NULL };
135
136/*
137 * This function finds the pathname to a block device with a given
138 * device number.  It returns a pointer to allocated memory to the
139 * pathname on success, and NULL on failure.
140 */
141char *blkid_devno_to_devname(dev_t devno)
142{
143	struct dir_list *list = NULL, *new_list = NULL;
144	char *devname = NULL;
145	const char **dir;
146
147	/*
148	 * Add the starting directories to search in reverse order of
149	 * importance, since we are using a stack...
150	 */
151	for (dir = devdirs; *dir; dir++)
152		add_to_dirlist(*dir, &list);
153
154	while (list) {
155		struct dir_list *current = list;
156
157		list = list->next;
158		DBG(DEBUG_DEVNO, printf("directory %s\n", current->name));
159		blkid__scan_dir(current->name, devno, &new_list, &devname);
160		free(current->name);
161		free(current);
162		if (devname)
163			break;
164		/*
165		 * If we're done checking at this level, descend to
166		 * the next level of subdirectories. (breadth-first)
167		 */
168		if (list == NULL) {
169			list = new_list;
170			new_list = NULL;
171		}
172	}
173	free_dirlist(&list);
174	free_dirlist(&new_list);
175
176	if (!devname) {
177		DBG(DEBUG_DEVNO,
178		    printf("blkid: couldn't find devno 0x%04lx\n",
179			   (unsigned long) devno));
180	} else {
181		DBG(DEBUG_DEVNO,
182		    printf("found devno 0x%04llx as %s\n", (long long)devno, devname));
183	}
184
185
186	return devname;
187}
188
189#ifdef TEST_PROGRAM
190int main(int argc, char** argv)
191{
192	char	*devname, *tmp;
193	int	major, minor;
194	dev_t	devno;
195	const char *errmsg = "Couldn't parse %s: %s\n";
196
197	blkid_debug_mask = DEBUG_ALL;
198	if ((argc != 2) && (argc != 3)) {
199		fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n"
200			"Resolve a device number to a device name\n",
201			argv[0], argv[0]);
202		exit(1);
203	}
204	if (argc == 2) {
205		devno = strtoul(argv[1], &tmp, 0);
206		if (*tmp) {
207			fprintf(stderr, errmsg, "device number", argv[1]);
208			exit(1);
209		}
210	} else {
211		major = strtoul(argv[1], &tmp, 0);
212		if (*tmp) {
213			fprintf(stderr, errmsg, "major number", argv[1]);
214			exit(1);
215		}
216		minor = strtoul(argv[2], &tmp, 0);
217		if (*tmp) {
218			fprintf(stderr, errmsg, "minor number", argv[2]);
219			exit(1);
220		}
221		devno = makedev(major, minor);
222	}
223	printf("Looking for device 0x%04llx\n", (long long)devno);
224	devname = blkid_devno_to_devname(devno);
225	free(devname);
226	return 0;
227}
228#endif
229