devname.c revision 7515a7447836708241b2b30ca7d70d1257265e75
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/*
42 * Find a dev struct in the cache by device name, if available.
43 *
44 * If there is no entry with the specified device name, and the create
45 * flag is set, then create an empty device entry.
46 */
47blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
48{
49	blkid_dev dev = NULL, tmp;
50	struct list_head *p, *pnext;
51
52	if (!cache || !devname)
53		return NULL;
54
55	list_for_each(p, &cache->bic_devs) {
56		tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
57		if (strcmp(tmp->bid_name, devname))
58			continue;
59
60		DBG(DEBUG_DEVNAME,
61		    printf("found devname %s in cache\n", tmp->bid_name));
62		dev = tmp;
63		break;
64	}
65
66	if (!dev && (flags & BLKID_DEV_CREATE)) {
67		dev = blkid_new_dev();
68		if (!dev)
69			return NULL;
70		dev->bid_time = INT_MIN;
71		dev->bid_name = blkid_strdup(devname);
72		dev->bid_cache = cache;
73		list_add_tail(&dev->bid_devs, &cache->bic_devs);
74		cache->bic_flags |= BLKID_BIC_FL_CHANGED;
75	}
76
77	if (flags & BLKID_DEV_VERIFY) {
78		dev = blkid_verify(cache, dev);
79		if (!dev || !(dev->bid_flags & BLKID_BID_FL_VERIFIED))
80			return dev;
81		/*
82		 * If the device is verified, then search the blkid
83		 * cache for any entries that match on the type, uuid,
84		 * and label, and verify them; if a cache entry can
85		 * not be verified, then it's stale and so we remove
86		 * it.
87		 */
88		list_for_each_safe(p, pnext, &cache->bic_devs) {
89			blkid_dev dev2;
90			if (!p)
91				break;
92			dev2 = list_entry(p, struct blkid_struct_dev, bid_devs);
93			if (dev2->bid_flags & BLKID_BID_FL_VERIFIED)
94				continue;
95			if (!dev->bid_type || !dev2->bid_type ||
96			    strcmp(dev->bid_type, dev2->bid_type))
97				continue;
98			if (dev->bid_label && dev2->bid_label &&
99			    strcmp(dev->bid_label, dev2->bid_label))
100				continue;
101			if (dev->bid_uuid && dev2->bid_uuid &&
102			    strcmp(dev->bid_uuid, dev2->bid_uuid))
103				continue;
104			if ((dev->bid_label && !dev2->bid_label) ||
105			    (!dev->bid_label && dev2->bid_label) ||
106			    (dev->bid_uuid && !dev2->bid_uuid) ||
107			    (!dev->bid_uuid && dev2->bid_uuid))
108				continue;
109			dev2 = blkid_verify(cache, dev2);
110			if (dev2 && !(dev2->bid_flags & BLKID_BID_FL_VERIFIED))
111				blkid_free_dev(dev2);
112		}
113	}
114	return dev;
115}
116
117/* Directories where we will try to search for device names */
118static const char *dirlist[] = { "/dev", "/devfs", "/devices", NULL };
119
120/*
121 * Probe a single block device to add to the device cache.
122 */
123static void probe_one(blkid_cache cache, const char *ptname,
124		      dev_t devno, int pri, int only_if_new)
125{
126	blkid_dev dev = NULL;
127	struct list_head *p, *pnext;
128	const char **dir;
129	char *devname = NULL;
130
131	/* See if we already have this device number in the cache. */
132	list_for_each_safe(p, pnext, &cache->bic_devs) {
133		blkid_dev tmp = list_entry(p, struct blkid_struct_dev,
134					   bid_devs);
135		if (tmp->bid_devno == devno) {
136			if (only_if_new && !access(tmp->bid_name, F_OK))
137				return;
138			dev = blkid_verify(cache, tmp);
139			if (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED))
140				break;
141			dev = 0;
142		}
143	}
144	if (dev && dev->bid_devno == devno)
145		goto set_pri;
146
147	/*
148	 * Take a quick look at /dev/ptname for the device number.  We check
149	 * all of the likely device directories.  If we don't find it, or if
150	 * the stat information doesn't check out, use blkid_devno_to_devname()
151	 * to find it via an exhaustive search for the device major/minor.
152	 */
153	for (dir = dirlist; *dir; dir++) {
154		struct stat st;
155		char device[256];
156
157		sprintf(device, "%s/%s", *dir, ptname);
158		if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) &&
159		    dev->bid_devno == devno)
160			goto set_pri;
161
162		if (stat(device, &st) == 0 && S_ISBLK(st.st_mode) &&
163		    st.st_rdev == devno) {
164			devname = blkid_strdup(device);
165			break;
166		}
167	}
168	/* Do a short-cut scan of /dev/mapper first */
169	if (!devname)
170		blkid__scan_dir("/dev/mapper", devno, 0, &devname);
171	if (!devname) {
172		devname = blkid_devno_to_devname(devno);
173		if (!devname)
174			return;
175	}
176	dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL);
177	free(devname);
178
179set_pri:
180	if (dev) {
181		if (pri)
182			dev->bid_pri = pri;
183		else if (!strncmp(dev->bid_name, "/dev/mapper/", 11))
184			dev->bid_pri = BLKID_PRI_DM;
185		else if (!strncmp(ptname, "md", 2))
186			dev->bid_pri = BLKID_PRI_MD;
187 	}
188	return;
189}
190
191#define PROC_PARTITIONS "/proc/partitions"
192#define VG_DIR		"/proc/lvm/VGs"
193
194/*
195 * This function initializes the UUID cache with devices from the LVM
196 * proc hierarchy.  We currently depend on the names of the LVM
197 * hierarchy giving us the device structure in /dev.  (XXX is this a
198 * safe thing to do?)
199 */
200#ifdef VG_DIR
201#include <dirent.h>
202static dev_t lvm_get_devno(const char *lvm_device)
203{
204	FILE *lvf;
205	char buf[1024];
206	int ma, mi;
207	dev_t ret = 0;
208
209	DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device));
210	if ((lvf = fopen(lvm_device, "r")) == NULL) {
211		DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno,
212					  strerror(errno)));
213		return 0;
214	}
215
216	while (fgets(buf, sizeof(buf), lvf)) {
217		if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) {
218			ret = makedev(ma, mi);
219			break;
220		}
221	}
222	fclose(lvf);
223
224	return ret;
225}
226
227static void lvm_probe_all(blkid_cache cache, int only_if_new)
228{
229	DIR		*vg_list;
230	struct dirent	*vg_iter;
231	int		vg_len = strlen(VG_DIR);
232	dev_t		dev;
233
234	if ((vg_list = opendir(VG_DIR)) == NULL)
235		return;
236
237	DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR));
238
239	while ((vg_iter = readdir(vg_list)) != NULL) {
240		DIR		*lv_list;
241		char		*vdirname;
242		char		*vg_name;
243		struct dirent	*lv_iter;
244
245		vg_name = vg_iter->d_name;
246		if (!strcmp(vg_name, ".") || !strcmp(vg_name, ".."))
247			continue;
248		vdirname = malloc(vg_len + strlen(vg_name) + 8);
249		if (!vdirname)
250			goto exit;
251		sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name);
252
253		lv_list = opendir(vdirname);
254		free(vdirname);
255		if (lv_list == NULL)
256			continue;
257
258		while ((lv_iter = readdir(lv_list)) != NULL) {
259			char		*lv_name, *lvm_device;
260
261			lv_name = lv_iter->d_name;
262			if (!strcmp(lv_name, ".") || !strcmp(lv_name, ".."))
263				continue;
264
265			lvm_device = malloc(vg_len + strlen(vg_name) +
266					    strlen(lv_name) + 8);
267			if (!lvm_device) {
268				closedir(lv_list);
269				goto exit;
270			}
271			sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
272				lv_name);
273			dev = lvm_get_devno(lvm_device);
274			sprintf(lvm_device, "%s/%s", vg_name, lv_name);
275			DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n",
276						  lvm_device,
277						  (unsigned int) dev));
278			probe_one(cache, lvm_device, dev, BLKID_PRI_LVM,
279				  only_if_new);
280			free(lvm_device);
281		}
282		closedir(lv_list);
283	}
284exit:
285	closedir(vg_list);
286}
287#endif
288
289#define PROC_EVMS_VOLUMES "/proc/evms/volumes"
290
291static int
292evms_probe_all(blkid_cache cache, int only_if_new)
293{
294	char line[100];
295	int ma, mi, sz, num = 0;
296	FILE *procpt;
297	char device[110];
298
299	procpt = fopen(PROC_EVMS_VOLUMES, "r");
300	if (!procpt)
301		return 0;
302	while (fgets(line, sizeof(line), procpt)) {
303		if (sscanf (line, " %d %d %d %*s %*s %[^\n ]",
304			    &ma, &mi, &sz, device) != 4)
305			continue;
306
307		DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n",
308					  device, ma, mi));
309
310		probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS,
311			  only_if_new);
312		num++;
313	}
314	fclose(procpt);
315	return num;
316}
317
318/*
319 * Read the device data for all available block devices in the system.
320 */
321static int probe_all(blkid_cache cache, int only_if_new)
322{
323	FILE *proc;
324	char line[1024];
325	char ptname0[128], ptname1[128], *ptname = 0;
326	char *ptnames[2];
327	dev_t devs[2];
328	int ma, mi;
329	unsigned long long sz;
330	int lens[2] = { 0, 0 };
331	int which = 0, last = 0;
332
333	ptnames[0] = ptname0;
334	ptnames[1] = ptname1;
335
336	if (!cache)
337		return -BLKID_ERR_PARAM;
338
339	if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
340	    time(0) - cache->bic_time < BLKID_PROBE_INTERVAL)
341		return 0;
342
343	blkid_read_cache(cache);
344	evms_probe_all(cache, only_if_new);
345#ifdef VG_DIR
346	lvm_probe_all(cache, only_if_new);
347#endif
348
349	proc = fopen(PROC_PARTITIONS, "r");
350	if (!proc)
351		return -BLKID_ERR_PROC;
352
353	while (fgets(line, sizeof(line), proc)) {
354		last = which;
355		which ^= 1;
356		ptname = ptnames[which];
357
358		if (sscanf(line, " %d %d %llu %128[^\n ]",
359			   &ma, &mi, &sz, ptname) != 4)
360			continue;
361		devs[which] = makedev(ma, mi);
362
363		DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname));
364
365		/* Skip whole disk devs unless they have no partitions.
366		 * If base name of device has changed, also
367		 * check previous dev to see if it didn't have a partn.
368		 * heuristic: partition name ends in a digit, & partition
369		 * names contain whole device name as substring.
370		 *
371		 * Skip extended partitions.
372		 * heuristic: size is 1
373		 *
374		 * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
375		 */
376
377		lens[which] = strlen(ptname);
378
379		/* ends in a digit, clearly a partition, so check */
380		if (isdigit(ptname[lens[which] - 1])) {
381			DBG(DEBUG_DEVNAME,
382			    printf("partition dev %s, devno 0x%04X\n",
383				   ptname, (unsigned int) devs[which]));
384
385			if (sz > 1)
386				probe_one(cache, ptname, devs[which], 0,
387					  only_if_new);
388			lens[which] = 0;	/* mark as checked */
389		}
390
391		/*
392		 * If last was not checked because it looked like a whole-disk
393		 * dev, and the device's base name has changed,
394		 * check last as well.
395		 */
396		if (lens[last] && strncmp(ptnames[last], ptname, lens[last])) {
397			DBG(DEBUG_DEVNAME,
398			    printf("whole dev %s, devno 0x%04X\n",
399				   ptnames[last], (unsigned int) devs[last]));
400			probe_one(cache, ptnames[last], devs[last], 0,
401				  only_if_new);
402			lens[last] = 0;
403		}
404	}
405
406	/* Handle the last device if it wasn't partitioned */
407	if (lens[which])
408		probe_one(cache, ptname, devs[which], 0, only_if_new);
409
410	fclose(proc);
411	blkid_flush_cache(cache);
412	return 0;
413}
414
415int blkid_probe_all(blkid_cache cache)
416{
417	int ret;
418
419	DBG(DEBUG_PROBE, printf("Begin blkid_probe_all()\n"));
420	ret = probe_all(cache, 0);
421	cache->bic_time = time(0);
422	cache->bic_flags |= BLKID_BIC_FL_PROBED;
423	DBG(DEBUG_PROBE, printf("End blkid_probe_all()\n"));
424	return ret;
425}
426
427int blkid_probe_all_new(blkid_cache cache)
428{
429	int ret;
430
431	DBG(DEBUG_PROBE, printf("Begin blkid_probe_all_new()\n"));
432	ret = probe_all(cache, 1);
433	DBG(DEBUG_PROBE, printf("End blkid_probe_all_new()\n"));
434	return ret;
435}
436
437
438#ifdef TEST_PROGRAM
439int main(int argc, char **argv)
440{
441	blkid_cache cache = NULL;
442	int ret;
443
444	blkid_debug_mask = DEBUG_ALL;
445	if (argc != 1) {
446		fprintf(stderr, "Usage: %s\n"
447			"Probe all devices and exit\n", argv[0]);
448		exit(1);
449	}
450	if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
451		fprintf(stderr, "%s: error creating cache (%d)\n",
452			argv[0], ret);
453		exit(1);
454	}
455	if (blkid_probe_all(cache) < 0)
456		printf("%s: error probing devices\n", argv[0]);
457
458	blkid_put_cache(cache);
459	return (0);
460}
461#endif
462