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