Whamcloud - gitweb
Integrate new blkid library.
[tools/e2fsprogs.git] / lib / blkid / devno.c
1 /*
2  * devno.c - find a particular device by its device number (major/minor)
3  *
4  * Copyright (C) 2000, 2001 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 "blkid/blkid.h"
35
36 #ifdef DEBUG_DEVNO
37 #define DEB_DEVNO(fmt, arg...) printf("devno: " fmt, ## arg)
38 #else
39 #define DEB_DEVNO(fmt, arg...) do {} while (0)
40 #endif
41
42 struct dir_list {
43         char    *name;
44         struct dir_list *next;
45 };
46
47 char *stringn_copy(const char *s, const int length)
48 {
49         char *ret;
50
51         if (!s)
52                 return NULL;
53
54         ret = malloc(length + 1);
55         if (ret) {
56                 strncpy(ret, s, length);
57                 ret[length] = '\0';
58         }
59         return ret;
60 }
61
62 char *string_copy(const char *s)
63 {
64         if (!s)
65                 return NULL;
66
67         return stringn_copy(s, strlen(s));
68 }
69
70 void string_free(char *s)
71 {
72         if (s)
73                 free(s);
74 }
75
76 /*
77  * This function adds an entry to the directory list
78  */
79 static void add_to_dirlist(const char *name, struct dir_list **list)
80 {
81         struct dir_list *dp;
82
83         dp = malloc(sizeof(struct dir_list));
84         if (!dp)
85                 return;
86         dp->name = string_copy(name);
87         if (!dp->name) {
88                 free(dp);
89                 return;
90         }
91         dp->next = *list;
92         *list = dp;
93 }
94
95 /*
96  * This function frees a directory list
97  */
98 static void free_dirlist(struct dir_list **list)
99 {
100         struct dir_list *dp, *next;
101
102         for (dp = *list; dp; dp = next) {
103                 next = dp->next;
104                 string_free(dp->name);
105                 free(dp);
106         }
107         *list = NULL;
108 }
109
110 static int scan_dir(char *dirname, dev_t devno, struct dir_list **list,
111                     char **devname)
112 {
113         DIR     *dir;
114         struct dirent *dp;
115         char    path[1024];
116         int     dirlen;
117         struct stat st;
118         int     ret = 0;
119
120         dirlen = strlen(dirname);
121         if ((dir = opendir(dirname)) == NULL)
122                 return errno;
123         dp = readdir(dir);
124         while (dp) {
125                 if (dirlen + strlen(dp->d_name) + 2 >= sizeof(path))
126                         goto skip_to_next;
127
128                 if (dp->d_name[0] == '.' &&
129                     ((dp->d_name[1] == 0) ||
130                      ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
131                         goto skip_to_next;
132
133                 sprintf(path, "%s/%s", dirname, dp->d_name);
134                 if (stat(path, &st) < 0)
135                         goto skip_to_next;
136
137                 if (S_ISDIR(st.st_mode))
138                         add_to_dirlist(path, list);
139                 else if (S_ISBLK(st.st_mode) && st.st_rdev == devno) {
140                         *devname = string_copy(path);
141                         DEB_DEVNO("found 0x%Lx at %s (%p)\n", devno,
142                                  *devname, *devname);
143                         if (!*devname)
144                                 ret = -BLKID_ERR_MEM;
145                         break;
146                 }
147         skip_to_next:
148                 dp = readdir(dir);
149         }
150         closedir(dir);
151         return ret;
152 }
153
154 /* Directories where we will try to search for device numbers */
155 const char *devdirs[] = { "/dev", "/devfs", "/devices", NULL };
156
157 /*
158  * This function finds the pathname to a block device with a given
159  * device number.  It returns a pointer to allocated memory to the
160  * pathname on success, and NULL on failure.
161  */
162 char *blkid_devno_to_devname(dev_t devno)
163 {
164         struct dir_list *list = NULL, *new_list = NULL;
165         char *devname = NULL;
166         const char **dir;
167
168         /*
169          * Add the starting directories to search in reverse order of
170          * importance, since we are using a stack...
171          */
172         for (dir = devdirs; *dir; dir++)
173                 /* go to end of list */;
174
175         do {
176                 --dir;
177                 add_to_dirlist(*dir, &list);
178         } while (dir != devdirs);
179
180         while (list) {
181                 struct dir_list *current = list;
182
183                 list = list->next;
184                 DEB_DEVNO("directory %s\n", current->name);
185                 scan_dir(current->name, devno, &new_list, &devname);
186                 string_free(current->name);
187                 free(current);
188                 if (devname)
189                         break;
190                 /*
191                  * If we're done checking at this level, descend to
192                  * the next level of subdirectories. (breadth-first)
193                  */
194                 if (list == NULL) {
195                         list = new_list;
196                         new_list = NULL;
197                 }
198         }
199         free_dirlist(&list);
200         free_dirlist(&new_list);
201
202         if (!devname)
203                 fprintf(stderr, "blkid: couldn't find devno 0x%04Lx\n", devno);
204         else
205                 DEB_DEVNO("found devno 0x%04Lx as %s\n", devno, devname);
206
207         return devname;
208 }
209
210 blkid_dev *blkid_find_devno(blkid_cache *cache, dev_t devno)
211 {
212         blkid_dev *dev = NULL;
213         struct list_head *p, *n;
214
215         if (!cache)
216                 return NULL;
217
218         /* This cannot be a standard list_for_each() because we may be
219          * deleting the referenced struct in blkid_verify_devname() and
220          * pointing to another one that was probed from disk, and "p"
221          * would point to freed memory.
222          */
223         list_for_each_safe(p, n, &cache->bic_devs) {
224                 blkid_dev *tmp = list_entry(p, blkid_dev, bid_devs);
225                 if (tmp->bid_devno != devno)
226                         continue;
227
228                 tmp = blkid_verify_devname(cache, tmp);
229                 if (!tmp || tmp->bid_devno != devno)
230                         continue;
231
232                 dev = tmp;
233                 break;
234         }
235
236         if (dev)
237                 DEB_DEVNO("found devno 0x%04LX in cache as %s\n",
238                           devno, dev->bid_name);
239
240         return dev;
241 }
242
243 blkid_dev *blkid_get_devno(blkid_cache *cache, dev_t devno)
244 {
245         char *devname;
246         blkid_dev *dev;
247
248         if (!(dev = blkid_find_devno(cache, devno)) &&
249             (devname = blkid_devno_to_devname(devno))) {
250                 dev = blkid_get_devname(cache, devname);
251                 string_free(devname);
252         }
253
254         return dev;
255 }
256
257 #ifdef TEST_PROGRAM
258 int main(int argc, char** argv)
259 {
260         char    *devname, *tmp;
261         int     major, minor;
262         dev_t   devno;
263         const char *errmsg = "Couldn't parse %s: %s\n";
264
265         if ((argc != 2) && (argc != 3)) {
266                 fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n"
267                         "Resolve a device number to a device name\n",
268                         argv[0], argv[0]);
269                 exit(1);
270         }
271         if (argc == 2) {
272                 devno = strtoul(argv[1], &tmp, 0);
273                 if (*tmp) {
274                         fprintf(stderr, errmsg, "device number", argv[1]);
275                         exit(1);
276                 }
277         } else {
278                 major = strtoul(argv[1], &tmp, 0);
279                 if (*tmp) {
280                         fprintf(stderr, errmsg, "major number", argv[1]);
281                         exit(1);
282                 }
283                 minor = strtoul(argv[2], &tmp, 0);
284                 if (*tmp) {
285                         fprintf(stderr, errmsg, "minor number", argv[2]);
286                         exit(1);
287                 }
288                 devno = makedev(major, minor);
289         }
290         printf("Looking for device 0x%04Lx\n", devno);
291         devname = blkid_devno_to_devname(devno);
292         if (devname)
293                 string_free(devname);
294         return 0;
295 }
296 #endif