/*
* mdadm - manage Linux "md" devices aka RAID arrays.
*
- * Copyright (C) 2001-2012 Neil Brown <neilb@suse.de>
+ * Copyright (C) 2001-2013 Neil Brown <neilb@suse.de>
*
*
* This program is free software; you can redistribute it and/or modify
#include "mdadm.h"
#include "md_p.h"
+#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <sys/un.h>
#include <sys/resource.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
#include <ctype.h>
#include <dirent.h>
#include <signal.h>
+#include <dlfcn.h>
+
/*
* following taken from linux/blkpg.h because they aren't
aren't permitted). */
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+static struct dlm_hooks *dlm_hooks = NULL;
+static int is_dlm_hooks_ready = 0;
+struct dlm_lock_resource *dlm_lock_res = NULL;
+static int ast_called = 0;
+
+struct dlm_lock_resource {
+ dlm_lshandle_t *ls;
+ struct dlm_lksb lksb;
+};
+
+int dlm_funs_ready(void)
+{
+ return is_dlm_hooks_ready ? 1 : 0;
+}
+
+/* Using poll(2) to wait for and dispatch ASTs */
+static int poll_for_ast(dlm_lshandle_t ls)
+{
+ struct pollfd pfd;
+
+ pfd.fd = dlm_hooks->ls_get_fd(ls);
+ pfd.events = POLLIN;
+
+ while (!ast_called)
+ {
+ if (poll(&pfd, 1, 0) < 0)
+ {
+ perror("poll");
+ return -1;
+ }
+ dlm_hooks->dispatch(dlm_hooks->ls_get_fd(ls));
+ }
+ ast_called = 0;
+
+ return 0;
+}
+
+static void dlm_ast(void *arg)
+{
+ ast_called = 1;
+}
+
+static char *cluster_name = NULL;
+/* Create the lockspace, take bitmapXXX locks on all the bitmaps. */
+int cluster_get_dlmlock(int *lockid)
+{
+ int ret = -1;
+ char str[64];
+ int flags = LKF_NOQUEUE;
+
+ ret = get_cluster_name(&cluster_name);
+ if (ret) {
+ pr_err("The md can't get cluster name\n");
+ return -1;
+ }
+
+ dlm_lock_res = xmalloc(sizeof(struct dlm_lock_resource));
+ dlm_lock_res->ls = dlm_hooks->create_lockspace(cluster_name, O_RDWR);
+ if (!dlm_lock_res->ls) {
+ pr_err("%s failed to create lockspace\n", cluster_name);
+ goto out;
+ }
+
+ /* Conversions need the lockid in the LKSB */
+ if (flags & LKF_CONVERT)
+ dlm_lock_res->lksb.sb_lkid = *lockid;
+
+ snprintf(str, 64, "bitmap%s", cluster_name);
+ /* if flags with LKF_CONVERT causes below return ENOENT which means
+ * "No such file or directory" */
+ ret = dlm_hooks->ls_lock(dlm_lock_res->ls, LKM_PWMODE, &dlm_lock_res->lksb,
+ flags, str, strlen(str), 0, dlm_ast,
+ dlm_lock_res, NULL, NULL);
+ if (ret) {
+ pr_err("error %d when get PW mode on lock %s\n", errno, str);
+ goto out;
+ }
+
+ /* Wait for it to complete */
+ poll_for_ast(dlm_lock_res->ls);
+ *lockid = dlm_lock_res->lksb.sb_lkid;
+
+ errno = dlm_lock_res->lksb.sb_status;
+ if (errno) {
+ pr_err("error %d happened in ast with lock %s\n", errno, str);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+int cluster_release_dlmlock(int lockid)
+{
+ int ret = -1;
+
+ if (!cluster_name)
+ return -1;
+
+ /* if flags with LKF_CONVERT causes below return EINVAL which means
+ * "Invalid argument" */
+ ret = dlm_hooks->ls_unlock(dlm_lock_res->ls, lockid, 0,
+ &dlm_lock_res->lksb, dlm_lock_res);
+ if (ret) {
+ pr_err("error %d happened when unlock\n", errno);
+ /* XXX make sure the lock is unlocked eventually */
+ goto out;
+ }
+
+ /* Wait for it to complete */
+ poll_for_ast(dlm_lock_res->ls);
+
+ errno = dlm_lock_res->lksb.sb_status;
+ if (errno != EUNLOCK) {
+ pr_err("error %d happened in ast when unlock lockspace\n", errno);
+ /* XXX make sure the lockspace is unlocked eventually */
+ goto out;
+ }
+
+ ret = dlm_hooks->release_lockspace(cluster_name, dlm_lock_res->ls, 1);
+ if (ret) {
+ pr_err("error %d happened when release lockspace\n", errno);
+ /* XXX make sure the lockspace is released eventually */
+ goto out;
+ }
+ free(dlm_lock_res);
+
+out:
+ return ret;
+}
+
/*
* Parse a 128 bit uuid in 4 integers
* format is 32 hexx nibbles with options :.<space> separator
}
#endif
+int parse_cluster_confirm_arg(char *input, char **devname, int *slot)
+{
+ char *dev;
+ *slot = strtoul(input, &dev, 10);
+ if (dev == input || dev[0] != ':')
+ return -1;
+ *devname = dev+1;
+ return 0;
+}
+
void remove_partitions(int fd)
{
/* remove partitions from this block devices.
if (ioctl(fd, BLKPG, &a) == 0)
/* Very unlikely, but not a partition */
return 0;
- if (errno == ENXIO)
+ if (errno == ENXIO || errno == ENOTTY)
/* not a partition */
return 0;
case 1:
return avail_disks >= 1;
case 4:
+ if (avail_disks == raid_disks - 1 &&
+ !avail[raid_disks - 1])
+ /* If just the parity device is missing, then we
+ * have enough, even if not clean
+ */
+ return 1;
+ /* FALL THROUGH */
case 5:
if (clean)
return avail_disks >= raid_disks-1;
{
struct mdu_array_info_s array;
struct mdu_disk_info_s disk;
- int avail_disks = 0;
int i, rv;
char *avail;
continue;
if (disk.raid_disk < 0 || disk.raid_disk >= array.raid_disks)
continue;
- avail_disks++;
avail[disk.raid_disk] = 1;
}
/* This is used on an active array, so assume it is clean */
*/
unsigned char sb[1024];
time_t mtime;
- int size, bsize;
+ unsigned long long size;
+ int bsize;
if (lseek(fd, 1024,0)!= 1024)
return 0;
if (read(fd, sb, 1024)!= 1024)
mtime = sb[44]|(sb[45]|(sb[46]|sb[47]<<8)<<8)<<8;
bsize = sb[24]|(sb[25]|(sb[26]|sb[27]<<8)<<8)<<8;
size = sb[4]|(sb[5]|(sb[6]|sb[7]<<8)<<8)<<8;
+ size <<= bsize;
pr_err("%s appears to contain an ext2fs file system\n",
name);
- cont_err("size=%dK mtime=%s",
- size*(1<<bsize), ctime(&mtime));
+ cont_err("size=%lluK mtime=%s", size, ctime(&mtime));
return 1;
}
*
*/
unsigned char sb[1024];
- unsigned long size;
+ unsigned long long size;
if (lseek(fd, 64*1024, 0) != 64*1024)
return 0;
if (read(fd, sb, 1024) != 1024)
return 0;
pr_err("%s appears to contain a reiserfs file system\n",name);
size = sb[0]|(sb[1]|(sb[2]|sb[3]<<8)<<8)<<8;
- cont_err("size = %luK\n", size*4);
+ cont_err("size = %lluK\n", size*4);
return 1;
}
if (bytes < 5000*1024)
buf[0] = 0;
else if (bytes < 2*1024LL*1024LL*1024LL) {
- long cMiB = (bytes / ( (1LL<<20) / 200LL ) +1) /2;
+ long cMiB = (bytes * 200LL / (1LL<<20) + 1) / 2;
long cMB = (bytes / ( 1000000LL / 200LL ) +1) /2;
snprintf(buf, sizeof(buf), " (%ld.%02ld MiB %ld.%02ld MB)",
cMiB/100 , cMiB % 100,
cMB/100, cMB % 100);
} else {
- long cGiB = (bytes / ( (1LL<<30) / 200LL ) +1) /2;
+ long cGiB = (bytes * 200LL / (1LL<<30) +1) / 2;
long cGB = (bytes / (1000000000LL/200LL ) +1) /2;
snprintf(buf, sizeof(buf), " (%ld.%02ld GiB %ld.%02ld GB)",
cGiB/100 , cGiB % 100,
buf[0] = 0;
else if (prefix == IEC) {
if (bytes < 2*1024LL*1024LL*1024LL) {
- long cMiB = (bytes / ( (1LL<<20) / 200LL ) +1) /2;
+ long cMiB = (bytes * 200LL / (1LL<<20) +1) /2;
snprintf(buf, sizeof(buf), "%ld.%02ldMiB",
cMiB/100 , cMiB % 100);
} else {
- long cGiB = (bytes / ( (1LL<<30) / 200LL ) +1) /2;
+ long cGiB = (bytes * 200LL / (1LL<<30) +1) /2;
snprintf(buf, sizeof(buf), "%ld.%02ldGiB",
cGiB/100 , cGiB % 100);
}
}
#endif /* !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO) */
+int get_maj_min(char *dev, int *major, int *minor)
+{
+ char *e;
+ *major = strtoul(dev, &e, 0);
+ return (e > dev && *e == ':' && e[1] &&
+ (*minor = strtoul(e+1, &e, 0)) >= 0 &&
+ *e == 0);
+}
+
int dev_open(char *dev, int flags)
{
/* like 'open', but if 'dev' matches %d:%d, create a temp
* block device and open that
*/
- char *e;
int fd = -1;
char devname[32];
int major;
if (!dev) return -1;
flags |= O_DIRECT;
- major = strtoul(dev, &e, 0);
- if (e > dev && *e == ':' && e[1] &&
- (minor = strtoul(e+1, &e, 0)) >= 0 &&
- *e == 0) {
+ if (get_maj_min(dev, &major, &minor)) {
snprintf(devname, sizeof(devname), "/dev/.tmp.md.%d:%d:%d",
(int)getpid(), major, minor);
if (mknod(devname, S_IFBLK|0600, makedev(major, minor)) == 0) {
delay *= 2;
}
if (i == 25)
- dprintf("%s: timeout waiting for %s\n", __func__, dev);
+ dprintf("timeout waiting for %s\n", dev);
}
struct superswitch *superlist[] =
char pathbuf[1024];
char *paths[4] = {
pathbuf,
- "/sbin/mdmon",
- "mdmon",
+ BINDIR "/mdmon",
+ "./mdmon",
NULL
};
pathbuf[0] = '\0';
/* First try to run systemctl */
- switch(fork()) {
- case 0:
- /* FIXME yuk. CLOSE_EXEC?? */
- skipped = 0;
- for (i = 3; skipped < 20; i++)
- if (close(i) < 0)
- skipped++;
- else
- skipped = 0;
-
- /* Don't want to see error messages from systemctl.
- * If the service doesn't exist, we start mdmon ourselves.
- */
- close(2);
- open("/dev/null", O_WRONLY);
- snprintf(pathbuf, sizeof(pathbuf), "mdmon@%s.service",
- devnm);
- status = execl("/usr/bin/systemctl", "systemctl", "start",
- pathbuf, NULL);
- status = execl("/bin/systemctl", "systemctl", "start",
- pathbuf, NULL);
- exit(1);
- case -1: pr_err("cannot run mdmon. "
- "Array remains readonly\n");
- return -1;
- default: /* parent - good */
- pid = wait(&status);
- if (pid >= 0 && status == 0)
- return 0;
- }
+ if (!check_env("MDADM_NO_SYSTEMCTL"))
+ switch(fork()) {
+ case 0:
+ /* FIXME yuk. CLOSE_EXEC?? */
+ skipped = 0;
+ for (i = 3; skipped < 20; i++)
+ if (close(i) < 0)
+ skipped++;
+ else
+ skipped = 0;
+
+ /* Don't want to see error messages from
+ * systemctl. If the service doesn't exist,
+ * we start mdmon ourselves.
+ */
+ close(2);
+ open("/dev/null", O_WRONLY);
+ snprintf(pathbuf, sizeof(pathbuf), "mdmon@%s.service",
+ devnm);
+ status = execl("/usr/bin/systemctl", "systemctl",
+ "start",
+ pathbuf, NULL);
+ status = execl("/bin/systemctl", "systemctl", "start",
+ pathbuf, NULL);
+ exit(1);
+ case -1: pr_err("cannot run mdmon. Array remains readonly\n");
+ return -1;
+ default: /* parent - good */
+ pid = wait(&status);
+ if (pid >= 0 && status == 0)
+ return 0;
+ }
/* That failed, try running mdmon directly */
switch(fork()) {
for (i = 0; paths[i]; i++)
if (paths[i][0]) {
- execl(paths[i], "mdmon",
+ execl(paths[i], paths[i],
devnm, NULL);
}
exit(1);
- case -1: pr_err("cannot run mdmon. "
- "Array remains readonly\n");
+ case -1: pr_err("cannot run mdmon. Array remains readonly\n");
return -1;
default: /* parent - good */
pid = wait(&status);
if (pid < 0 || status != 0) {
- pr_err("failed to launch mdmon. "
- "Array remains readonly\n");
+ pr_err("failed to launch mdmon. Array remains readonly\n");
return -1;
}
}
if (check_env("MDADM_EXPERIMENTAL"))
return 1;
else {
- pr_err("To use this feature MDADM_EXPERIMENTAL"
- " environment variable has to be defined.\n");
+ pr_err("To use this feature MDADM_EXPERIMENTAL environment variable has to be defined.\n");
return 0;
}
}
lim.rlim_cur = fds;
setrlimit(RLIMIT_NOFILE, &lim);
}
+
+int in_initrd(void)
+{
+ /* This is based on similar function in systemd. */
+ struct statfs s;
+ /* statfs.f_type is signed long on s390x and MIPS, causing all
+ sorts of sign extension problems with RAMFS_MAGIC being
+ defined as 0x858458f6 */
+ return statfs("/", &s) >= 0 &&
+ ((unsigned long)s.f_type == TMPFS_MAGIC ||
+ ((unsigned long)s.f_type & 0xFFFFFFFFUL) ==
+ ((unsigned long)RAMFS_MAGIC & 0xFFFFFFFFUL));
+}
+
+void reopen_mddev(int mdfd)
+{
+ /* Re-open without any O_EXCL, but keep
+ * the same fd
+ */
+ char *devnm;
+ int fd;
+ devnm = fd2devnm(mdfd);
+ close(mdfd);
+ fd = open_dev(devnm);
+ if (fd >= 0 && fd != mdfd)
+ dup2(fd, mdfd);
+}
+
+static struct cmap_hooks *cmap_hooks = NULL;
+static int is_cmap_hooks_ready = 0;
+
+#ifndef MDASSEMBLE
+void set_cmap_hooks(void)
+{
+ cmap_hooks = xmalloc(sizeof(struct cmap_hooks));
+ cmap_hooks->cmap_handle = dlopen("libcmap.so.4", RTLD_NOW | RTLD_LOCAL);
+ if (!cmap_hooks->cmap_handle)
+ return;
+
+ cmap_hooks->initialize = dlsym(cmap_hooks->cmap_handle, "cmap_initialize");
+ cmap_hooks->get_string = dlsym(cmap_hooks->cmap_handle, "cmap_get_string");
+ cmap_hooks->finalize = dlsym(cmap_hooks->cmap_handle, "cmap_finalize");
+
+ if (!cmap_hooks->initialize || !cmap_hooks->get_string ||
+ !cmap_hooks->finalize)
+ dlclose(cmap_hooks->cmap_handle);
+ else
+ is_cmap_hooks_ready = 1;
+}
+
+int get_cluster_name(char **cluster_name)
+{
+ int rv = -1;
+ cmap_handle_t handle;
+
+ if (!is_cmap_hooks_ready)
+ return rv;
+
+ rv = cmap_hooks->initialize(&handle);
+ if (rv != CS_OK)
+ goto out;
+
+ rv = cmap_hooks->get_string(handle, "totem.cluster_name", cluster_name);
+ if (rv != CS_OK) {
+ free(*cluster_name);
+ rv = -1;
+ goto name_err;
+ }
+
+ rv = 0;
+name_err:
+ cmap_hooks->finalize(handle);
+out:
+ return rv;
+}
+
+void set_dlm_hooks(void)
+{
+ dlm_hooks = xmalloc(sizeof(struct dlm_hooks));
+ dlm_hooks->dlm_handle = dlopen("libdlm_lt.so.3", RTLD_NOW | RTLD_LOCAL);
+ if (!dlm_hooks->dlm_handle)
+ return;
+
+ dlm_hooks->create_lockspace = dlsym(dlm_hooks->dlm_handle, "dlm_create_lockspace");
+ dlm_hooks->release_lockspace = dlsym(dlm_hooks->dlm_handle, "dlm_release_lockspace");
+ dlm_hooks->ls_lock = dlsym(dlm_hooks->dlm_handle, "dlm_ls_lock");
+ dlm_hooks->ls_unlock = dlsym(dlm_hooks->dlm_handle, "dlm_ls_unlock");
+ dlm_hooks->ls_get_fd = dlsym(dlm_hooks->dlm_handle, "dlm_ls_get_fd");
+ dlm_hooks->dispatch = dlsym(dlm_hooks->dlm_handle, "dlm_dispatch");
+
+ if (!dlm_hooks->create_lockspace || !dlm_hooks->ls_lock ||
+ !dlm_hooks->ls_unlock || !dlm_hooks->release_lockspace ||
+ !dlm_hooks->ls_get_fd || !dlm_hooks->dispatch)
+ dlclose(dlm_hooks->dlm_handle);
+ else
+ is_dlm_hooks_ready = 1;
+}
+
+void set_hooks(void)
+{
+ set_dlm_hooks();
+ set_cmap_hooks();
+}
+#endif