| /* | 
 |  * get_device_by_label.h | 
 |  * | 
 |  * Copyright 1999 by Andries Brouwer | 
 |  * Copyright 1999, 2000 by Theodore Ts'o | 
 |  * | 
 |  * This file may be redistributed under the terms of the GNU Public | 
 |  * License. | 
 |  * | 
 |  * Taken from aeb's mount, 990619 | 
 |  * Updated from aeb's mount, 20000725 | 
 |  * Added call to ext2fs_find_block_device, so that we can find devices | 
 |  * 	even if devfs (ugh) is compiled in, but not mounted, since | 
 |  * 	this messes up /proc/partitions, by TYT. | 
 |  */ | 
 |  | 
 | #include <stdio.h> | 
 | #include <string.h> | 
 | #include <stdlib.h> | 
 | #include <ctype.h> | 
 | #include <fcntl.h> | 
 | #include <unistd.h> | 
 | #include <sys/types.h> | 
 | #include <sys/stat.h> | 
 | #ifdef HAVE_SYS_MKDEV_H | 
 | #include <sys/mkdev.h> | 
 | #endif | 
 | #ifdef HAVE_SYS_SYSMACROS_H | 
 | #include <sys/sysmacros.h> | 
 | #endif | 
 | #include <dirent.h> | 
 | #include "nls-enable.h" | 
 | #include "fsck.h" | 
 | #include "get_device_by_label.h" | 
 |  | 
 | /* function prototype from libext2 */ | 
 | extern char *ext2fs_find_block_device(dev_t device); | 
 |  | 
 | #define PROC_PARTITIONS "/proc/partitions" | 
 | #define PROC_EVMS_VOLUMES "/proc/evms/volumes" | 
 | #define DEVLABELDIR	"/dev" | 
 | #define VG_DIR          "/proc/lvm/VGs" | 
 |  | 
 | #define EXT2_SUPER_MAGIC    0xEF53 | 
 | struct ext2_super_block { | 
 |         unsigned char   s_dummy1[56]; | 
 |         unsigned char   s_magic[2]; | 
 |         unsigned char   s_dummy2[46]; | 
 |         unsigned char   s_uuid[16]; | 
 |         unsigned char   s_volume_name[16]; | 
 | }; | 
 | #define ext2magic(s)    ((unsigned int) s.s_magic[0] + (((unsigned int) s.s_magic[1]) << 8)) | 
 |  | 
 | #define XFS_SUPER_MAGIC "XFSB" | 
 | struct xfs_super_block { | 
 | 	unsigned char	s_magic[4]; | 
 | 	unsigned char	s_dummy[28]; | 
 | 	unsigned char	s_uuid[16]; | 
 | 	unsigned char	s_dummy2[60]; | 
 | 	unsigned char	s_fname[12]; | 
 | }; | 
 |  | 
 | struct reiserfs_super_block | 
 | { | 
 |  	/* Following entries are based on reiserfsutils 3.6.3  | 
 | 	 * (Copyright Hans Reiser) since Linux kernel headers | 
 | 	 * (2.4.18) seemed not up-to-date. */ | 
 | 	unsigned char	s_dummy1[52]; | 
 | 	unsigned char	s_magic[10]; | 
 | 	unsigned char	s_dummy2[10]; | 
 | 	unsigned char	s_version[2]; | 
 | 	unsigned char	s_dummy3[10]; | 
 | 	unsigned char	s_uuid[16]; | 
 | 	unsigned char	s_label[16]; | 
 | 	unsigned char	s_unused[88]; | 
 | }; | 
 |  | 
 | #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" /* v. 3.6 */ | 
 | #define REISER3FS_SUPER_MAGIC_STRING "ReIsEr3Fs" /* Journal Relocation */ | 
 | #define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024) | 
 | /* the spot for the super in versions 3.5 - 3.5.10 (inclusive) -  | 
 |  * We'll use it in case volume has been converted. */ | 
 | #define REISERFS_OLD_DISK_OFFSET_IN_BYTES (8 * 1024) | 
 | #define reiserversion(s)	((unsigned) (s).s_version[0] + (((unsigned) (s).s_version[1]) << 8)) | 
 |  | 
 | /* We're checking for ReiserFS v. 3.6 and RJ 3.6 SB */ | 
 | static int | 
 | reiser_supports_uuid (struct reiserfs_super_block *sb) | 
 | { | 
 | 	return (strncmp(sb->s_magic, REISER2FS_SUPER_MAGIC_STRING, | 
 | 			    strlen (REISER2FS_SUPER_MAGIC_STRING)) == 0) | 
 | 		|| (strncmp(sb->s_magic, REISER3FS_SUPER_MAGIC_STRING, | 
 | 			    strlen (REISER3FS_SUPER_MAGIC_STRING)) == 0 | 
 | 				&& reiserversion(*sb) == 2); | 
 | } | 
 |  | 
 | static struct uuidCache_s { | 
 | 	struct uuidCache_s *next; | 
 | 	char uuid[16]; | 
 | 	char *label; | 
 | 	char *device; | 
 | } *uuidCache = NULL; | 
 |  | 
 | char *string_copy(const char *s) | 
 | { | 
 | 	char	*ret; | 
 |  | 
 | 	ret = malloc(strlen(s)+1); | 
 | 	if (ret) | 
 | 		strcpy(ret, s); | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* for now, only ext2, ext3, xfs and ReiserFS are supported */ | 
 | static int | 
 | get_label_uuid(const char *device, char **label, char *uuid) { | 
 |  | 
 | 	/* start with ext2/3, xfs and ReiserFS tests, taken from mount_guess_fstype */ | 
 | 	/* should merge these later */ | 
 | 	int fd; | 
 | 	size_t label_size; | 
 | 	unsigned char *sb_uuid = 0, *sb_label = 0; | 
 | 	struct ext2_super_block e2sb; | 
 | 	struct xfs_super_block xfsb; | 
 | 	struct reiserfs_super_block rfsb; | 
 |  | 
 | 	fd = open(device, O_RDONLY); | 
 | 	if (fd < 0) | 
 | 		return 1; | 
 |  | 
 | 	if (lseek(fd, 1024, SEEK_SET) == 1024 | 
 | 	    && read(fd, (char *) &e2sb, sizeof(e2sb)) == sizeof(e2sb) | 
 | 	    && (ext2magic(e2sb) == EXT2_SUPER_MAGIC)) { | 
 | 		sb_uuid = e2sb.s_uuid; | 
 | 		sb_label = e2sb.s_volume_name; | 
 | 		label_size = sizeof(e2sb.s_volume_name); | 
 | 	} else if (lseek(fd, 0, SEEK_SET) == 0 | 
 | 	    && read(fd, (char *) &xfsb, sizeof(xfsb)) == sizeof(xfsb) | 
 | 	    && strncmp((char *) &xfsb.s_magic, XFS_SUPER_MAGIC, 4) == 0) { | 
 | 		sb_uuid = xfsb.s_uuid; | 
 | 		sb_label = xfsb.s_fname; | 
 | 		label_size = sizeof(xfsb.s_fname); | 
 | 	} else if ((lseek(fd, REISERFS_OLD_DISK_OFFSET_IN_BYTES, SEEK_SET) | 
 | 			    == REISERFS_OLD_DISK_OFFSET_IN_BYTES | 
 | 	    && read(fd, (char *) &rfsb, sizeof(rfsb)) == sizeof(rfsb) | 
 | 	    && (reiser_supports_uuid(&rfsb))) | 
 | 	    || (lseek(fd, REISERFS_DISK_OFFSET_IN_BYTES, SEEK_SET) | 
 | 			    == REISERFS_DISK_OFFSET_IN_BYTES | 
 | 	    && read(fd, (char *) &rfsb, sizeof(rfsb)) == sizeof(rfsb) | 
 | 	    && (reiser_supports_uuid(&rfsb)))) { | 
 | 		sb_uuid = rfsb.s_uuid; | 
 | 		sb_label = rfsb.s_label; | 
 | 		label_size = sizeof(rfsb.s_label); | 
 | 	} else { | 
 | 		close(fd); | 
 | 		return 1; | 
 | 	} | 
 |  | 
 | 	close(fd); | 
 | 	if (sb_uuid) | 
 | 		memcpy(uuid, sb_uuid, sizeof(e2sb.s_uuid)); | 
 | 	if (sb_label) { | 
 | 		if ((*label = calloc(label_size + 1, 1)) != NULL) | 
 | 			memcpy(*label, sb_label, label_size); | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | #define CBBUF  (16 * 1024) | 
 |  | 
 | static void | 
 | uuidcache_addentry(char *device, char *label, char *uuid) { | 
 | 	struct uuidCache_s *last; | 
 |  | 
 | 	if (!uuidCache) { | 
 | 		last = uuidCache = malloc(sizeof(*uuidCache)); | 
 | 	} else { | 
 | 		for (last = uuidCache; last->next; last = last->next) ; | 
 | 		last->next = malloc(sizeof(*uuidCache)); | 
 | 		last = last->next; | 
 | 	} | 
 | 	last->next = NULL; | 
 | 	last->device = device; | 
 | 	last->label = label; | 
 | 	memcpy(last->uuid, uuid, sizeof(last->uuid)); | 
 | } | 
 |  | 
 | /* | 
 |  * This function initializes the UUID cache with devices from the LVM | 
 |  * proc hierarchy.  We currently depend on the names of the LVM | 
 |  * hierarchy giving us the device structure in /dev.  (XXX is this a | 
 |  * safe thing to do?) | 
 |  */ | 
 | #ifdef VG_DIR | 
 | static void init_lvm(void) | 
 | { | 
 | 	DIR		*vg_dir, *lv_list; | 
 | 	char		*vdirname, *lvm_device; | 
 | 	char		uuid[16], *label, *vname, *lname; | 
 | 	struct dirent 	*vg_iter, *lv_iter; | 
 | 	 | 
 | 	if ((vg_dir = opendir(VG_DIR)) == NULL) | 
 | 		return; | 
 |  | 
 | 	while ((vg_iter = readdir(vg_dir)) != 0) { | 
 | 		vname = vg_iter->d_name; | 
 | 		if (!strcmp(vname, ".") || !strcmp(vname, "..")) | 
 | 			continue; | 
 | 		vdirname = malloc(strlen(VG_DIR)+strlen(vname)+8); | 
 | 		if (!vdirname) { | 
 | 			closedir(vg_dir); | 
 | 			return; | 
 | 		} | 
 | 		sprintf(vdirname, "%s/%s/LVs", VG_DIR, vname); | 
 |  | 
 | 		lv_list = opendir(vdirname); | 
 | 		free(vdirname); | 
 | 		if (lv_list == NULL) | 
 | 			return; | 
 |  | 
 | 		while ((lv_iter = readdir(lv_list)) != 0) { | 
 | 			lname = lv_iter->d_name; | 
 | 			if (!strcmp(lname, ".") || !strcmp(lname, "..")) | 
 | 				continue; | 
 |  | 
 | 			lvm_device = malloc(strlen(DEVLABELDIR) + | 
 | 					    strlen(vname)+ | 
 | 					    strlen(lname)+8); | 
 | 			if (!lvm_device) { | 
 | 				closedir(lv_list); | 
 | 				closedir(vg_dir); | 
 | 				return; | 
 | 			} | 
 | 			sprintf(lvm_device, "%s/%s/%s", DEVLABELDIR, | 
 | 				vname, lname); | 
 | 			if (!get_label_uuid(lvm_device, &label, uuid)) { | 
 | 				uuidcache_addentry(string_copy(lvm_device), | 
 | 						   label, uuid); | 
 | 			} else | 
 | 				free(lvm_device); | 
 | 		} | 
 | 		closedir(lv_list); | 
 | 	} | 
 | 	closedir( vg_dir ); | 
 | } | 
 | #endif | 
 |  | 
 | static void | 
 | read_partitions(void) | 
 | { | 
 | 	char line[100]; | 
 | 	char *s; | 
 | 	int ma, mi, sz; | 
 | 	static char ptname[100]; | 
 | 	FILE *procpt; | 
 | 	char uuid[16], *label, *devname; | 
 | 	char device[110]; | 
 | 	dev_t	dev; | 
 | 	struct stat statbuf; | 
 | 	int firstPass; | 
 | 	int handleOnFirst; | 
 | 	char *iobuf; | 
 |  | 
 | 	procpt = fopen(PROC_PARTITIONS, "r"); | 
 | 	if (!procpt) | 
 | 		return; | 
 |  | 
 | 	iobuf = (char *)malloc(CBBUF); | 
 | 	if (iobuf) | 
 | 		setvbuf(procpt, iobuf, _IOFBF, CBBUF); | 
 |  | 
 | 	for (firstPass = 1; firstPass >= 0; firstPass--) { | 
 | 	    fseek(procpt, 0, SEEK_SET); | 
 |  | 
 | 	    while (fgets(line, sizeof(line), procpt)) { | 
 | 		if (sscanf (line, " %d %d %d %[^\n ]", | 
 | 			    &ma, &mi, &sz, ptname) != 4) | 
 | 			continue; | 
 |  | 
 | 		/* skip extended partitions (heuristic: size 1) */ | 
 | 		if (sz == 1) | 
 | 			continue; | 
 |  | 
 | 		/* look only at md devices on first pass */ | 
 | 		handleOnFirst = !strncmp(ptname, "md", 2); | 
 | 		if (firstPass != handleOnFirst) | 
 | 			continue; | 
 |  | 
 | 		/* skip entire disk (minor 0, 64, ... on ide; | 
 | 		   0, 16, ... on sd) */ | 
 | 		/* heuristic: partition name ends in a digit */ | 
 |  | 
 | 		for(s = ptname; *s; s++); | 
 | 		if (isdigit(s[-1])) { | 
 | 			/* | 
 | 			 * We first look in /dev for the device, but | 
 | 			 * if we don't find it, or if the stat | 
 | 			 * information doesn't check out, we use | 
 | 			 * ext2fs_find_block_device to find it. | 
 | 			 */ | 
 | 			sprintf(device, "%s/%s", DEVLABELDIR, ptname); | 
 | 			dev = makedev(ma, mi); | 
 | 			if ((stat(device, &statbuf) < 0) || | 
 | 			    (statbuf.st_rdev != dev)) { | 
 | 				devname = ext2fs_find_block_device(dev); | 
 | 			} else | 
 | 				devname = string_copy(device); | 
 | 			if (!devname) | 
 | 				continue; | 
 | #ifdef DEBUG | 
 | 			printf("Checking partition %s (%d, %d)\n", | 
 | 			       devname, ma, mi); | 
 | #endif | 
 | 			if (!get_label_uuid(devname, &label, uuid)) | 
 | 				uuidcache_addentry(devname, label, uuid); | 
 | 			else | 
 | 				free(devname); | 
 | 		} | 
 | 	    } | 
 | 	} | 
 |  | 
 | 	fclose(procpt); | 
 | 	if (iobuf) | 
 | 		free(iobuf); | 
 | } | 
 |  | 
 | static void | 
 | read_evms(void) | 
 | { | 
 | 	char line[100]; | 
 | 	int ma, mi, sz; | 
 | 	FILE *procpt; | 
 | 	char uuid[16], *label, *devname; | 
 | 	char device[110]; | 
 | 	dev_t	dev; | 
 | 	struct stat statbuf; | 
 |  | 
 | 	procpt = fopen(PROC_EVMS_VOLUMES, "r"); | 
 | 	if (!procpt) | 
 | 		return; | 
 | 	while (fgets(line, sizeof(line), procpt)) { | 
 | 		if (sscanf (line, " %d %d %d %*s %*s %[^\n ]", | 
 | 			    &ma, &mi, &sz, device) != 4) | 
 | 			continue; | 
 |  | 
 | 		/* | 
 | 		 * We first look for the device in the named location, | 
 | 		 * but if we don't find it, or if the stat information | 
 | 		 * doesn't check out, we use ext2fs_find_block_device | 
 | 		 * to find it. | 
 | 		 */ | 
 | 		dev = makedev(ma, mi); | 
 | 		if ((stat(device, &statbuf) < 0) || (statbuf.st_rdev != dev)) { | 
 | 			devname = ext2fs_find_block_device(dev); | 
 | 		} else | 
 | 			devname = string_copy(device); | 
 | 		if (!devname) | 
 | 			continue; | 
 | #ifdef DEBUG | 
 | 		printf("Checking partition %s (%d, %d)\n", | 
 | 		       devname, ma, mi); | 
 | #endif | 
 | 		if (!get_label_uuid(devname, &label, uuid)) | 
 | 			uuidcache_addentry(devname, label, uuid); | 
 | 		else | 
 | 			free(devname); | 
 | 	} | 
 | 	fclose(procpt); | 
 | } | 
 |  | 
 | static void | 
 | uuidcache_init(void) | 
 | { | 
 | 	if (uuidCache) | 
 | 		return; | 
 |  | 
 | #ifdef VG_DIR | 
 | 	init_lvm(); | 
 | #endif | 
 | 	read_evms(); | 
 | 	read_partitions(); | 
 | } | 
 |  | 
 | #define UUID   1 | 
 | #define VOL    2 | 
 |  | 
 | static char * | 
 | get_spec_by_x(int n, const char *t) { | 
 | 	struct uuidCache_s *uc; | 
 |  | 
 | 	uuidcache_init(); | 
 | 	uc = uuidCache; | 
 |  | 
 | 	if (t == NULL) | 
 | 		return NULL; | 
 |  | 
 | 	while(uc) { | 
 | 		switch (n) { | 
 | 		case UUID: | 
 | 			if (!memcmp(t, uc->uuid, sizeof(uc->uuid))) | 
 | 				return string_copy(uc->device); | 
 | 			break; | 
 | 		case VOL: | 
 | 			if (!strcmp(t, uc->label)) | 
 | 				return string_copy(uc->device); | 
 | 			break; | 
 | 		} | 
 | 		uc = uc->next; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static char fromhex(char c) | 
 | { | 
 | 	if (isdigit(c)) | 
 | 		return (c - '0'); | 
 | 	else if (islower(c)) | 
 | 		return (c - 'a' + 10); | 
 | 	else | 
 | 		return (c - 'A' + 10); | 
 | } | 
 |  | 
 | char * | 
 | get_spec_by_uuid(const char *s) | 
 | { | 
 | 	char uuid[16]; | 
 | 	int i; | 
 |  | 
 | 	if (strlen(s) != 36 || | 
 | 	    s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-') | 
 | 		goto bad_uuid; | 
 | 	for (i=0; i<16; i++) { | 
 | 	    if (*s == '-') s++; | 
 | 	    if (!isxdigit(s[0]) || !isxdigit(s[1])) | 
 | 		    goto bad_uuid; | 
 | 	    uuid[i] = ((fromhex(s[0])<<4) | fromhex(s[1])); | 
 | 	    s += 2; | 
 | 	} | 
 | 	return get_spec_by_x(UUID, uuid); | 
 |  | 
 |  bad_uuid: | 
 | 	fprintf(stderr, _("WARNING: %s: bad UUID\n"), s); | 
 | 	return NULL; | 
 | } | 
 |  | 
 | char * | 
 | get_spec_by_volume_label(const char *s) { | 
 | 	return get_spec_by_x(VOL, s); | 
 | } | 
 |  | 
 | const char * | 
 | get_volume_label_by_spec(const char *spec) { | 
 |         struct uuidCache_s *uc; | 
 |  | 
 |         uuidcache_init(); | 
 |         uc = uuidCache; | 
 |  | 
 | 	while(uc) { | 
 | 		if (!strcmp(spec, uc->device)) | 
 | 			return uc->label; | 
 | 		uc = uc->next; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | /* | 
 |  * Interpret the device name if necessary. | 
 |  * Frees the pointer passed to it if we return a different device string. | 
 |  */ | 
 | char *interpret_spec(char *spec) | 
 | { | 
 | 	char *dev = NULL; | 
 |  | 
 | 	if (!spec) | 
 | 		return NULL; | 
 |  | 
 | 	if (!strncmp(spec, "UUID=", 5)) | 
 | 		dev = get_spec_by_uuid(spec+5); | 
 | 	else if (!strncmp(spec, "LABEL=", 6)) | 
 | 		dev = get_spec_by_volume_label(spec+6); | 
 | 	else | 
 | 		dev = string_copy(spec); | 
 | 	return dev; | 
 | } | 
 |  | 
 | #ifdef DEBUG | 
 | main(int argc, char **argv) | 
 | { | 
 | 	uuidcache_init(); | 
 | } | 
 | #endif |