blob: 524ec7ca9e46dc815801daa1a704259dca9e4653 [file] [log] [blame]
/*
* mount_by_label.c - aeb
*
* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
* - added Native Language Support
* 2000-01-20 James Antill <james@and.org>
* - Added error message if /proc/partitions cannot be opened
* 2000-05-09 Erik Troan <ewt@redhat.com>
* - Added cache for UUID and disk labels
* 2000-11-07 Nathan Scott <nathans@sgi.com>
* - Added XFS support
* 2001-11-22 Kirby Bohling <kbohling@birddog.com>
* - Added support of labels on LVM
* 2002-03-21 Christoph Hellwig <hch@infradead.org>
* - Added JFS support
* 2002-07-11 Christoph Hellwig <hch@infradead.org>
* - Added JFS v2 format support
* 2002-07-26 Luciano Chavez <lnx1138@us.ibm.com>
* - Added EVMS support
*/
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h> /* needed for opendir */
#include <dirent.h>
#include "sundries.h" /* for xstrdup */
#include "linux_fs.h"
#include "get_label_uuid.h"
#include "mount_by_label.h"
#include "nls.h"
#define PROC_PARTITIONS "/proc/partitions"
#define DEVLABELDIR "/dev"
#define VG_DIR "/proc/lvm/VGs"
#define EVMS_VOLUME_NAME_SIZE 127
#define PROC_EVMS_VOLUMES "/proc/evms/volumes"
static struct uuidCache_s {
struct uuidCache_s *next;
char uuid[16];
char *label;
char *device;
} *uuidCache = NULL;
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));
}
/* LVM support - Kirby Bohling */
static void
uuidcache_init_lvm(void) {
char buffer[PATH_MAX];
char lvm_device[PATH_MAX];
DIR *vg_dir, *lv_list;
struct dirent *vg_iter, *lv_iter;
char uuid[16], *label;
vg_dir = opendir(VG_DIR);
if (vg_dir == NULL) /* to be expected */
return;
seekdir(vg_dir, 2);
while ((vg_iter = readdir(vg_dir)) != 0) {
sprintf(buffer, "%s/%s/LVs", VG_DIR, vg_iter->d_name);
lv_list = opendir(buffer);
if (lv_list == NULL) {
perror("mount (init_lvm)");
continue;
}
seekdir(lv_list, 2);
while ((lv_iter = readdir(lv_list)) != 0) {
/* Now we have the file.. could open it and read out
* where the device is, read the first line, second
* field... Instead we guess.
*/
sprintf(lvm_device, "%s/%s/%s", DEVLABELDIR,
vg_iter->d_name, lv_iter->d_name);
if (!get_label_uuid(lvm_device, &label, uuid))
uuidcache_addentry(strdup(lvm_device),
label, uuid);
}
closedir(lv_list);
}
closedir(vg_dir);
}
static int
uuidcache_init_evms(void) {
FILE *procvol;
char *label;
char uuid[16];
char volname[EVMS_VOLUME_NAME_SIZE+1];
char line[EVMS_VOLUME_NAME_SIZE+80];
procvol = fopen(PROC_EVMS_VOLUMES, "r");
if (!procvol)
return 0;
while (fgets(line, sizeof(line), procvol)) {
if (sscanf(line, "%*d %*d %*d %*s %*s %[^\n]", volname) == 1) {
if (!get_label_uuid(volname, &label, uuid))
uuidcache_addentry(strdup(volname), label, uuid);
}
}
fclose(procvol);
return 1;
}
/*
* xvm is a proprietary sgi volume manager, it goes into /proc/partitions
* like this:
*
* 4 0 2210817 xvm/local/vol/myvolume/data/block
* 4 1 2210817 xvm/local/vol/myvolume/rt/block
* 4 2 2210817 xvm/local/vol/myvolume/log/block
* 4 3 2210818 xvm/local/vol/discs3/data/block
*
* The heuristics here are that the device should start with "xvm,"
* but should not end in "log/block" or "rt/block" - those are
* special devices for the xfs filesystem external log & realtime device.
*/
/*
* XVM support - Eric Sandeen
* Return 1 if this looks like an xvm device that should be scanned
*/
static int
is_xvm(char *ptname)
{
/*
* Scan anything with "xvm" and "data" in its name.
* That might pick up non-data xvm subvols if the
* volumename contains the string 'data' but
* that should be harmless.
*/
if (strstr(ptname, "xvm") && strstr(ptname, "data"))
return 1;
return 0;
}
static void
uuidcache_init(void) {
char line[100];
char *s;
int ma, mi, sz;
static char ptname[100];
FILE *procpt;
char uuid[16], *label;
char device[110];
int firstPass;
int handleOnFirst;
#if 0
char iobuf[32*1024]; /* For setvbuf */
#endif
if (uuidCache)
return;
if (uuidcache_init_evms())
return;
procpt = fopen(PROC_PARTITIONS, "r");
if (!procpt) {
static int warn = 0;
if (!warn++)
error (_("mount: could not open %s, so UUID and LABEL "
"conversion cannot be done.\n"),
PROC_PARTITIONS);
return;
}
#if 0
/* Ugly kludge - the contents of /proc/partitions change in time,
and this causes failures when the file is not read in one go.
In particular, one cannot use stdio on /proc/partitions.
Doing this ourselves is not easy either, since stat returns 0
so the size is unknown. We might try increasing buffer sizes
until a single read gets all. For now only pick a largish buffer size. */
/* All these troubles are mainly caused by people who patch the kernel
to keep statistics in /proc/partitions. Of course, statistics belong
in some /proc/diskstats, not in some /proc file that happened to
exist already. */
setvbuf(procpt, iobuf, _IOFBF, sizeof(iobuf));
#endif
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 */
/* devfs has .../disc and .../part1 etc. */
for(s = ptname; *s; s++);
if (isdigit(s[-1]) || is_xvm(ptname)) {
/*
* Note: this is a heuristic only - there is no reason
* why these devices should live in /dev.
* Perhaps this directory should be specifiable by option.
* One might for example have /devlabel with links to /dev
* for the devices that may be accessed in this way.
* (This is useful, if the cdrom on /dev/hdc must not
* be accessed.)
*/
sprintf(device, "%s/%s", DEVLABELDIR, ptname);
if (!get_label_uuid(device, &label, uuid))
uuidcache_addentry(strdup(device), label, uuid);
}
}
}
fclose(procpt);
uuidcache_init_lvm();
}
#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;
while(uc) {
switch (n) {
case UUID:
if (!memcmp(t, uc->uuid, sizeof(uc->uuid)))
return xstrdup(uc->device);
break;
case VOL:
if (!strcmp(t, uc->label))
return xstrdup(uc->device);
break;
}
uc = uc->next;
}
return NULL;
}
static u_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) {
u_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:
die(EX_USAGE, _("mount: bad UUID"));
return NULL; /* just for gcc */
}
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;
}
/*
* second_occurrence_of_vol_label()
* As labels are user defined they are not necessarily
* system-wide unique. Make sure that they are.
*/
const char *
second_occurrence_of_vol_label (const char *label) {
struct uuidCache_s *last;
int occurrences = 0;
uuidcache_init();
for (last = uuidCache; last->next; last = last->next) {
if (!strcmp(last->label, label)) {
occurrences++;
if (occurrences == 2)
return last->device;
}
}
return NULL;
}