mirror of
https://github.com/sheumann/hush.git
synced 2024-08-17 00:29:03 +00:00
430 lines
9.0 KiB
C
430 lines
9.0 KiB
C
/* vi: set sw=4 ts=4: */
|
|
/*
|
|
* Support functions for mounting devices by label/uuid
|
|
*
|
|
* Copyright (C) 2006 by Jason Schoon <floydpink@gmail.com>
|
|
* Some portions cribbed from e2fsprogs, util-linux, dosfstools
|
|
*
|
|
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
|
|
*/
|
|
|
|
#include "volume_id_internal.h"
|
|
|
|
//#define BLKGETSIZE64 _IOR(0x12,114,size_t)
|
|
|
|
static struct uuidCache_s {
|
|
struct uuidCache_s *next;
|
|
// int major, minor;
|
|
char *device;
|
|
char *label;
|
|
char *uc_uuid; /* prefix makes it easier to grep for */
|
|
} *uuidCache;
|
|
|
|
/* Returns !0 on error.
|
|
* Otherwise, returns malloc'ed strings for label and uuid
|
|
* (and they can't be NULL, although they can be "") */
|
|
#if !ENABLE_FEATURE_VOLUMEID_ISO9660
|
|
#define get_label_uuid(device, label, uuid, iso_only) \
|
|
get_label_uuid(device, label, uuid)
|
|
#endif
|
|
static int
|
|
get_label_uuid(const char *device, char **label, char **uuid, int iso_only)
|
|
{
|
|
int rv = 1;
|
|
uint64_t size;
|
|
struct volume_id *vid;
|
|
|
|
vid = volume_id_open_node(device);
|
|
if (!vid)
|
|
return rv;
|
|
|
|
if (ioctl(vid->fd, BLKGETSIZE64, &size) != 0)
|
|
size = 0;
|
|
|
|
#if ENABLE_FEATURE_VOLUMEID_ISO9660
|
|
if ((iso_only ?
|
|
volume_id_probe_iso9660(vid, 0) :
|
|
volume_id_probe_all(vid, 0, size)
|
|
) != 0
|
|
) {
|
|
goto ret;
|
|
}
|
|
#else
|
|
if (volume_id_probe_all(vid, 0, size) != 0) {
|
|
goto ret;
|
|
}
|
|
#endif
|
|
|
|
if (vid->label[0] != '\0' || vid->uuid[0] != '\0') {
|
|
*label = xstrndup(vid->label, sizeof(vid->label));
|
|
*uuid = xstrndup(vid->uuid, sizeof(vid->uuid));
|
|
dbg("found label '%s', uuid '%s' on %s", *label, *uuid, device);
|
|
rv = 0;
|
|
}
|
|
ret:
|
|
free_volume_id(vid);
|
|
return rv;
|
|
}
|
|
|
|
/* NB: we take ownership of (malloc'ed) label and uuid */
|
|
static void
|
|
uuidcache_addentry(char *device, /*int major, int minor,*/ char *label, char *uuid)
|
|
{
|
|
struct uuidCache_s *last;
|
|
|
|
if (!uuidCache) {
|
|
last = uuidCache = xzalloc(sizeof(*uuidCache));
|
|
} else {
|
|
for (last = uuidCache; last->next; last = last->next)
|
|
continue;
|
|
last->next = xzalloc(sizeof(*uuidCache));
|
|
last = last->next;
|
|
}
|
|
/*last->next = NULL; - xzalloc did it*/
|
|
// last->major = major;
|
|
// last->minor = minor;
|
|
last->device = device;
|
|
last->label = label;
|
|
last->uc_uuid = uuid;
|
|
}
|
|
|
|
/* If get_label_uuid() on device_name returns success,
|
|
* add a cache entry for this device.
|
|
* If device node does not exist, it will be temporarily created. */
|
|
#if !ENABLE_FEATURE_VOLUMEID_ISO9660
|
|
#define uuidcache_check_device(device_name, ma, mi, iso_only) \
|
|
uuidcache_check_device(device_name, ma, mi)
|
|
#endif
|
|
static void
|
|
uuidcache_check_device(const char *device_name, int ma, int mi, int iso_only)
|
|
{
|
|
char *device, *last_slash;
|
|
char *uuid, *label;
|
|
char *ptr;
|
|
int must_remove = 0;
|
|
int added = 0;
|
|
|
|
last_slash = NULL;
|
|
device = xasprintf("/dev/%s", device_name);
|
|
if (access(device, F_OK) != 0) {
|
|
/* device does not exist, temporarily create */
|
|
int slash_cnt = 0;
|
|
|
|
if ((ma | mi) < 0)
|
|
goto ret; /* we don't know major:minor! */
|
|
|
|
ptr = device;
|
|
while (*ptr)
|
|
if (*ptr++ == '/')
|
|
slash_cnt++;
|
|
if (slash_cnt > 2) {
|
|
// BUG: handles only slash_cnt == 3 case
|
|
last_slash = strrchr(device, '/');
|
|
*last_slash = '\0';
|
|
if (mkdir(device, 0644)) {
|
|
bb_perror_msg("can't create directory %s", device);
|
|
*last_slash = '/';
|
|
last_slash = NULL; /* prevents rmdir */
|
|
} else {
|
|
*last_slash = '/';
|
|
}
|
|
}
|
|
mknod(device, S_IFBLK | 0600, makedev(ma, mi));
|
|
must_remove = 1;
|
|
}
|
|
|
|
uuid = NULL;
|
|
label = NULL;
|
|
if (get_label_uuid(device, &label, &uuid, iso_only) == 0) {
|
|
uuidcache_addentry(device, /*ma, mi,*/ label, uuid);
|
|
/* "device" is owned by cache now, don't free */
|
|
added = 1;
|
|
}
|
|
|
|
if (must_remove)
|
|
unlink(device);
|
|
if (last_slash) {
|
|
*last_slash = '\0';
|
|
rmdir(device);
|
|
}
|
|
ret:
|
|
if (!added)
|
|
free(device);
|
|
}
|
|
|
|
/* Run uuidcache_check_device() for every device mentioned
|
|
* in /proc/partitions */
|
|
static void
|
|
uuidcache_init_partitions(void)
|
|
{
|
|
char line[100];
|
|
int ma, mi;
|
|
unsigned long long sz;
|
|
FILE *procpt;
|
|
int firstPass;
|
|
int handleOnFirst;
|
|
char *chptr;
|
|
|
|
procpt = xfopen("/proc/partitions", "r");
|
|
/*
|
|
# cat /proc/partitions
|
|
major minor #blocks name
|
|
|
|
8 0 293036184 sda
|
|
8 1 6835626 sda1
|
|
8 2 1 sda2
|
|
8 5 979933 sda5
|
|
8 6 15623181 sda6
|
|
8 7 97659103 sda7
|
|
8 8 171935631 sda8
|
|
*/
|
|
for (firstPass = 1; firstPass >= 0; firstPass--) {
|
|
fseek(procpt, 0, SEEK_SET);
|
|
|
|
while (fgets(line, sizeof(line), procpt)) {
|
|
/* The original version of this code used sscanf, but
|
|
diet's sscanf is quite limited */
|
|
chptr = line;
|
|
if (*chptr != ' ') continue;
|
|
chptr = skip_whitespace(chptr);
|
|
|
|
ma = bb_strtou(chptr, &chptr, 0);
|
|
if (ma < 0) continue;
|
|
chptr = skip_whitespace(chptr);
|
|
|
|
mi = bb_strtou(chptr, &chptr, 0);
|
|
if (mi < 0) continue;
|
|
chptr = skip_whitespace(chptr);
|
|
|
|
sz = bb_strtoull(chptr, &chptr, 0);
|
|
if ((long long)sz == -1LL) continue;
|
|
chptr = skip_whitespace(chptr);
|
|
|
|
/* skip extended partitions (heuristic: size 1) */
|
|
if (sz == 1)
|
|
continue;
|
|
|
|
*strchrnul(chptr, '\n') = '\0';
|
|
/* now chptr => device name */
|
|
dbg("/proc/partitions: maj:%d min:%d sz:%llu name:'%s'",
|
|
ma, mi, sz, chptr);
|
|
if (!chptr[0])
|
|
continue;
|
|
|
|
/* look only at md devices on first pass */
|
|
handleOnFirst = (chptr[0] == 'm' && chptr[1] == 'd');
|
|
if (firstPass != handleOnFirst)
|
|
continue;
|
|
|
|
/* heuristic: partition name ends in a digit */
|
|
if (isdigit(chptr[strlen(chptr) - 1])) {
|
|
uuidcache_check_device(chptr, ma, mi, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(procpt);
|
|
}
|
|
|
|
static void
|
|
dev_get_major_minor(char *device_name, int *major, int *minor)
|
|
{
|
|
char dev[16];
|
|
char *dev_path;
|
|
char *colon;
|
|
int sz;
|
|
|
|
dev_path = xasprintf("/sys/block/%s/dev", device_name);
|
|
sz = open_read_close(dev_path, dev, sizeof(dev) - 1);
|
|
if (sz < 0)
|
|
goto ret;
|
|
dev[sz] = '\0';
|
|
|
|
colon = strchr(dev, ':');
|
|
if (!colon)
|
|
goto ret;
|
|
*major = bb_strtou(dev, NULL, 10);
|
|
*minor = bb_strtou(colon + 1, NULL, 10);
|
|
|
|
ret:
|
|
free(dev_path);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
uuidcache_init_cdroms(void)
|
|
{
|
|
#define PROC_CDROMS "/proc/sys/dev/cdrom/info"
|
|
char line[100];
|
|
int ma, mi;
|
|
FILE *proccd;
|
|
|
|
proccd = fopen(PROC_CDROMS, "r");
|
|
if (!proccd) {
|
|
// static smallint warn = 0;
|
|
// if (!warn) {
|
|
// warn = 1;
|
|
// bb_error_msg("can't open %s, UUID and LABEL "
|
|
// "conversion cannot be done for CD-Roms",
|
|
// PROC_CDROMS);
|
|
// }
|
|
return;
|
|
}
|
|
|
|
while (fgets(line, sizeof(line), proccd)) {
|
|
static const char drive_name_string[] ALIGN1 = "drive name:";
|
|
|
|
if (strncmp(line, drive_name_string, sizeof(drive_name_string) - 1) == 0) {
|
|
char *device_name;
|
|
|
|
device_name = strtok(skip_whitespace(line + sizeof(drive_name_string) - 1), " \t\n");
|
|
while (device_name && device_name[0]) {
|
|
ma = mi = -1;
|
|
dev_get_major_minor(device_name, &ma, &mi);
|
|
uuidcache_check_device(device_name, ma, mi, 1);
|
|
device_name = strtok(NULL, " \t\n");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
fclose(proccd);
|
|
}
|
|
|
|
static void
|
|
uuidcache_init(void)
|
|
{
|
|
if (uuidCache)
|
|
return;
|
|
|
|
uuidcache_init_partitions();
|
|
uuidcache_init_cdroms();
|
|
}
|
|
|
|
#define UUID 1
|
|
#define VOL 2
|
|
|
|
#ifdef UNUSED
|
|
static char *
|
|
get_spec_by_x(int n, const char *t, int *majorPtr, int *minorPtr)
|
|
{
|
|
struct uuidCache_s *uc;
|
|
|
|
uuidcache_init();
|
|
uc = uuidCache;
|
|
|
|
while (uc) {
|
|
switch (n) {
|
|
case UUID:
|
|
if (strcmp(t, uc->uc_uuid) == 0) {
|
|
*majorPtr = uc->major;
|
|
*minorPtr = uc->minor;
|
|
return uc->device;
|
|
}
|
|
break;
|
|
case VOL:
|
|
if (strcmp(t, uc->label) == 0) {
|
|
*majorPtr = uc->major;
|
|
*minorPtr = uc->minor;
|
|
return uc->device;
|
|
}
|
|
break;
|
|
}
|
|
uc = uc->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static unsigned char
|
|
fromhex(char c)
|
|
{
|
|
if (isdigit(c))
|
|
return (c - '0');
|
|
return ((c|0x20) - 'a' + 10);
|
|
}
|
|
|
|
static char *
|
|
get_spec_by_uuid(const char *s, int *major, int *minor)
|
|
{
|
|
unsigned 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, (char *)uuid, major, minor);
|
|
|
|
bad_uuid:
|
|
fprintf(stderr, _("mount: bad UUID"));
|
|
return 0;
|
|
}
|
|
|
|
static char *
|
|
get_spec_by_volume_label(const char *s, int *major, int *minor)
|
|
{
|
|
return get_spec_by_x(VOL, s, major, minor);
|
|
}
|
|
|
|
static int display_uuid_cache(void)
|
|
{
|
|
struct uuidCache_s *u;
|
|
size_t i;
|
|
|
|
uuidcache_init();
|
|
|
|
u = uuidCache;
|
|
while (u) {
|
|
printf("%s %s %s\n", u->device, u->label, u->uc_uuid);
|
|
u = u->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif // UNUSED
|
|
|
|
|
|
/* Used by mount and findfs */
|
|
|
|
char *get_devname_from_label(const char *spec)
|
|
{
|
|
struct uuidCache_s *uc;
|
|
int spec_len = strlen(spec);
|
|
|
|
uuidcache_init();
|
|
uc = uuidCache;
|
|
while (uc) {
|
|
// FIXME: empty label ("LABEL=") matches anything??!
|
|
if (uc->label[0] && strncmp(spec, uc->label, spec_len) == 0) {
|
|
return xstrdup(uc->device);
|
|
}
|
|
uc = uc->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char *get_devname_from_uuid(const char *spec)
|
|
{
|
|
struct uuidCache_s *uc;
|
|
|
|
uuidcache_init();
|
|
uc = uuidCache;
|
|
while (uc) {
|
|
/* case of hex numbers doesn't matter */
|
|
if (strcasecmp(spec, uc->uc_uuid) == 0) {
|
|
return xstrdup(uc->device);
|
|
}
|
|
uc = uc->next;
|
|
}
|
|
return NULL;
|
|
}
|