Whamcloud - gitweb
Add device-mapper support to the blkid library
[tools/e2fsprogs.git] / lib / blkid / devname.c
1 /*
2  * devname.c - get a dev by its device inode name
3  *
4  * Copyright (C) Andries Brouwer
5  * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o
6  * Copyright (C) 2001 Andreas Dilger
7  *
8  * %Begin-Header%
9  * This file may be redistributed under the terms of the
10  * GNU Lesser General Public License.
11  * %End-Header%
12  */
13
14 #define _GNU_SOURCE 1
15
16 #include <stdio.h>
17 #include <string.h>
18 #if HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21 #include <stdlib.h>
22 #include <string.h>
23 #include <ctype.h>
24 #if HAVE_SYS_TYPES_H
25 #include <sys/types.h>
26 #endif
27 #if HAVE_SYS_STAT_H
28 #include <sys/stat.h>
29 #endif
30 #if HAVE_ERRNO_H
31 #include <errno.h>
32 #endif
33 #if HAVE_SYS_MKDEV_H
34 #include <sys/mkdev.h>
35 #endif
36 #include <time.h>
37
38 #include "blkidP.h"
39
40 #ifdef HAVE_DEVMAPPER
41 #include <libdevmapper.h>
42 #endif
43
44 /*
45  * Find a dev struct in the cache by device name, if available.
46  *
47  * If there is no entry with the specified device name, and the create
48  * flag is set, then create an empty device entry.
49  */
50 blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
51 {
52         blkid_dev dev = NULL, tmp;
53         struct list_head *p;
54
55         if (!cache || !devname)
56                 return NULL;
57
58         list_for_each(p, &cache->bic_devs) {
59                 tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
60                 if (strcmp(tmp->bid_name, devname))
61                         continue;
62
63                 DBG(DEBUG_DEVNAME, 
64                     printf("found devname %s in cache\n", tmp->bid_name));
65                 dev = tmp;
66                 break;
67         }
68
69         if (!dev && (flags & BLKID_DEV_CREATE)) {
70                 dev = blkid_new_dev();
71                 if (!dev)
72                         return NULL;
73                 dev->bid_name = blkid_strdup(devname);
74                 dev->bid_cache = cache;
75                 list_add_tail(&dev->bid_devs, &cache->bic_devs);
76                 cache->bic_flags |= BLKID_BIC_FL_CHANGED;
77         }
78
79         if (flags & BLKID_DEV_VERIFY)
80                 dev = blkid_verify(cache, dev);
81         return dev;
82 }
83
84 #ifdef HAVE_DEVMAPPER
85 static int dm_device_is_leaf(const dev_t dev);
86 #endif
87
88 /*
89  * Probe a single block device to add to the device cache.
90  */
91 static void probe_one(blkid_cache cache, const char *ptname,
92                       dev_t devno, int pri, int only_if_new)
93 {
94         blkid_dev dev = NULL;
95         struct list_head *p;
96         const char **dir;
97         char *devname = NULL;
98
99         /* See if we already have this device number in the cache. */
100         list_for_each(p, &cache->bic_devs) {
101                 blkid_dev tmp = list_entry(p, struct blkid_struct_dev,
102                                            bid_devs);
103 #ifdef HAVE_DEVMAPPER
104                 if (!dm_device_is_leaf(devno))
105                         continue;
106 #endif
107                 if (tmp->bid_devno == devno) {
108                         if (only_if_new)
109                                 return;
110                         dev = blkid_verify(cache, tmp);
111                         break;
112                 }
113         }
114         if (dev && dev->bid_devno == devno)
115                 goto set_pri;
116
117         /*
118          * Take a quick look at /dev/ptname for the device number.  We check
119          * all of the likely device directories.  If we don't find it, or if
120          * the stat information doesn't check out, use blkid_devno_to_devname()
121          * to find it via an exhaustive search for the device major/minor.
122          */
123         for (dir = blkid_devdirs; *dir; dir++) {
124                 struct stat st;
125                 char device[256];
126
127                 sprintf(device, "%s/%s", *dir, ptname);
128                 if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) &&
129                     dev->bid_devno == devno)
130                         goto set_pri;
131
132                 if (stat(device, &st) == 0 && S_ISBLK(st.st_mode) && 
133                     st.st_rdev == devno) {
134                         devname = blkid_strdup(device);
135                         break;
136                 }
137         }
138         if (!devname) {
139                 devname = blkid_devno_to_devname(devno);
140                 if (!devname)
141                         return;
142         }
143         dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL);
144         free(devname);
145
146 set_pri:
147         if (!pri && !strncmp(ptname, "md", 2))
148                 pri = BLKID_PRI_MD;
149         if (dev)
150                 dev->bid_pri = pri;
151         return;
152 }
153
154 #ifdef HAVE_DEVMAPPER
155 /* 
156  * device-mapper support 
157  */
158 static int dm_device_has_dep(const dev_t dev, const char *name)
159 {
160         struct dm_task *task;
161         struct dm_deps *deps;
162         struct dm_info info;
163         int i;
164
165         task = dm_task_create(DM_DEVICE_DEPS);
166         if (!task)
167                 return 0;
168
169         dm_task_set_name(task, name);
170         dm_task_run(task);
171         dm_task_get_info(task, &info);
172
173         if (!info.exists) {
174                 dm_task_destroy(task);
175                 return 0;
176         }
177
178         deps = dm_task_get_deps(task);
179         if (!deps || deps->count == 0) {
180                 dm_task_destroy(task);
181                 return 0;
182         }
183
184         for (i = 0; i < deps->count; i++) {
185                 dev_t dep_dev = deps->device[i];
186
187                 if (dev == dep_dev) {
188                         dm_task_destroy(task);
189                         return 1;
190                 }
191         }
192
193         dm_task_destroy(task);
194         return 0;
195 }
196
197 static int dm_device_is_leaf(const dev_t dev)
198 {
199         struct dm_task *task;
200         struct dm_names *names;
201         unsigned int next = 0;
202         int n, ret = 1;
203
204         task = dm_task_create(DM_DEVICE_LIST);
205         if (!task)
206                 return 1;
207
208         dm_task_run(task);
209         names = dm_task_get_names(task);
210         if (!names || !names->dev) {
211                 dm_task_destroy(task);
212                 return 1;
213         }
214
215         n = 0;
216         do {
217                 names = (void *)names + next;
218
219                 if (dm_device_has_dep(dev, names->name))
220                         ret = 0;
221
222                 next = names->next;
223         } while (next);
224
225         dm_task_destroy(task);
226
227         return ret;
228 }
229
230 static dev_t dm_get_devno(const char *name)
231 {
232         struct dm_task *task;
233         struct dm_info info;
234         dev_t ret = 0;
235
236         task = dm_task_create(DM_DEVICE_INFO);
237         if (!task)
238                 return ret;
239
240         dm_task_set_name(task, name);
241         dm_task_run(task);
242         dm_task_get_info(task, &info);
243
244         if (!info.exists) {
245                 dm_task_destroy(task);
246                 return ret;
247         }
248
249         ret = makedev(info.major, info.minor);
250
251         dm_task_destroy(task);
252         
253         return ret;
254 }
255
256 static void dm_probe_all(blkid_cache cache, int only_if_new)
257 {
258         struct dm_task *task;
259         struct dm_names *names;
260         unsigned int next = 0;
261         int n;
262
263         task = dm_task_create(DM_DEVICE_LIST);
264         if (!task)
265                 return;
266
267         dm_task_run(task);
268         names = dm_task_get_names(task);
269         if (!names || !names->dev) {
270                 dm_task_destroy(task);
271                 return;
272         }
273
274         n = 0;
275         do {
276                 int rc;
277                 char *device = NULL;
278                 dev_t dev = 0;
279
280                 names = (void *)names + next;
281
282                 rc = asprintf(&device, "/dev/mapper/%s", names->name);
283                 if (rc < 0)
284                         goto try_next;
285
286                 dev = dm_get_devno(names->name);
287                 if (dev == 0)
288                         goto try_next;
289
290                 if (!dm_device_is_leaf(dev)) 
291                         goto try_next;
292
293                 probe_one(cache, device, dev, BLKID_PRI_DM, only_if_new);
294
295 try_next:
296                 next = names->next;
297         } while (next);
298
299         dm_task_destroy(task);
300 }
301 #endif /* HAVE_DEVMAPPER */
302
303 #define PROC_PARTITIONS "/proc/partitions"
304 #define VG_DIR          "/proc/lvm/VGs"
305
306 /*
307  * This function initializes the UUID cache with devices from the LVM
308  * proc hierarchy.  We currently depend on the names of the LVM
309  * hierarchy giving us the device structure in /dev.  (XXX is this a
310  * safe thing to do?)
311  */
312 #ifdef VG_DIR
313 #include <dirent.h>
314 static dev_t lvm_get_devno(const char *lvm_device)
315 {
316         FILE *lvf;
317         char buf[1024];
318         int ma, mi;
319         dev_t ret = 0;
320
321         DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device));
322         if ((lvf = fopen(lvm_device, "r")) == NULL) {
323                 DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno,
324                                           strerror(errno)));
325                 return 0;
326         }
327
328         while (fgets(buf, sizeof(buf), lvf)) {
329                 if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) {
330                         ret = makedev(ma, mi);
331                         break;
332                 }
333         }
334         fclose(lvf);
335
336         return ret;
337 }
338
339 static void lvm_probe_all(blkid_cache cache, int only_if_new)
340 {
341         DIR             *vg_list;
342         struct dirent   *vg_iter;
343         int             vg_len = strlen(VG_DIR);
344         dev_t           dev;
345
346         if ((vg_list = opendir(VG_DIR)) == NULL)
347                 return;
348
349         DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR));
350
351         while ((vg_iter = readdir(vg_list)) != NULL) {
352                 DIR             *lv_list;
353                 char            *vdirname;
354                 char            *vg_name;
355                 struct dirent   *lv_iter;
356
357                 vg_name = vg_iter->d_name;
358                 if (!strcmp(vg_name, ".") || !strcmp(vg_name, ".."))
359                         continue;
360                 vdirname = malloc(vg_len + strlen(vg_name) + 8);
361                 if (!vdirname)
362                         goto exit;
363                 sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name);
364
365                 lv_list = opendir(vdirname);
366                 free(vdirname);
367                 if (lv_list == NULL)
368                         continue;
369
370                 while ((lv_iter = readdir(lv_list)) != NULL) {
371                         char            *lv_name, *lvm_device;
372
373                         lv_name = lv_iter->d_name;
374                         if (!strcmp(lv_name, ".") || !strcmp(lv_name, ".."))
375                                 continue;
376
377                         lvm_device = malloc(vg_len + strlen(vg_name) +
378                                             strlen(lv_name) + 8);
379                         if (!lvm_device) {
380                                 closedir(lv_list);
381                                 goto exit;
382                         }
383                         sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
384                                 lv_name);
385                         dev = lvm_get_devno(lvm_device);
386                         sprintf(lvm_device, "%s/%s", vg_name, lv_name);
387                         DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n",
388                                                   lvm_device,
389                                                   (unsigned int) dev));
390                         probe_one(cache, lvm_device, dev, BLKID_PRI_LVM, 
391                                   only_if_new);
392                         free(lvm_device);
393                 }
394                 closedir(lv_list);
395         }
396 exit:
397         closedir(vg_list);
398 }
399 #endif
400
401 #define PROC_EVMS_VOLUMES "/proc/evms/volumes"
402
403 static int
404 evms_probe_all(blkid_cache cache, int only_if_new)
405 {
406         char line[100];
407         int ma, mi, sz, num = 0;
408         FILE *procpt;
409         char device[110];
410
411         procpt = fopen(PROC_EVMS_VOLUMES, "r");
412         if (!procpt)
413                 return 0;
414         while (fgets(line, sizeof(line), procpt)) {
415                 if (sscanf (line, " %d %d %d %*s %*s %[^\n ]",
416                             &ma, &mi, &sz, device) != 4)
417                         continue;
418
419                 DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n",
420                                           device, ma, mi));
421
422                 probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS,
423                           only_if_new);
424                 num++;
425         }
426         fclose(procpt);
427         return num;
428 }
429
430 /*
431  * Read the device data for all available block devices in the system.
432  */
433 static int probe_all(blkid_cache cache, int only_if_new)
434 {
435         FILE *proc;
436         char line[1024];
437         char ptname0[128], ptname1[128], *ptname = 0;
438         char *ptnames[2];
439         dev_t devs[2];
440         int ma, mi;
441         unsigned long long sz;
442         int lens[2] = { 0, 0 };
443         int which = 0, last = 0;
444
445         ptnames[0] = ptname0;
446         ptnames[1] = ptname1;
447
448         if (!cache)
449                 return -BLKID_ERR_PARAM;
450
451         if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
452             time(0) - cache->bic_time < BLKID_PROBE_INTERVAL)
453                 return 0;
454
455         blkid_read_cache(cache);
456 #ifdef HAVE_DEVMAPPER
457         dm_probe_all(cache, only_if_new);
458 #endif
459         evms_probe_all(cache, only_if_new);
460 #ifdef VG_DIR
461         lvm_probe_all(cache, only_if_new);
462 #endif
463
464         proc = fopen(PROC_PARTITIONS, "r");
465         if (!proc)
466                 return -BLKID_ERR_PROC;
467
468         while (fgets(line, sizeof(line), proc)) {
469                 last = which;
470                 which ^= 1;
471                 ptname = ptnames[which];
472
473                 if (sscanf(line, " %d %d %llu %128[^\n ]",
474                            &ma, &mi, &sz, ptname) != 4)
475                         continue;
476                 devs[which] = makedev(ma, mi);
477
478                 DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname));
479
480                 /* Skip whole disk devs unless they have no partitions
481                  * If we don't have a partition on this dev, also
482                  * check previous dev to see if it didn't have a partn.
483                  * heuristic: partition name ends in a digit.
484                  *
485                  * Skip extended partitions.
486                  * heuristic: size is 1
487                  *
488                  * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
489                  */
490
491                 lens[which] = strlen(ptname);
492                 if (isdigit(ptname[lens[which] - 1])) {
493                         DBG(DEBUG_DEVNAME,
494                             printf("partition dev %s, devno 0x%04X\n",
495                                    ptname, (unsigned int) devs[which]));
496
497                         if (sz > 1)
498                                 probe_one(cache, ptname, devs[which], 0, 
499                                           only_if_new);
500                         lens[which] = 0;
501                         lens[last] = 0;
502                 } else if (lens[last] && strncmp(ptnames[last], ptname,
503                                                  lens[last])) {
504                         DBG(DEBUG_DEVNAME,
505                             printf("whole dev %s, devno 0x%04X\n",
506                                    ptnames[last], (unsigned int) devs[last]));
507                         probe_one(cache, ptnames[last], devs[last], 0,
508                                   only_if_new);
509                         lens[last] = 0;
510                 }
511         }
512
513         /* Handle the last device if it wasn't partitioned */
514         if (lens[which])
515                 probe_one(cache, ptname, devs[which], 0, only_if_new);
516
517         fclose(proc);
518         blkid_flush_cache(cache);
519         return 0;
520 }
521
522 int blkid_probe_all(blkid_cache cache)
523 {
524         int ret;
525
526         DBG(DEBUG_PROBE, printf("Begin blkid_probe_all()\n"));
527         ret = probe_all(cache, 0);
528         cache->bic_time = time(0);
529         cache->bic_flags |= BLKID_BIC_FL_PROBED;
530         DBG(DEBUG_PROBE, printf("End blkid_probe_all()\n"));
531         return ret;
532 }
533
534 int blkid_probe_all_new(blkid_cache cache)
535 {
536         int ret;
537
538         DBG(DEBUG_PROBE, printf("Begin blkid_probe_all_new()\n"));
539         ret = probe_all(cache, 1);
540         DBG(DEBUG_PROBE, printf("End blkid_probe_all_new()\n"));
541         return ret;
542 }
543
544
545 #ifdef TEST_PROGRAM
546 int main(int argc, char **argv)
547 {
548         blkid_cache cache = NULL;
549         int ret;
550
551         blkid_debug_mask = DEBUG_ALL;
552         if (argc != 1) {
553                 fprintf(stderr, "Usage: %s\n"
554                         "Probe all devices and exit\n", argv[0]);
555                 exit(1);
556         }
557         if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
558                 fprintf(stderr, "%s: error creating cache (%d)\n",
559                         argv[0], ret);
560                 exit(1);
561         }
562         if (blkid_probe_all(cache) < 0)
563                 printf("%s: error probing devices\n", argv[0]);
564
565         blkid_put_cache(cache);
566         return (0);
567 }
568 #endif