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