diff --git a/WHATS_NEW_DM b/WHATS_NEW_DM index 05808537d..722e94869 100644 --- a/WHATS_NEW_DM +++ b/WHATS_NEW_DM @@ -1,5 +1,6 @@ Version 1.00.20 - ============================= + Attempt to fix /dev/mapper/control transparently if it's wrong. Configuration-time option for setting uid/gid/mode for /dev/mapper nodes. Update kernel patches for 2.4.27/2.4.28-pre-4 (includes minor fixes). Add --noheadings columns option for colon-separated dmsetup output. diff --git a/libdm/Makefile.in b/libdm/Makefile.in index 5f5309e41..2d7c23a21 100644 --- a/libdm/Makefile.in +++ b/libdm/Makefile.in @@ -1,6 +1,6 @@ # # Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. -# Copyright (C) 2004 Red Hat, Inc. All rights reserved. +# Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. # # This file is part of the device-mapper userspace tools. # @@ -17,7 +17,7 @@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ interface = @interface@ -SOURCES = libdm-common.c $(interface)/libdm-iface.c +SOURCES = libdm-common.c libdm-file.c $(interface)/libdm-iface.c INCLUDES = -I$(interface) diff --git a/libdm/ioctl/libdm-iface.c b/libdm/ioctl/libdm-iface.c index 555cdf627..460a7d2fb 100644 --- a/libdm/ioctl/libdm-iface.c +++ b/libdm/ioctl/libdm-iface.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. * * This file is part of the device-mapper userspace tools. * @@ -13,22 +13,17 @@ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "lib.h" #include "libdm-targets.h" #include "libdm-common.h" -#include "log.h" +#include "libdm-file.h" #ifdef DM_COMPAT # include "libdm-compat.h" #endif -#include -#include -#include -#include #include #include -#include -#include #include #include @@ -53,6 +48,13 @@ #error The version of dm-ioctl.h included is incompatible. #endif +/* FIXME This should be exported in device-mapper.h */ +#define DM_NAME "device-mapper" + +#define PROC_MISC "/proc/misc" +#define PROC_DEVICES "/proc/devices" +#define MISC_NAME "misc" + /* dm major version no for running kernel */ static int _dm_version = DM_VERSION_MAJOR; static int _log_suppress = 0; @@ -114,22 +116,144 @@ static void *_align(void *ptr, unsigned int a) return (void *) (((unsigned long) ptr + agn) & ~agn); } +static int _get_proc_number(const char *file, const char *name, + uint32_t *number) +{ + FILE *fl; + char nm[256]; + int c; + + if (!(fl = fopen(file, "r"))) { + log_error("%s: fopen failed: %s", file, strerror(errno)); + return 0; + } + + while (!feof(fl)) { + if (fscanf(fl, "%d %255s\n", number, &nm[0]) == 2) { + if (!strcmp(name, nm)) { + fclose(fl); + return 1; + } + } + do { + c = fgetc(fl); + } while (c != EOF && c != '\n'); + } + fclose(fl); + + log_error("%s: No entry for %s found", file, name); + return 0; +} + +static int _control_device_number(uint32_t *major, uint32_t *minor) +{ + if (!_get_proc_number(PROC_DEVICES, MISC_NAME, major) || + !_get_proc_number(PROC_MISC, DM_NAME, minor)) { + *major = 0; + return 0; + } + + return 1; +} + +/* + * Returns 1 if exists; 0 if it doesn't; -1 if it's wrong + */ +static int _control_exists(const char *control, uint32_t major, uint32_t minor) +{ + struct stat buf; + + if (stat(control, &buf) < 0) { + if (errno != ENOENT) + log_error("%s: stat failed: %s", control, + strerror(errno)); + return 0; + } + + if (!S_ISCHR(buf.st_mode)) { + log_verbose("%s: Wrong inode type", control); + if (!unlink(control)) + return 0; + log_error("%s: unlink failed: %s", control, + strerror(errno)); + return -1; + } + + if (major && buf.st_rdev != MKDEV(major, minor)) { + log_verbose("%s: Wrong device number: (%u, %u) instead of " + "(%u, %u)", control, + MAJOR(buf.st_mode), MINOR(buf.st_mode), + major, minor); + if (!unlink(control)) + return 0; + log_error("%s: unlink failed: %s", control, + strerror(errno)); + return -1; + } + + return 1; +} + +static int _create_control(const char *control, uint32_t major, uint32_t minor) +{ + int ret; + mode_t old_umask; + + if (!major) + return 0; + + old_umask = umask(0022); + ret = create_dir(dm_dir()); + umask(old_umask); + + if (!ret) + return 0; + + log_verbose("Creating device %s (%u, %u)", control, major, minor); + + if (mknod(control, S_IFCHR | S_IRUSR | S_IWUSR, + MKDEV(major, minor)) < 0) { + log_error("%s: mknod failed: %s", control, strerror(errno)); + return 0; + } + +#ifdef HAVE_SELINUX + if (!set_selinux_context(control)) { + stack; + return 0; + } +#endif + + return 1; +} + static int _open_control(void) { char control[PATH_MAX]; + uint32_t major = 0, minor; if (_control_fd != -1) return 1; snprintf(control, sizeof(control), "%s/control", dm_dir()); + if (!_control_device_number(&major, &minor)) + log_error("Is device-mapper driver missing from kernel?"); + + if (!_control_exists(control, major, minor) && + !_create_control(control, major, minor)) + goto error; + if ((_control_fd = open(control, O_RDWR)) < 0) { log_error("%s: open failed: %s", control, strerror(errno)); - log_error("Is device-mapper driver missing from kernel?"); - return 0; + goto error; } return 1; + +error: + log_error("Failure to communicate with kernel device-mapper driver."); + return 0; } void dm_task_destroy(struct dm_task *dmt) diff --git a/libdm/libdm-common.c b/libdm/libdm-common.c index 2e9b05f27..44f4dfe8c 100644 --- a/libdm/libdm-common.c +++ b/libdm/libdm-common.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. * * This file is part of the device-mapper userspace tools. * @@ -13,20 +13,14 @@ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "lib.h" #include "libdm-targets.h" #include "libdm-common.h" #include "list.h" -#include "log.h" #include "kdev_t.h" -#include -#include #include -#include #include -#include -#include -#include #include #ifdef HAVE_SELINUX @@ -100,14 +94,14 @@ struct dm_task *dm_task_create(int type) { struct dm_task *dmt = malloc(sizeof(*dmt)); - if (!dm_check_version()) - return NULL; - if (!dmt) { log_error("dm_task_create: malloc(%d) failed", sizeof(*dmt)); return NULL; } + if (!dm_check_version()) + return NULL; + memset(dmt, 0, sizeof(*dmt)); dmt->type = type; @@ -205,7 +199,7 @@ int dm_task_add_target(struct dm_task *dmt, uint64_t start, uint64_t size, } #ifdef HAVE_SELINUX -static int _set_selinux_context(const char *path) +int set_selinux_context(const char *path) { security_context_t scontext; @@ -270,7 +264,7 @@ static int _add_dev_node(const char *dev_name, uint32_t major, uint32_t minor, } #ifdef HAVE_SELINUX - if (!_set_selinux_context(path)) + if (!set_selinux_context(path)) return 0; #endif diff --git a/libdm/libdm-common.h.in b/libdm/libdm-common.h.in index c2a97e962..5129e4576 100644 --- a/libdm/libdm-common.h.in +++ b/libdm/libdm-common.h.in @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. * * This file is part of the device-mapper userspace tools. * @@ -28,6 +28,8 @@ int rm_dev_node(const char *dev_name); int rename_dev_node(const char *old_name, const char *new_name); void update_devs(void); +int set_selinux_context(const char *path); + #define DM_LIB_VERSION @DM_LIB_VERSION@ #endif diff --git a/libdm/libdm-file.c b/libdm/libdm-file.c new file mode 100644 index 000000000..5295b9e9d --- /dev/null +++ b/libdm/libdm-file.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. + * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. + * + * This file is part of the device-mapper userspace tools. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "lib.h" +#include "libdm-file.h" + +#include +#include +#include +#include + +static int _create_dir_recursive(const char *dir) +{ + char *orig, *s; + int rc; + + log_verbose("Creating directory \"%s\"", dir); + /* Create parent directories */ + orig = s = strdup(dir); + while ((s = strchr(s, '/')) != NULL) { + *s = '\0'; + if (*orig) { + rc = mkdir(orig, 0777); + if (rc < 0 && errno != EEXIST) { + log_error("%s: mkdir failed: %s", orig, + strerror(errno)); + free(orig); + return 0; + } + } + *s++ = '/'; + } + free(orig); + + /* Create final directory */ + rc = mkdir(dir, 0777); + if (rc < 0 && errno != EEXIST) { + log_error("%s: mkdir failed: %s", orig, + strerror(errno)); + return 0; + } + return 1; +} + +int create_dir(const char *dir) +{ + struct stat info; + + if (!*dir) + return 1; + + if (stat(dir, &info) < 0) + return _create_dir_recursive(dir); + + if (S_ISDIR(info.st_mode)) + return 1; + + log_error("Directory \"%s\" not found", dir); + return 0; +} + diff --git a/libdm/libdm-file.h b/libdm/libdm-file.h new file mode 100644 index 000000000..b2d48e210 --- /dev/null +++ b/libdm/libdm-file.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. + * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved. + * + * This file is part of the device-mapper userspace tools. + * + * This copyrighted material is made available to anyone wishing to use, + * modify, copy, or redistribute it subject to the terms and conditions + * of the GNU General Public License v.2. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef LIB_DMFILE_H +#define LIB_DMFILE_H + +/* + * Create directory (recursively) if necessary. Return 1 + * if directory was successfully created (or already exists), else 0. + */ +int create_dir(const char *dir); + +#endif