#include <sys/utsname.h>
#include <sys/wait.h>
#include <sys/un.h>
+#include <sys/resource.h>
#include <ctype.h>
#include <dirent.h>
#include <signal.h>
-int __offroot;
-
/*
* following taken from linux/blkpg.h because they aren't
* anywhere else and it isn't safe to #include linux/ * stuff.
/* The argument structure */
struct blkpg_ioctl_arg {
- int op;
- int flags;
- int datalen;
- void *data;
+ int op;
+ int flags;
+ int datalen;
+ void *data;
};
/* The subfunctions (for the op field) */
int md_get_version(int fd)
{
- struct stat stb;
- mdu_version_t vers;
+ struct stat stb;
+ mdu_version_t vers;
- if (fstat(fd, &stb)<0)
- return -1;
- if ((S_IFMT&stb.st_mode) != S_IFBLK)
- return -1;
+ if (fstat(fd, &stb)<0)
+ return -1;
+ if ((S_IFMT&stb.st_mode) != S_IFBLK)
+ return -1;
- if (ioctl(fd, RAID_VERSION, &vers) == 0)
- return (vers.major*10000) + (vers.minor*100) + vers.patchlevel;
- if (errno == EACCES)
- return -1;
- if (major(stb.st_rdev) == MD_MAJOR)
- return (3600);
- return -1;
+ if (ioctl(fd, RAID_VERSION, &vers) == 0)
+ return (vers.major*10000) + (vers.minor*100) + vers.patchlevel;
+ if (errno == EACCES)
+ return -1;
+ if (major(stb.st_rdev) == MD_MAJOR)
+ return (3600);
+ return -1;
}
int get_linux_version()
* followed by 'K', 'M', or 'G'.
* Without a suffix, K is assumed.
* Number returned is in sectors (half-K)
- * 0 returned on error.
+ * INVALID_SECTORS returned on error.
*/
char *c;
long long s = strtoll(size, &c, 10);
c++;
s *= 1024 * 1024 * 2;
break;
+ case 's': /* sectors */
+ c++;
+ break;
}
} else
- s = 0;
+ s = INVALID_SECTORS;
if (*c)
- s = 0;
+ s = INVALID_SECTORS;
return s;
}
/* there must be one of the 'copies' form 'first' */
int n = copies;
int cnt = 0;
+ int this = first;
while (n--) {
- if (avail[first])
+ if (avail[this])
cnt++;
- first = (first+1) % raid_disks;
+ this = (this+1) % raid_disks;
}
if (cnt == 0)
return 0;
-
+ first = (first+(layout&255)) % raid_disks;
} while (first != 0);
return 1;
size = sb[4]|(sb[5]|(sb[6]|sb[7]<<8)<<8)<<8;
pr_err("%s appears to contain an ext2fs file system\n",
name);
- fprintf(stderr," size=%dK mtime=%s",
+ cont_err("size=%dK mtime=%s",
size*(1<<bsize), ctime(&mtime));
return 1;
}
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;
- fprintf(stderr, " size = %luK\n", size*4);
+ cont_err("size = %luK\n", size*4);
return 1;
}
char *level;
struct supertype *st = guess_super(fd);
- if (!st) return 0;
- st->ignore_hw_compat = 1;
+ if (!st)
+ return 0;
st->ss->load_super(st, fd, name);
/* Looks like a raid array .. */
pr_err("%s appears to be part of a raid array:\n",
crtime = info.array.ctime;
level = map_num(pers, info.array.level);
if (!level) level = "-unknown-";
- fprintf(stderr, " level=%s devices=%d ctime=%s",
- level, info.array.raid_disks, ctime(&crtime));
+ cont_err("level=%s devices=%d ctime=%s",
+ level, info.array.raid_disks, ctime(&crtime));
return 1;
}
return buf;
}
-char *human_size_brief(long long bytes)
+char *human_size_brief(long long bytes, int prefix)
{
static char buf[30];
* gigabytes, as that shows more precision and isn't
* too large a number.
* Terabytes are not yet handled.
+ *
+ * If prefix == IEC, we mean prefixes like kibi,mebi,gibi etc.
+ * If prefix == JEDEC, we mean prefixes like kilo,mega,giga etc.
*/
if (bytes < 5000*1024)
buf[0] = 0;
- else if (bytes < 2*1024LL*1024LL*1024LL) {
- long cMiB = (bytes / ( (1LL<<20) / 200LL ) +1) /2;
- snprintf(buf, sizeof(buf), " (%ld.%02ldMiB)",
- cMiB/100 , cMiB % 100);
- } else {
- long cGiB = (bytes / ( (1LL<<30) / 200LL ) +1) /2;
- snprintf(buf, sizeof(buf), " (%ld.%02ldGiB)",
- cGiB/100 , cGiB % 100);
+ else if (prefix == IEC) {
+ if (bytes < 2*1024LL*1024LL*1024LL) {
+ long cMiB = (bytes / ( (1LL<<20) / 200LL ) +1) /2;
+ snprintf(buf, sizeof(buf), "%ld.%02ldMiB",
+ cMiB/100 , cMiB % 100);
+ } else {
+ long cGiB = (bytes / ( (1LL<<30) / 200LL ) +1) /2;
+ snprintf(buf, sizeof(buf), "%ld.%02ldGiB",
+ cGiB/100 , cGiB % 100);
+ }
}
+ else if (prefix == JEDEC) {
+ if (bytes < 2*1024LL*1024LL*1024LL) {
+ long cMB = (bytes / ( 1000000LL / 200LL ) +1) /2;
+ snprintf(buf, sizeof(buf), "%ld.%02ldMB",
+ cMB/100, cMB % 100);
+ } else {
+ long cGB = (bytes / (1000000000LL/200LL ) +1) /2;
+ snprintf(buf, sizeof(buf), "%ld.%02ldGB",
+ cGB/100 , cGB % 100);
+ }
+ }
+ else
+ buf[0] = 0;
return buf;
}
return data_disks;
}
+int devnm2devid(char *devnm)
+{
+ /* First look in /sys/block/$DEVNM/dev for %d:%d
+ * If that fails, try parsing out a number
+ */
+ char path[100];
+ char *ep;
+ int fd;
+ int mjr,mnr;
+
+ sprintf(path, "/sys/block/%s/dev", devnm);
+ fd = open(path, O_RDONLY);
+ if (fd >= 0) {
+ char buf[20];
+ int n = read(fd, buf, sizeof(buf));
+ close(fd);
+ if (n > 0)
+ buf[n] = 0;
+ if (n > 0 && sscanf(buf, "%d:%d\n", &mjr, &mnr) == 2)
+ return makedev(mjr, mnr);
+ }
+ if (strncmp(devnm, "md_d", 4) == 0 &&
+ isdigit(devnm[4]) &&
+ (mnr = strtoul(devnm+4, &ep, 10)) >= 0 &&
+ ep > devnm && *ep == 0)
+ return makedev(get_mdp_major(), mnr << MdpMinorShift);
+
+ if (strncmp(devnm, "md", 2) == 0 &&
+ isdigit(devnm[2]) &&
+ (mnr = strtoul(devnm+2, &ep, 10)) >= 0 &&
+ ep > devnm && *ep == 0)
+ return makedev(MD_MAJOR, mnr);
+
+ return 0;
+}
+
#if !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO)
-char *get_md_name(int dev)
+char *get_md_name(char *devnm)
{
/* find /dev/md%d or /dev/md/%d or make a device /dev/.tmp.md%d */
/* if dev < 0, want /dev/md/d%d or find mdp in /proc/devices ... */
+
static char devname[50];
struct stat stb;
- dev_t rdev;
+ dev_t rdev = devnm2devid(devnm);
char *dn;
- if (dev < 0) {
- int mdp = get_mdp_major();
- if (mdp < 0) return NULL;
- rdev = makedev(mdp, (-1-dev)<<6);
- snprintf(devname, sizeof(devname), "/dev/md/d%d", -1-dev);
- if (stat(devname, &stb) == 0
- && (S_IFMT&stb.st_mode) == S_IFBLK
- && (stb.st_rdev == rdev))
- return devname;
- } else {
- rdev = makedev(MD_MAJOR, dev);
- snprintf(devname, sizeof(devname), "/dev/md%d", dev);
- if (stat(devname, &stb) == 0
- && (S_IFMT&stb.st_mode) == S_IFBLK
- && (stb.st_rdev == rdev))
- return devname;
-
- snprintf(devname, sizeof(devname), "/dev/md/%d", dev);
+ if (rdev == 0)
+ return 0;
+ if (strncmp(devnm, "md_", 3) == 0) {
+ snprintf(devname, sizeof(devname), "/dev/md/%s",
+ devnm + 3);
if (stat(devname, &stb) == 0
&& (S_IFMT&stb.st_mode) == S_IFBLK
&& (stb.st_rdev == rdev))
return devname;
}
+ snprintf(devname, sizeof(devname), "/dev/%s", devnm);
+ if (stat(devname, &stb) == 0
+ && (S_IFMT&stb.st_mode) == S_IFBLK
+ && (stb.st_rdev == rdev))
+ return devname;
+
+ snprintf(devname, sizeof(devname), "/dev/md/%s", devnm+2);
+ if (stat(devname, &stb) == 0
+ && (S_IFMT&stb.st_mode) == S_IFBLK
+ && (stb.st_rdev == rdev))
+ return devname;
+
dn = map_dev(major(rdev), minor(rdev), 0);
if (dn)
return dn;
- snprintf(devname, sizeof(devname), "/dev/.tmp.md%d", dev);
+ snprintf(devname, sizeof(devname), "/dev/.tmp.%s", devnm);
if (mknod(devname, S_IFBLK | 0600, rdev) == -1)
if (errno != EEXIST)
return NULL;
if (strncmp(name, "/dev/.tmp.md", 12) == 0)
unlink(name);
}
-
-int find_free_devnum(int use_partitions)
-{
- int devnum;
- for (devnum = 127; devnum != 128;
- devnum = devnum ? devnum-1 : (1<<20)-1) {
- char *dn;
- int _devnum;
- char nbuf[50];
-
- _devnum = use_partitions ? (-1-devnum) : devnum;
- if (mddev_busy(_devnum))
- continue;
- sprintf(nbuf, "%s%d", use_partitions?"mdp":"md", devnum);
- if (!conf_name_is_free(nbuf))
- continue;
- /* make sure it is new to /dev too, at least as a
- * non-standard */
- dn = map_dev(dev2major(_devnum), dev2minor(_devnum), 0);
- if (dn && ! is_standard(dn, NULL))
- continue;
- break;
- }
- if (devnum == 128)
- return NoMdDev;
- return use_partitions ? (-1-devnum) : devnum;
-}
#endif /* !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO) */
int dev_open(char *dev, int flags)
if (e > dev && *e == ':' && e[1] &&
(minor = strtoul(e+1, &e, 0)) >= 0 &&
*e == 0) {
- char *path = map_dev(major, minor, 0);
- if (path)
- fd = open(path, flags);
- if (fd < 0) {
- snprintf(devname, sizeof(devname), "/dev/.tmp.md.%d:%d:%d",
- (int)getpid(), major, minor);
- if (mknod(devname, S_IFBLK|0600, makedev(major, minor)) == 0) {
- fd = open(devname, flags);
- unlink(devname);
- }
+ snprintf(devname, sizeof(devname), "/dev/.tmp.md.%d:%d:%d",
+ (int)getpid(), major, minor);
+ if (mknod(devname, S_IFBLK|0600, makedev(major, minor)) == 0) {
+ fd = open(devname, flags);
+ unlink(devname);
}
if (fd < 0) {
+ /* Try /tmp as /dev appear to be read-only */
snprintf(devname, sizeof(devname), "/tmp/.tmp.md.%d:%d:%d",
(int)getpid(), major, minor);
if (mknod(devname, S_IFBLK|0600, makedev(major, minor)) == 0) {
return fd;
}
-int open_dev_flags(int devnum, int flags)
+int open_dev_flags(char *devnm, int flags)
{
+ int devid;
char buf[20];
- sprintf(buf, "%d:%d", dev2major(devnum), dev2minor(devnum));
+ devid = devnm2devid(devnm);
+ sprintf(buf, "%d:%d", major(devid), minor(devid));
return dev_open(buf, flags);
}
-int open_dev(int devnum)
+int open_dev(char *devnm)
{
- return open_dev_flags(devnum, O_RDONLY);
+ return open_dev_flags(devnm, O_RDONLY);
}
-int open_dev_excl(int devnum)
+int open_dev_excl(char *devnm)
{
char buf[20];
int i;
int flags = O_RDWR;
+ int devid = devnm2devid(devnm);
+ long delay = 1000;
- sprintf(buf, "%d:%d", dev2major(devnum), dev2minor(devnum));
+ sprintf(buf, "%d:%d", major(devid), minor(devid));
for (i = 0 ; i < 25 ; i++) {
int fd = dev_open(buf, flags|O_EXCL);
if (fd >= 0)
}
if (errno != EBUSY)
return fd;
- usleep(200000);
+ usleep(delay);
+ if (delay < 200000)
+ delay *= 2;
}
return -1;
}
{
int i;
struct stat stb_want;
+ long delay = 1000;
if (fstat(fd, &stb_want) != 0 ||
(stb_want.st_mode & S_IFMT) != S_IFBLK)
(stb.st_mode & S_IFMT) == S_IFBLK &&
(stb.st_rdev == stb_want.st_rdev))
return;
- usleep(200000);
+ usleep(delay);
+ if (delay < 200000)
+ delay *= 2;
}
if (i == 25)
dprintf("%s: timeout waiting for %s\n", __func__, dev);
char version[20];
int i;
char *subarray = NULL;
- int container = NoMdDev;
+ char container[32] = "";
- sra = sysfs_read(fd, 0, GET_VERSION);
+ sra = sysfs_read(fd, NULL, GET_VERSION);
if (sra) {
vers = sra->array.major_version;
*subarray++ = '\0';
subarray = xstrdup(subarray);
}
- container = devname2devnum(dev);
+ strcpy(container, dev);
if (sra)
sysfs_free(sra);
sra = sysfs_read(-1, container, GET_VERSION);
st->sb = NULL;
if (subarrayp)
*subarrayp = subarray;
- st->container_dev = container;
- st->devnum = fd2devnum(fd);
+ strcpy(st->container_devnm, container);
+ strcpy(st->devnm, fd2devnm(fd));
} else
free(subarray);
st->ss = orig->ss;
st->max_devs = orig->max_devs;
st->minor_version = orig->minor_version;
+ st->ignore_hw_compat = orig->ignore_hw_compat;
st->sb = NULL;
st->info = NULL;
return st;
int i;
st = xcalloc(1, sizeof(*st));
- st->container_dev = NoMdDev;
+ st->container_devnm[0] = 0;
for (i = 0 ; superlist[i]; i++) {
int rv;
rv = superlist[bestsuper]->load_super(st, fd, NULL);
if (rv == 0) {
superlist[bestsuper]->free_super(st);
- st->ignore_hw_compat = 0;
return st;
}
}
return 0;
}
-void get_one_disk(int mdfd, mdu_array_info_t *ainf, mdu_disk_info_t *disk)
-{
- int d;
-
- ioctl(mdfd, GET_ARRAY_INFO, ainf);
- for (d = 0 ; d < MAX_DISKS ; d++) {
- if (ioctl(mdfd, GET_DISK_INFO, disk) == 0 &&
- (disk->major || disk->minor))
- return;
- }
-}
-
int open_container(int fd)
{
/* 'fd' is a block device. Find out if it is in use
continue;
if (de->d_name[0] == '.')
continue;
+ /* Need to make sure it is a container and not a volume */
+ sprintf(e, "/%s/md/metadata_version", de->d_name);
+ dfd = open(path, O_RDONLY);
+ if (dfd < 0)
+ continue;
+ n = read(dfd, buf, sizeof(buf));
+ close(dfd);
+ if (n <= 0 || (unsigned)n >= sizeof(buf))
+ continue;
+ buf[n] = 0;
+ if (strncmp(buf, "external", 8) != 0 ||
+ n < 10 ||
+ buf[9] == '/')
+ continue;
sprintf(e, "/%s/dev", de->d_name);
dfd = open(path, O_RDONLY);
if (dfd < 0)
return NULL;
}
+int metadata_container_matches(char *metadata, char *devnm)
+{
+ /* Check if 'devnm' is the container named in 'metadata'
+ * which is
+ * /containername/componentname or
+ * -containername/componentname
+ */
+ int l;
+ if (*metadata != '/' && *metadata != '-')
+ return 0;
+ l = strlen(devnm);
+ if (strncmp(metadata+1, devnm, l) != 0)
+ return 0;
+ if (metadata[l+1] != '/')
+ return 0;
+ return 1;
+}
+
+int metadata_subdev_matches(char *metadata, char *devnm)
+{
+ /* Check if 'devnm' is the subdev named in 'metadata'
+ * which is
+ * /containername/subdev or
+ * -containername/subdev
+ */
+ char *sl;
+ if (*metadata != '/' && *metadata != '-')
+ return 0;
+ sl = strchr(metadata+1, '/');
+ if (!sl)
+ return 0;
+ if (strcmp(sl+1, devnm) == 0)
+ return 1;
+ return 0;
+}
+
int is_container_member(struct mdstat_ent *mdstat, char *container)
{
if (mdstat->metadata_version == NULL ||
strncmp(mdstat->metadata_version, "external:", 9) != 0 ||
- !is_subarray(mdstat->metadata_version+9) ||
- strncmp(mdstat->metadata_version+10, container, strlen(container)) != 0 ||
- mdstat->metadata_version[10+strlen(container)] != '/')
+ !metadata_container_matches(mdstat->metadata_version+9, container))
return 0;
return 1;
struct mdinfo *mdi;
struct mdinfo *info;
int fd, err = 1;
+ char *_devnm;
fd = open(dev, O_RDWR|O_EXCL);
if (fd < 0) {
return -1;
}
- st->devnum = fd2devnum(fd);
- if (st->devnum == NoMdDev) {
+ _devnm = fd2devnm(fd);
+ if (_devnm == NULL) {
if (!quiet)
pr_err("Failed to determine device number for %s\n",
dev);
goto close_fd;
}
+ strcpy(st->devnm, _devnm);
- mdi = sysfs_read(fd, st->devnum, GET_VERSION|GET_LEVEL);
+ mdi = sysfs_read(fd, st->devnm, GET_VERSION|GET_LEVEL);
if (!mdi) {
if (!quiet)
pr_err("Failed to read sysfs for %s\n",
goto free_sysfs;
}
- st->devname = devnum2devname(st->devnum);
- if (!st->devname) {
+ if (st->devnm[0] == 0) {
if (!quiet)
pr_err("Failed to allocate device name\n");
goto free_sysfs;
if (!st->ss->load_container) {
if (!quiet)
pr_err("%s is not a container\n", dev);
- goto free_name;
+ goto free_sysfs;
}
if (st->ss->load_container(st, fd, NULL)) {
if (!quiet)
pr_err("Failed to load metadata for %s\n",
dev);
- goto free_name;
+ goto free_sysfs;
}
info = st->ss->container_content(st, subarray);
free_super:
if (err)
st->ss->free_super(st);
- free_name:
- if (err)
- free(st->devname);
free_sysfs:
sysfs_free(mdi);
close_fd:
return recovery_start;
}
-int mdmon_pid(int devnum)
+int mdmon_pid(char *devnm)
{
char path[100];
char pid[10];
int fd;
int n;
- char *devname = devnum2devname(devnum);
- sprintf(path, "%s/%s.pid", MDMON_DIR, devname);
- free(devname);
+ sprintf(path, "%s/%s.pid", MDMON_DIR, devnm);
fd = open(path, O_RDONLY | O_NOATIME, 0);
return atoi(pid);
}
-int mdmon_running(int devnum)
+int mdmon_running(char *devnm)
{
- int pid = mdmon_pid(devnum);
+ int pid = mdmon_pid(devnm);
if (pid <= 0)
return 0;
if (kill(pid, 0) == 0)
return 0;
}
-int start_mdmon(int devnum)
+int start_mdmon(char *devnm)
{
int i, skipped;
int len;
} else
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;
+ }
+
+ /* That failed, try running mdmon directly */
switch(fork()) {
case 0:
/* FIXME yuk. CLOSE_EXEC?? */
for (i = 0; paths[i]; i++)
if (paths[i][0]) {
- if (__offroot) {
- execl(paths[i], "mdmon", "--offroot",
- devnum2devname(devnum),
- NULL);
- } else {
- execl(paths[i], "mdmon",
- devnum2devname(devnum),
- NULL);
- }
+ execl(paths[i], "mdmon",
+ devnm, NULL);
}
exit(1);
case -1: pr_err("cannot run mdmon. "
return -1;
default: /* parent - good */
pid = wait(&status);
- if (pid < 0 || status != 0)
+ if (pid < 0 || status != 0) {
+ pr_err("failed to launch mdmon. "
+ "Array remains readonly\n");
return -1;
+ }
}
return 0;
}
-int check_env(char *name)
-{
- char *val = getenv(name);
-
- if (val && atoi(val) == 1)
- return 1;
-
- return 0;
-}
-
__u32 random32(void)
{
__u32 rv;
return -1;
}
- sfd = connect_monitor(devnum2devname(st->container_dev));
+ sfd = connect_monitor(st->container_devnm);
if (sfd < 0)
return -1;
found = 1;
/* check if domain matches */
if (found && domlist) {
- struct dev_policy *pol = devnum_policy(dev);
+ struct dev_policy *pol = devid_policy(dev);
if (spare_group)
pol_add(&pol, pol_domain,
spare_group, NULL);
}
return disks;
}
+
+/* Checks if paths point to the same device
+ * Returns 0 if they do.
+ * Returns 1 if they don't.
+ * Returns -1 if something went wrong,
+ * e.g. paths are empty or the files
+ * they point to don't exist */
+int compare_paths (char* path1, char* path2)
+{
+ struct stat st1,st2;
+
+ if (path1 == NULL || path2 == NULL)
+ return -1;
+ if (stat(path1,&st1) != 0)
+ return -1;
+ if (stat(path2,&st2) != 0)
+ return -1;
+ if ((st1.st_ino == st2.st_ino) && (st1.st_dev == st2.st_dev))
+ return 0;
+ return 1;
+}
+
+/* Make sure we can open as many devices as needed */
+void enable_fds(int devices)
+{
+ unsigned int fds = 20 + devices;
+ struct rlimit lim;
+ if (getrlimit(RLIMIT_NOFILE, &lim) != 0
+ || lim.rlim_cur >= fds)
+ return;
+ if (lim.rlim_max < fds)
+ lim.rlim_max = fds;
+ lim.rlim_cur = fds;
+ setrlimit(RLIMIT_NOFILE, &lim);
+}