mirror of
https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git
synced 2024-11-28 12:34:13 +08:00
f68b3b0bda
partitions returned in /proc/partitions that begin with with the string "lvm".
493 lines
11 KiB
C
493 lines
11 KiB
C
/*
|
|
* 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 */
|
|
/* OR partition name starts with 'lvm' */
|
|
|
|
for(s = ptname; *s; s++);
|
|
if (isdigit(s[-1]) || !strncmp(ptname, "lvm", 3)) {
|
|
/*
|
|
* 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
|