b5c1bc9a87d2be873d7878fb5c30fa64d1e43a54
[oweals/busybox.git] / e2fsprogs / blkid / devname.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * devname.c - get a dev by its device inode name
4  *
5  * Copyright (C) Andries Brouwer
6  * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o
7  * Copyright (C) 2001 Andreas Dilger
8  *
9  * %Begin-Header%
10  * This file may be redistributed under the terms of the
11  * GNU Lesser General Public License.
12  * %End-Header%
13  */
14
15 #include <stdio.h>
16 #include <string.h>
17 #if HAVE_UNISTD_H
18 #include <unistd.h>
19 #endif
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23 #if HAVE_SYS_TYPES_H
24 #include <sys/types.h>
25 #endif
26 #if HAVE_SYS_STAT_H
27 #include <sys/stat.h>
28 #endif
29 #if HAVE_ERRNO_H
30 #include <errno.h>
31 #endif
32 #if HAVE_SYS_MKDEV_H
33 #include <sys/mkdev.h>
34 #endif
35 #include <time.h>
36
37 #include "blkidP.h"
38
39 /*
40  * Find a dev struct in the cache by device name, if available.
41  *
42  * If there is no entry with the specified device name, and the create
43  * flag is set, then create an empty device entry.
44  */
45 blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
46 {
47         blkid_dev dev = NULL, tmp;
48         struct list_head *p;
49
50         if (!cache || !devname)
51                 return NULL;
52
53         list_for_each(p, &cache->bic_devs) {
54                 tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
55                 if (strcmp(tmp->bid_name, devname))
56                         continue;
57
58                 DBG(DEBUG_DEVNAME,
59                     printf("found devname %s in cache\n", tmp->bid_name));
60                 dev = tmp;
61                 break;
62         }
63
64         if (!dev && (flags & BLKID_DEV_CREATE)) {
65                 dev = blkid_new_dev();
66                 if (!dev)
67                         return NULL;
68                 dev->bid_name = blkid_strdup(devname);
69                 dev->bid_cache = cache;
70                 list_add_tail(&dev->bid_devs, &cache->bic_devs);
71                 cache->bic_flags |= BLKID_BIC_FL_CHANGED;
72         }
73
74         if (flags & BLKID_DEV_VERIFY)
75                 dev = blkid_verify(cache, dev);
76         return dev;
77 }
78
79 /*
80  * Probe a single block device to add to the device cache.
81  */
82 static void probe_one(blkid_cache cache, const char *ptname,
83                       dev_t devno, int pri)
84 {
85         blkid_dev dev = NULL;
86         struct list_head *p;
87         const char **dir;
88         char *devname = NULL;
89
90         /* See if we already have this device number in the cache. */
91         list_for_each(p, &cache->bic_devs) {
92                 blkid_dev tmp = list_entry(p, struct blkid_struct_dev,
93                                            bid_devs);
94                 if (tmp->bid_devno == devno) {
95                         dev = blkid_verify(cache, tmp);
96                         break;
97                 }
98         }
99         if (dev && dev->bid_devno == devno)
100                 goto set_pri;
101
102         /*
103          * Take a quick look at /dev/ptname for the device number.  We check
104          * all of the likely device directories.  If we don't find it, or if
105          * the stat information doesn't check out, use blkid_devno_to_devname()
106          * to find it via an exhaustive search for the device major/minor.
107          */
108         for (dir = blkid_devdirs; *dir; dir++) {
109                 struct stat st;
110                 char device[256];
111
112                 sprintf(device, "%s/%s", *dir, ptname);
113                 if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) &&
114                     dev->bid_devno == devno)
115                         goto set_pri;
116
117                 if (stat(device, &st) == 0 && S_ISBLK(st.st_mode) &&
118                     st.st_rdev == devno) {
119                         devname = blkid_strdup(device);
120                         break;
121                 }
122         }
123         if (!devname) {
124                 devname = blkid_devno_to_devname(devno);
125                 if (!devname)
126                         return;
127         }
128         dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL);
129         free(devname);
130
131 set_pri:
132         if (!pri && !strncmp(ptname, "md", 2))
133                 pri = BLKID_PRI_MD;
134         if (dev)
135                 dev->bid_pri = pri;
136         return;
137 }
138
139 #define PROC_PARTITIONS "/proc/partitions"
140 #define VG_DIR          "/proc/lvm/VGs"
141
142 /*
143  * This function initializes the UUID cache with devices from the LVM
144  * proc hierarchy.  We currently depend on the names of the LVM
145  * hierarchy giving us the device structure in /dev.  (XXX is this a
146  * safe thing to do?)
147  */
148 #ifdef VG_DIR
149 #include <dirent.h>
150 static dev_t lvm_get_devno(const char *lvm_device)
151 {
152         FILE *lvf;
153         char buf[1024];
154         int ma, mi;
155         dev_t ret = 0;
156
157         DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device));
158         if ((lvf = fopen(lvm_device, "r")) == NULL) {
159                 DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno,
160                                           strerror(errno)));
161                 return 0;
162         }
163
164         while (fgets(buf, sizeof(buf), lvf)) {
165                 if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) {
166                         ret = makedev(ma, mi);
167                         break;
168                 }
169         }
170         fclose(lvf);
171
172         return ret;
173 }
174
175 static void lvm_probe_all(blkid_cache cache)
176 {
177         DIR             *vg_list;
178         struct dirent   *vg_iter;
179         int             vg_len = strlen(VG_DIR);
180         dev_t           dev;
181
182         if ((vg_list = opendir(VG_DIR)) == NULL)
183                 return;
184
185         DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR));
186
187         while ((vg_iter = readdir(vg_list)) != NULL) {
188                 DIR             *lv_list;
189                 char            *vdirname;
190                 char            *vg_name;
191                 struct dirent   *lv_iter;
192
193                 vg_name = vg_iter->d_name;
194                 if (!strcmp(vg_name, ".") || !strcmp(vg_name, ".."))
195                         continue;
196                 vdirname = xmalloc(vg_len + strlen(vg_name) + 8);
197                 sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name);
198
199                 lv_list = opendir(vdirname);
200                 free(vdirname);
201                 if (lv_list == NULL)
202                         continue;
203
204                 while ((lv_iter = readdir(lv_list)) != NULL) {
205                         char            *lv_name, *lvm_device;
206
207                         lv_name = lv_iter->d_name;
208                         if (!strcmp(lv_name, ".") || !strcmp(lv_name, ".."))
209                                 continue;
210
211                         lvm_device = xmalloc(vg_len + strlen(vg_name) +
212                                             strlen(lv_name) + 8);
213                         sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
214                                 lv_name);
215                         dev = lvm_get_devno(lvm_device);
216                         sprintf(lvm_device, "%s/%s", vg_name, lv_name);
217                         DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n",
218                                                   lvm_device,
219                                                   (unsigned int) dev));
220                         probe_one(cache, lvm_device, dev, BLKID_PRI_LVM);
221                         free(lvm_device);
222                 }
223                 closedir(lv_list);
224         }
225         closedir(vg_list);
226 }
227 #endif
228
229 #define PROC_EVMS_VOLUMES "/proc/evms/volumes"
230
231 static int
232 evms_probe_all(blkid_cache cache)
233 {
234         char line[100];
235         int ma, mi, sz, num = 0;
236         FILE *procpt;
237         char device[110];
238
239         procpt = fopen(PROC_EVMS_VOLUMES, "r");
240         if (!procpt)
241                 return 0;
242         while (fgets(line, sizeof(line), procpt)) {
243                 if (sscanf (line, " %d %d %d %*s %*s %[^\n ]",
244                             &ma, &mi, &sz, device) != 4)
245                         continue;
246
247                 DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n",
248                                           device, ma, mi));
249
250                 probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS);
251                 num++;
252         }
253         fclose(procpt);
254         return num;
255 }
256
257 /*
258  * Read the device data for all available block devices in the system.
259  */
260 int blkid_probe_all(blkid_cache cache)
261 {
262         FILE *proc;
263         char line[1024];
264         char ptname0[128], ptname1[128], *ptname = 0;
265         char *ptnames[2];
266         dev_t devs[2];
267         int ma, mi;
268         unsigned long long sz;
269         int lens[2] = { 0, 0 };
270         int which = 0, last = 0;
271
272         ptnames[0] = ptname0;
273         ptnames[1] = ptname1;
274
275         if (!cache)
276                 return -BLKID_ERR_PARAM;
277
278         if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
279             time(0) - cache->bic_time < BLKID_PROBE_INTERVAL)
280                 return 0;
281
282         blkid_read_cache(cache);
283         evms_probe_all(cache);
284 #ifdef VG_DIR
285         lvm_probe_all(cache);
286 #endif
287
288         proc = fopen(PROC_PARTITIONS, "r");
289         if (!proc)
290                 return -BLKID_ERR_PROC;
291
292         while (fgets(line, sizeof(line), proc)) {
293                 last = which;
294                 which ^= 1;
295                 ptname = ptnames[which];
296
297                 if (sscanf(line, " %d %d %llu %128[^\n ]",
298                            &ma, &mi, &sz, ptname) != 4)
299                         continue;
300                 devs[which] = makedev(ma, mi);
301
302                 DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname));
303
304                 /* Skip whole disk devs unless they have no partitions
305                  * If we don't have a partition on this dev, also
306                  * check previous dev to see if it didn't have a partn.
307                  * heuristic: partition name ends in a digit.
308                  *
309                  * Skip extended partitions.
310                  * heuristic: size is 1
311                  *
312                  * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
313                  */
314
315                 lens[which] = strlen(ptname);
316                 if (isdigit(ptname[lens[which] - 1])) {
317                         DBG(DEBUG_DEVNAME,
318                             printf("partition dev %s, devno 0x%04X\n",
319                                    ptname, (unsigned int) devs[which]));
320
321                         if (sz > 1)
322                                 probe_one(cache, ptname, devs[which], 0);
323                         lens[which] = 0;
324                         lens[last] = 0;
325                 } else if (lens[last] && strncmp(ptnames[last], ptname,
326                                                  lens[last])) {
327                         DBG(DEBUG_DEVNAME,
328                             printf("whole dev %s, devno 0x%04X\n",
329                                    ptnames[last], (unsigned int) devs[last]));
330                         probe_one(cache, ptnames[last], devs[last], 0);
331                         lens[last] = 0;
332                 }
333         }
334
335         /* Handle the last device if it wasn't partitioned */
336         if (lens[which])
337                 probe_one(cache, ptname, devs[which], 0);
338
339         fclose(proc);
340
341         cache->bic_time = time(0);
342         cache->bic_flags |= BLKID_BIC_FL_PROBED;
343         blkid_flush_cache(cache);
344         return 0;
345 }
346
347 #ifdef TEST_PROGRAM
348 int main(int argc, char **argv)
349 {
350         blkid_cache cache = NULL;
351         int ret;
352
353         blkid_debug_mask = DEBUG_ALL;
354         if (argc != 1) {
355                 fprintf(stderr, "Usage: %s\n"
356                         "Probe all devices and exit\n", argv[0]);
357                 exit(1);
358         }
359         if ((ret = blkid_get_cache(&cache, bb_dev_null)) != 0) {
360                 fprintf(stderr, "%s: error creating cache (%d)\n",
361                         argv[0], ret);
362                 exit(1);
363         }
364         if (blkid_probe_all(cache) < 0)
365                 printf("%s: error probing devices\n", argv[0]);
366
367         blkid_put_cache(cache);
368         return (0);
369 }
370 #endif