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