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