/*
* mdadm - manage Linux "md" devices aka RAID arrays.
*
- * Copyright (C) 2001-2006 Neil Brown <neilb@suse.de>
+ * Copyright (C) 2001-2009 Neil Brown <neilb@suse.de>
*
*
* This program is free software; you can redistribute it and/or modify
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Neil Brown
- * Email: <neilb@cse.unsw.edu.au>
- * Paper: Neil Brown
- * School of Computer Science and Engineering
- * The University of New South Wales
- * Sydney, 2052
- * Australia
+ * Email: <neilb@suse.de>
*/
#include "mdadm.h"
#include "md_p.h"
#include <sys/socket.h>
#include <sys/utsname.h>
+#include <sys/wait.h>
#include <sys/un.h>
#include <ctype.h>
#include <dirent.h>
}
}
+const int uuid_match_any[4] = { ~0, ~0, ~0, ~0 };
int same_uuid(int a[4], int b[4], int swapuuid)
{
+ if (memcmp(a, uuid_match_any, sizeof(int[4])) == 0 ||
+ memcmp(b, uuid_match_any, sizeof(int[4])) == 0)
+ return 1;
+
if (swapuuid) {
/* parse uuids are hostendian.
* uuid's from some superblocks are big-ending
memcpy(a, b, 16);
}
+char *fname_from_uuid(struct supertype *st, struct mdinfo *info, char *buf, char sep)
+{
+ int i, j;
+ int id;
+ char uuid[16];
+ char *c = buf;
+ strcpy(c, "UUID-");
+ c += strlen(c);
+ copy_uuid(uuid, info->uuid, st->ss->swapuuid);
+ for (i = 0; i < 4; i++) {
+ id = uuid[i];
+ if (i)
+ *c++ = sep;
+ for (j = 3; j >= 0; j--) {
+ sprintf(c,"%02x", (unsigned char) uuid[j+4*i]);
+ c+= 2;
+ }
+ }
+ return buf;
+}
+
#ifndef MDASSEMBLE
int check_ext2(int fd, char *name)
{
if (strncmp(d, "/d",2)==0)
d += 2, type=1; /* /dev/md/dN{pM} */
else if (strncmp(d, "/md_d", 5)==0)
- d += 5, type=1; /* /dev/md_dNpM */
+ d += 5, type=1; /* /dev/md_dN{pM} */
else if (strncmp(d, "/md", 3)==0)
d += 3, type=-1; /* /dev/mdN */
else if (d-dev > 3 && strncmp(d-2, "md/", 3)==0)
int add_dev(const char *name, const struct stat *stb, int flag, struct FTW *s)
{
struct stat st;
+
if (S_ISLNK(stb->st_mode)) {
- stat(name, &st);
+ if (stat(name, &st) != 0)
+ return 0;
stb = &st;
}
/*
* Find a block device with the right major/minor number.
* If we find multiple names, choose the shortest.
- * If we find a non-standard name, it is probably there
- * deliberately so prefer it over a standard name.
+ * If we find a name in /dev/md/, we prefer that.
* This applies only to names for MD devices.
*/
char *map_dev(int major, int minor, int create)
{
struct devmap *p;
- char *std = NULL, *nonstd=NULL;
+ char *regular = NULL, *preferred=NULL;
int did_check = 0;
if (major == 0 && minor == 0)
for (p=devlist; p; p=p->next)
if (p->major == major &&
p->minor == minor) {
- if (is_standard(p->name, NULL)) {
- if (std == NULL ||
- strlen(p->name) < strlen(std))
- std = p->name;
+ if (strncmp(p->name, "/dev/md/",8) == 0) {
+ if (preferred == NULL ||
+ strlen(p->name) < strlen(preferred))
+ preferred = p->name;
} else {
- if (nonstd == NULL ||
- strlen(p->name) < strlen(nonstd))
- nonstd = p->name;
+ if (regular == NULL ||
+ strlen(p->name) < strlen(regular))
+ regular = p->name;
}
}
- if (!std && !nonstd && !did_check) {
+ if (!regular && !preferred && !did_check) {
devlist_ready = 0;
goto retry;
}
- if (create && !std && !nonstd) {
+ if (create && !regular && !preferred) {
static char buf[30];
snprintf(buf, sizeof(buf), "%d:%d", major, minor);
- nonstd = buf;
+ regular = buf;
}
- return nonstd ? nonstd : std;
+ return preferred ? preferred : regular;
}
unsigned long calc_csum(void *super, int bytes)
);
return buf;
}
+
+void print_r10_layout(int layout)
+{
+ int near = layout & 255;
+ int far = (layout >> 8) & 255;
+ int offset = (layout&0x10000);
+ char *sep = "";
+
+ if (near != 1) {
+ printf("%s near=%d", sep, near);
+ sep = ",";
+ }
+ if (far != 1)
+ printf("%s %s=%d", sep, offset?"offset":"far", far);
+ if (near*far == 1)
+ printf("NO REDUNDANCY");
+}
#endif
unsigned long long calc_array_size(int level, int raid_disks, int layout,
return data_disks * devsize;
}
-#if !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO)
int get_mdp_major(void)
{
static int mdp_major = -1;
return mdp_major;
}
-
-
+#if !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO)
char *get_md_name(int dev)
{
/* find /dev/md%d or /dev/md/%d or make a device /dev/.tmp.md%d */
{
int devnum;
for (devnum = 127; devnum != 128;
- devnum = devnum ? devnum-1 : (1<<22)-1) {
+ devnum = devnum ? devnum-1 : (1<<20)-1) {
char *dn;
int _devnum;
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);
+ fd = open(devname, flags|O_DIRECT);
unlink(devname);
}
} else
- fd = open(dev, flags);
+ fd = open(dev, flags|O_DIRECT);
return fd;
}
+int open_dev(int devnum)
+{
+ char buf[20];
+
+ sprintf(buf, "%d:%d", dev2major(devnum), dev2minor(devnum));
+ return dev_open(buf, O_RDWR);
+}
+
int open_dev_excl(int devnum)
{
char buf[20];
return -1;
}
+int same_dev(char *one, char *two)
+{
+ struct stat st1, st2;
+ if (stat(one, &st1) != 0)
+ return 0;
+ if (stat(two, &st2) != 0)
+ return 0;
+ if ((st1.st_mode & S_IFMT) != S_IFBLK)
+ return 0;
+ if ((st2.st_mode & S_IFMT) != S_IFBLK)
+ return 0;
+ return st1.st_rdev == st2.st_rdev;
+}
+
+void wait_for(char *dev, int fd)
+{
+ int i;
+ struct stat stb_want;
+
+ if (fstat(fd, &stb_want) != 0 ||
+ (stb_want.st_mode & S_IFMT) != S_IFBLK)
+ return;
+
+ for (i=0 ; i<25 ; i++) {
+ struct stat stb;
+ if (stat(dev, &stb) == 0 &&
+ (stb.st_mode & S_IFMT) == S_IFBLK &&
+ (stb.st_rdev == stb_want.st_rdev))
+ return;
+ usleep(200000);
+ }
+}
+
struct superswitch *superlist[] = { &super0, &super1, &super_ddf, &super_imsm, NULL };
#if !defined(MDASSEMBLE) || defined(MDASSEMBLE) && defined(MDASSEMBLE_AUTO)
sprintf(version, "%d.%d", vers, minor);
verstr = version;
}
- if (minor == -2 && verstr[0] == '/') {
+ if (minor == -2 && is_subarray(verstr)) {
char *dev = verstr+1;
subarray = strchr(dev, '/');
int devnum;
if (subarray)
*subarray++ = '\0';
- if (strncmp(dev, "md_d", 4) == 0)
- devnum = -1-atoi(dev+4);
- else
- devnum = atoi(dev+2);
+ devnum = devname2devnum(dev);
subarray = strdup(subarray);
if (sra)
sysfs_free(sra);
sra = sysfs_read(-1, devnum, GET_VERSION);
- verstr = sra->text_version ? : "-no-metadata-";
+ if (sra && sra->text_version[0])
+ verstr = sra->text_version;
+ else
+ verstr = "-no-metadata-";
}
for (i = 0; st == NULL && superlist[i] ; i++)
return -1;
}
+int add_disk(int mdfd, struct supertype *st,
+ struct mdinfo *sra, struct mdinfo *info)
+{
+ /* Add a device to an array, in one of 2 ways. */
+ int rv;
+#ifndef MDASSEMBLE
+ if (st->ss->external) {
+ rv = sysfs_add_disk(sra, info,
+ info->disk.state & (1<<MD_DISK_SYNC));
+ if (! rv) {
+ struct mdinfo *sd2;
+ for (sd2 = sra->devs; sd2; sd2=sd2->next)
+ if (sd2 == info)
+ break;
+ if (sd2 == NULL) {
+ sd2 = malloc(sizeof(*sd2));
+ *sd2 = *info;
+ sd2->next = sra->devs;
+ sra->devs = sd2;
+ }
+ }
+ } else
+#endif
+ rv = ioctl(mdfd, ADD_NEW_DISK, &info->disk);
+ return rv;
+}
+
+int set_array_info(int mdfd, struct supertype *st, struct mdinfo *info)
+{
+ /* Initialise kernel's knowledge of array.
+ * This varies between externally managed arrays
+ * and older kernels
+ */
+ int vers = md_get_version(mdfd);
+ int rv;
+
+#ifndef MDASSEMBLE
+ if (st->ss->external)
+ rv = sysfs_set_array(info, vers);
+ else
+#endif
+ if ((vers % 100) >= 1) { /* can use different versions */
+ mdu_array_info_t inf;
+ memset(&inf, 0, sizeof(inf));
+ inf.major_version = info->array.major_version;
+ inf.minor_version = info->array.minor_version;
+ rv = ioctl(mdfd, SET_ARRAY_INFO, &inf);
+ } else
+ rv = ioctl(mdfd, SET_ARRAY_INFO, NULL);
+ return rv;
+}
+
char *devnum2devname(int num)
{
char name[100];
return strdup(name);
}
+int devname2devnum(char *name)
+{
+ char *ep;
+ int num;
+ if (strncmp(name, "md_d", 4)==0)
+ num = -1-strtoul(name+4, &ep, 10);
+ else
+ num = strtoul(name+2, &ep, 10);
+ return num;
+}
+
+int stat2devnum(struct stat *st)
+{
+ char path[30];
+ char link[200];
+ char *cp;
+ int n;
+
+ if ((S_IFMT & st->st_mode) == S_IFBLK) {
+ if (major(st->st_rdev) == MD_MAJOR)
+ return minor(st->st_rdev);
+ else if (major(st->st_rdev) == get_mdp_major())
+ return -1- (minor(st->st_rdev)>>MdpMinorShift);
+
+ /* must be an extended-minor partition. Look at the
+ * /sys/dev/block/%d:%d link which must look like
+ * ../../block/mdXXX/mdXXXpYY
+ */
+ sprintf(path, "/sys/dev/block/%d:%d", major(st->st_rdev),
+ minor(st->st_rdev));
+ n = readlink(path, link, sizeof(link)-1);
+ if (n <= 0)
+ return NoMdDev;
+ link[n] = 0;
+ cp = strrchr(link, '/');
+ if (cp) *cp = 0;
+ cp = strchr(link, '/');
+ if (cp && strncmp(cp, "/md", 3) == 0)
+ return devname2devnum(cp+1);
+ }
+ return NoMdDev;
+
+}
+
int fd2devnum(int fd)
{
struct stat stb;
- if (fstat(fd, &stb) == 0 &&
- (S_IFMT&stb.st_mode)==S_IFBLK) {
- if (major(stb.st_rdev) == MD_MAJOR)
- return minor(stb.st_rdev);
- else
- return -1- (minor(stb.st_rdev)>>6);
- }
- return -1;
+ if (fstat(fd, &stb) == 0)
+ return stat2devnum(&stb);
+ return NoMdDev;
}
int mdmon_running(int devnum)
return 0;
}
+int start_mdmon(int devnum)
+{
+ int i;
+ int len;
+ pid_t pid;
+ int status;
+ char pathbuf[1024];
+ char *paths[4] = {
+ pathbuf,
+ "/sbin/mdmon",
+ "mdmon",
+ NULL
+ };
+
+ if (check_env("MDADM_NO_MDMON"))
+ return 0;
+ len = readlink("/proc/self/exe", pathbuf, sizeof(pathbuf));
+ if (len > 0) {
+ char *sl;
+ pathbuf[len] = 0;
+ sl = strrchr(pathbuf, '/');
+ if (sl)
+ sl++;
+ else
+ sl = pathbuf;
+ strcpy(sl, "mdmon");
+ } else
+ pathbuf[0] = '\0';
+
+ switch(fork()) {
+ case 0:
+ /* FIXME yuk. CLOSE_EXEC?? */
+ for (i=3; i < 100; i++)
+ close(i);
+ for (i=0; paths[i]; i++)
+ if (paths[i][0])
+ execl(paths[i], "mdmon",
+ devnum2devname(devnum),
+ NULL);
+ exit(1);
+ case -1: fprintf(stderr, Name ": cannot run mdmon. "
+ "Array remains readonly\n");
+ return -1;
+ default: /* parent - good */
+ pid = wait(&status);
+ if (pid < 0 || status != 0)
+ return -1;
+ }
+ return 0;
+}
+
+int check_env(char *name)
+{
+ char *val = getenv(name);
+
+ if (val && atoi(val) == 1)
+ return 1;
+
+ return 0;
+}
+
+#ifndef MDASSEMBLE
int flush_metadata_updates(struct supertype *st)
{
int sfd;
*st->update_tail = mu;
st->update_tail = &mu->next;
}
-
+#endif /* MDASSEMBLE */
#ifdef __TINYC__
/* tinyc doesn't optimize this check in ioctl.h out ... */