int vcpio_wr(ARCHD *arcn) { HD_VCPIO *hd; unsigned int nsz; char hdblk[sizeof(HD_VCPIO)]; /* * check and repair truncated device and inode fields in the cpio * header */ if (map_dev(arcn, (u_long)VCPIO_MASK, (u_long)VCPIO_MASK) < 0) return(-1); nsz = arcn->nlen + 1; hd = (HD_VCPIO *)hdblk; if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) arcn->sb.st_rdev = 0; /* * add the proper magic value depending whether we were asked for * file data crc's, and the crc if needed. */ if (docrc) { if (ul_asc((u_long)VCMAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) || ul_asc((u_long)arcn->crc,hd->c_chksum,sizeof(hd->c_chksum), HEX)) goto out; } else { if (ul_asc((u_long)VMAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) || ul_asc((u_long)0L, hd->c_chksum, sizeof(hd->c_chksum),HEX)) goto out; } switch(arcn->type) { case PAX_CTG: case PAX_REG: case PAX_HRG: /* * caller will copy file data to the archive. tell him how * much to pad. */ arcn->pad = VCPIO_PAD(arcn->sb.st_size); # ifdef NET2_STAT if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize, sizeof(hd->c_filesize), HEX)) { # else if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize, sizeof(hd->c_filesize), HEX)) { # endif paxwarn(1,"File is too large for sv4cpio format %s", arcn->org_name); return(1); } break; case PAX_SLK: /* * no file data for the caller to process, the file data has * the size of the link */ arcn->pad = 0L; if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize, sizeof(hd->c_filesize), HEX)) goto out; break; default: /* * no file data for the caller to process */ arcn->pad = 0L; if (ul_asc((u_long)0L, hd->c_filesize, sizeof(hd->c_filesize), HEX)) goto out; break; } /* * set the other fields in the header */ if (ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino), HEX) || ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode), HEX) || ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid), HEX) || ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid), HEX) || ul_asc((u_long)arcn->sb.st_mtime, hd->c_mtime, sizeof(hd->c_mtime), HEX) || ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink), HEX) || ul_asc((u_long)major(arcn->sb.st_dev),hd->c_maj, sizeof(hd->c_maj), HEX) || ul_asc((u_long)minor(arcn->sb.st_dev),hd->c_min, sizeof(hd->c_min), HEX) || ul_asc((u_long)major(arcn->sb.st_rdev),hd->c_rmaj,sizeof(hd->c_maj), HEX) || ul_asc((u_long)minor(arcn->sb.st_rdev),hd->c_rmin,sizeof(hd->c_min), HEX) || ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), HEX)) goto out; /* * write the header, the file name and padding as required. */ if ((wr_rdbuf(hdblk, (int)sizeof(HD_VCPIO)) < 0) || (wr_rdbuf(arcn->name, (int)nsz) < 0) || (wr_skip((off_t)(VCPIO_PAD(sizeof(HD_VCPIO) + nsz))) < 0)) { paxwarn(1,"Could not write sv4cpio header for %s",arcn->org_name); return(-1); } /* * if we have file data, tell the caller we are done, copy the file */ if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || (arcn->type == PAX_HRG)) return(0); /* * if we are not a link, tell the caller we are done, go to next file */ if (arcn->type != PAX_SLK) return(1); /* * write the link name, tell the caller we are done. */ if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) || (wr_skip((off_t)(VCPIO_PAD(arcn->ln_nlen))) < 0)) { paxwarn(1,"Could not write sv4cpio link name for %s", arcn->org_name); return(-1); } return(1); out: /* * header field is out of range */ paxwarn(1,"Sv4cpio header field is too small for file %s",arcn->org_name); return(1); } /* * Routines common to the old binary header cpio */ /* * bcpio_id() * determine if a block given to us is an old binary cpio header * (with/without header byte swapping) * Return: * 0 if a valid header, -1 otherwise */ int bcpio_id(char *blk, int size) { if (size < (int)sizeof(HD_BCPIO)) return(-1); /* * check both normal and byte swapped magic cookies */ if (((u_short)SHRT_EXT(blk)) == MAGIC) return(0); if (((u_short)RSHRT_EXT(blk)) == MAGIC) { if (!swp_head) ++swp_head; return(0); } return(-1); } /* * bcpio_rd() * determine if a buffer is an old binary archive entry. (It may have byte * swapped header) convert and store the values in the ARCHD parameter. * This is a very old header format and should not really be used. * Return: * 0 if a valid header, -1 otherwise. */ int bcpio_rd(ARCHD *arcn, char *buf) { HD_BCPIO *hd; int nsz; /* * check the header */ if (bcpio_id(buf, sizeof(HD_BCPIO)) < 0) return(-1); arcn->pad = 0L; hd = (HD_BCPIO *)buf; if (swp_head) { /* * header has swapped bytes on 16 bit boundaries */ arcn->sb.st_dev = (dev_t)(RSHRT_EXT(hd->h_dev)); arcn->sb.st_ino = (ino_t)(RSHRT_EXT(hd->h_ino)); arcn->sb.st_mode = (mode_t)(RSHRT_EXT(hd->h_mode)); arcn->sb.st_uid = (uid_t)(RSHRT_EXT(hd->h_uid)); arcn->sb.st_gid = (gid_t)(RSHRT_EXT(hd->h_gid)); arcn->sb.st_nlink = (nlink_t)(RSHRT_EXT(hd->h_nlink)); arcn->sb.st_rdev = (dev_t)(RSHRT_EXT(hd->h_rdev)); arcn->sb.st_mtime = (time_t)(RSHRT_EXT(hd->h_mtime_1)); arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) | ((time_t)(RSHRT_EXT(hd->h_mtime_2))); arcn->sb.st_size = (off_t)(RSHRT_EXT(hd->h_filesize_1)); arcn->sb.st_size = (arcn->sb.st_size << 16) | ((off_t)(RSHRT_EXT(hd->h_filesize_2))); nsz = (int)(RSHRT_EXT(hd->h_namesize)); } else { arcn->sb.st_dev = (dev_t)(SHRT_EXT(hd->h_dev)); arcn->sb.st_ino = (ino_t)(SHRT_EXT(hd->h_ino)); arcn->sb.st_mode = (mode_t)(SHRT_EXT(hd->h_mode)); arcn->sb.st_uid = (uid_t)(SHRT_EXT(hd->h_uid)); arcn->sb.st_gid = (gid_t)(SHRT_EXT(hd->h_gid)); arcn->sb.st_nlink = (nlink_t)(SHRT_EXT(hd->h_nlink)); arcn->sb.st_rdev = (dev_t)(SHRT_EXT(hd->h_rdev)); arcn->sb.st_mtime = (time_t)(SHRT_EXT(hd->h_mtime_1)); arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) | ((time_t)(SHRT_EXT(hd->h_mtime_2))); arcn->sb.st_size = (off_t)(SHRT_EXT(hd->h_filesize_1)); arcn->sb.st_size = (arcn->sb.st_size << 16) | ((off_t)(SHRT_EXT(hd->h_filesize_2))); nsz = (int)(SHRT_EXT(hd->h_namesize)); } arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; /* * check the file name size, if bogus give up. otherwise read the file * name */ if (nsz < 2) return(-1); arcn->nlen = nsz - 1; if (rd_nm(arcn, nsz) < 0) return(-1); /* * header + file name are aligned to 2 byte boundries, skip if needed */ if (rd_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0) return(-1); /* * if not a link (or a file with no data), calculate pad size (for * padding which follows the file data), clear the link name and return */ if (((arcn->sb.st_mode & C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)){ /* * we have a valid header (not a link) */ arcn->ln_nlen = 0; arcn->ln_name[0] = '\0'; arcn->pad = BCPIO_PAD(arcn->sb.st_size); return(com_rd(arcn)); } if ((rd_ln_nm(arcn) < 0) || (rd_skip((off_t)(BCPIO_PAD(arcn->sb.st_size))) < 0)) return(-1); /* * we have a valid header (with a link) */ return(com_rd(arcn)); }
int cpio_wr(ARCHD *arcn) { HD_CPIO *hd; int nsz; char hdblk[sizeof(HD_CPIO)]; /* * check and repair truncated device and inode fields in the header */ if (map_dev(arcn, (u_long)CPIO_MASK, (u_long)CPIO_MASK) < 0) return(-1); arcn->pad = 0L; nsz = arcn->nlen + 1; hd = (HD_CPIO *)hdblk; if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) arcn->sb.st_rdev = 0; switch(arcn->type) { case PAX_CTG: case PAX_REG: case PAX_HRG: /* * set data size for file data */ # ifdef NET2_STAT if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize, sizeof(hd->c_filesize), OCT)) { # else if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize, sizeof(hd->c_filesize), OCT)) { # endif paxwarn(1,"File is too large for cpio format %s", arcn->org_name); return(1); } break; case PAX_SLK: /* * set data size to hold link name */ if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize, sizeof(hd->c_filesize), OCT)) goto out; break; default: /* * all other file types have no file data */ if (ul_asc((u_long)0, hd->c_filesize, sizeof(hd->c_filesize), OCT)) goto out; break; } /* * copy the values to the header using octal ascii */ if (ul_asc((u_long)MAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) || ul_asc((u_long)arcn->sb.st_dev, hd->c_dev, sizeof(hd->c_dev), OCT) || ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino), OCT) || ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode), OCT) || ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid), OCT) || ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid), OCT) || ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink), OCT) || ul_asc((u_long)arcn->sb.st_rdev, hd->c_rdev, sizeof(hd->c_rdev), OCT) || ul_asc((u_long)arcn->sb.st_mtime,hd->c_mtime,sizeof(hd->c_mtime), OCT) || ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), OCT)) goto out; /* * write the file name to the archive */ if ((wr_rdbuf(hdblk, (int)sizeof(HD_CPIO)) < 0) || (wr_rdbuf(arcn->name, nsz) < 0)) { paxwarn(1, "Unable to write cpio header for %s", arcn->org_name); return(-1); } /* * if this file has data, we are done. The caller will write the file * data, if we are link tell caller we are done, go to next file */ if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || (arcn->type == PAX_HRG)) return(0); if (arcn->type != PAX_SLK) return(1); /* * write the link name to the archive, tell the caller to go to the * next file as we are done. */ if (wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) { paxwarn(1,"Unable to write cpio link name for %s",arcn->org_name); return(-1); } return(1); out: /* * header field is out of range */ paxwarn(1, "Cpio header field is too small to store file %s", arcn->org_name); return(1); } /* * Routines common to the system VR4 version of cpio (with/without file CRC) */ /* * vcpio_id() * determine if a block given to us is a valid system VR4 cpio header * WITHOUT crc. WATCH it the magic cookies are in OCTAL, the header * uses HEX * Return: * 0 if a valid header, -1 otherwise */ int vcpio_id(char *blk, int size) { if ((size < (int)sizeof(HD_VCPIO)) || (strncmp(blk, AVMAGIC, sizeof(AVMAGIC) - 1) != 0)) return(-1); return(0); } /* * crc_id() * determine if a block given to us is a valid system VR4 cpio header * WITH crc. WATCH it the magic cookies are in OCTAL the header uses HEX * Return: * 0 if a valid header, -1 otherwise */ int crc_id(char *blk, int size) { if ((size < (int)sizeof(HD_VCPIO)) || (strncmp(blk, AVCMAGIC, (int)sizeof(AVCMAGIC) - 1) != 0)) return(-1); return(0); } /* * crc_strd() w set file data CRC calculations. Fire up the hard link detection code * Return: * 0 if ok -1 otherwise (the return values of lnk_start()) */ int crc_strd(void) { docrc = 1; return(lnk_start()); }
int bcpio_wr(ARCHD *arcn) { HD_BCPIO *hd; int nsz; char hdblk[sizeof(HD_BCPIO)]; off_t t_offt; int t_int; time_t t_timet; /* * check and repair truncated device and inode fields in the cpio * header */ if (map_dev(arcn, (u_long)BCPIO_MASK, (u_long)BCPIO_MASK) < 0) return(-1); if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) arcn->sb.st_rdev = 0; hd = (HD_BCPIO *)hdblk; switch(arcn->type) { case PAX_CTG: case PAX_REG: case PAX_HRG: /* * caller will copy file data to the archive. tell him how * much to pad. */ arcn->pad = BCPIO_PAD(arcn->sb.st_size); hd->h_filesize_1[0] = CHR_WR_0(arcn->sb.st_size); hd->h_filesize_1[1] = CHR_WR_1(arcn->sb.st_size); hd->h_filesize_2[0] = CHR_WR_2(arcn->sb.st_size); hd->h_filesize_2[1] = CHR_WR_3(arcn->sb.st_size); t_offt = (off_t)(SHRT_EXT(hd->h_filesize_1)); t_offt = (t_offt<<16) | ((off_t)(SHRT_EXT(hd->h_filesize_2))); if (arcn->sb.st_size != t_offt) { paxwarn(1,"File is too large for bcpio format %s", arcn->org_name); return(1); } break; case PAX_SLK: /* * no file data for the caller to process, the file data has * the size of the link */ arcn->pad = 0L; hd->h_filesize_1[0] = CHR_WR_0(arcn->ln_nlen); hd->h_filesize_1[1] = CHR_WR_1(arcn->ln_nlen); hd->h_filesize_2[0] = CHR_WR_2(arcn->ln_nlen); hd->h_filesize_2[1] = CHR_WR_3(arcn->ln_nlen); t_int = (int)(SHRT_EXT(hd->h_filesize_1)); t_int = (t_int << 16) | ((int)(SHRT_EXT(hd->h_filesize_2))); if (arcn->ln_nlen != t_int) goto out; break; default: /* * no file data for the caller to process */ arcn->pad = 0L; hd->h_filesize_1[0] = (char)0; hd->h_filesize_1[1] = (char)0; hd->h_filesize_2[0] = (char)0; hd->h_filesize_2[1] = (char)0; break; } /* * build up the rest of the fields */ hd->h_magic[0] = CHR_WR_2(MAGIC); hd->h_magic[1] = CHR_WR_3(MAGIC); hd->h_dev[0] = CHR_WR_2(arcn->sb.st_dev); hd->h_dev[1] = CHR_WR_3(arcn->sb.st_dev); if (arcn->sb.st_dev != (dev_t)(SHRT_EXT(hd->h_dev))) goto out; hd->h_ino[0] = CHR_WR_2(arcn->sb.st_ino); hd->h_ino[1] = CHR_WR_3(arcn->sb.st_ino); if (arcn->sb.st_ino != (ino_t)(SHRT_EXT(hd->h_ino))) goto out; hd->h_mode[0] = CHR_WR_2(arcn->sb.st_mode); hd->h_mode[1] = CHR_WR_3(arcn->sb.st_mode); if (arcn->sb.st_mode != (mode_t)(SHRT_EXT(hd->h_mode))) goto out; hd->h_uid[0] = CHR_WR_2(arcn->sb.st_uid); hd->h_uid[1] = CHR_WR_3(arcn->sb.st_uid); if (arcn->sb.st_uid != (uid_t)(SHRT_EXT(hd->h_uid))) goto out; hd->h_gid[0] = CHR_WR_2(arcn->sb.st_gid); hd->h_gid[1] = CHR_WR_3(arcn->sb.st_gid); if (arcn->sb.st_gid != (gid_t)(SHRT_EXT(hd->h_gid))) goto out; hd->h_nlink[0] = CHR_WR_2(arcn->sb.st_nlink); hd->h_nlink[1] = CHR_WR_3(arcn->sb.st_nlink); if (arcn->sb.st_nlink != (nlink_t)(SHRT_EXT(hd->h_nlink))) goto out; hd->h_rdev[0] = CHR_WR_2(arcn->sb.st_rdev); hd->h_rdev[1] = CHR_WR_3(arcn->sb.st_rdev); if (arcn->sb.st_rdev != (dev_t)(SHRT_EXT(hd->h_rdev))) goto out; hd->h_mtime_1[0] = CHR_WR_0(arcn->sb.st_mtime); hd->h_mtime_1[1] = CHR_WR_1(arcn->sb.st_mtime); hd->h_mtime_2[0] = CHR_WR_2(arcn->sb.st_mtime); hd->h_mtime_2[1] = CHR_WR_3(arcn->sb.st_mtime); t_timet = (time_t)(SHRT_EXT(hd->h_mtime_1)); t_timet = (t_timet << 16) | ((time_t)(SHRT_EXT(hd->h_mtime_2))); if (arcn->sb.st_mtime != t_timet) goto out; nsz = arcn->nlen + 1; hd->h_namesize[0] = CHR_WR_2(nsz); hd->h_namesize[1] = CHR_WR_3(nsz); if (nsz != (int)(SHRT_EXT(hd->h_namesize))) goto out; /* * write the header, the file name and padding as required. */ if ((wr_rdbuf(hdblk, (int)sizeof(HD_BCPIO)) < 0) || (wr_rdbuf(arcn->name, nsz) < 0) || (wr_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0)) { paxwarn(1, "Could not write bcpio header for %s", arcn->org_name); return(-1); } /* * if we have file data, tell the caller we are done */ if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || (arcn->type == PAX_HRG)) return(0); /* * if we are not a link, tell the caller we are done, go to next file */ if (arcn->type != PAX_SLK) return(1); /* * write the link name, tell the caller we are done. */ if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) || (wr_skip((off_t)(BCPIO_PAD(arcn->ln_nlen))) < 0)) { paxwarn(1,"Could not write bcpio link name for %s",arcn->org_name); return(-1); } return(1); out: /* * header field is out of range */ paxwarn(1,"Bcpio header field is too small for file %s", arcn->org_name); return(1); }
int Monitor(mddev_dev_t devlist, char *mailaddr, char *alert_cmd, int period, int daemonise, int scan, int oneshot, int dosyslog, int test, char* pidfile, int increments) { /* * Every few seconds, scan every md device looking for changes * When a change is found, log it, possibly run the alert command, * and possibly send Email * * For each array, we record: * Update time * active/working/failed/spare drives * State of each device. * %rebuilt if rebuilding * * If the update time changes, check out all the data again * It is possible that we cannot get the state of each device * due to bugs in the md kernel module. * We also read /proc/mdstat to get rebuild percent, * and to get state on all active devices incase of kernel bug. * * Events are: * Fail * An active device had Faulty set or Active/Sync removed * FailSpare * A spare device had Faulty set * SpareActive * An active device had a reverse transition * RebuildStarted * percent went from -1 to +ve * RebuildNN * percent went from below to not-below NN% * DeviceDisappeared * Couldn't access a device which was previously visible * * if we detect an array with active<raid and spare==0 * we look at other arrays that have same spare-group * If we find one with active==raid and spare>0, * and if we can get_disk_info and find a name * Then we hot-remove and hot-add to the other array * * If devlist is NULL, then we can monitor everything because --scan * was given. We get an initial list from config file and add anything * that appears in /proc/mdstat */ struct state { char *devname; int devnum; /* to sync with mdstat info */ long utime; int err; char *spare_group; int active, working, failed, spare, raid; int expected_spares; int devstate[MaxDisks]; unsigned devid[MaxDisks]; int percent; struct state *next; } *statelist = NULL; int finished = 0; struct mdstat_ent *mdstat = NULL; char *mailfrom = NULL; if (!mailaddr) { mailaddr = conf_get_mailaddr(); if (mailaddr && ! scan) fprintf(stderr, Name ": Monitor using email address \"%s\" from config file\n", mailaddr); } mailfrom = conf_get_mailfrom(); if (!alert_cmd) { alert_cmd = conf_get_program(); if (alert_cmd && ! scan) fprintf(stderr, Name ": Monitor using program \"%s\" from config file\n", alert_cmd); } if (scan && !mailaddr && !alert_cmd) { fprintf(stderr, Name ": No mail address or alert command - not monitoring.\n"); return 1; } if (daemonise) { int pid = fork(); if (pid > 0) { if (!pidfile) printf("%d\n", pid); else { FILE *pid_file; pid_file=fopen(pidfile, "w"); if (!pid_file) perror("cannot create pid file"); else { fprintf(pid_file,"%d\n", pid); fclose(pid_file); } } return 0; } if (pid < 0) { perror("daemonise"); return 1; } close(0); open("/dev/null", O_RDWR); dup2(0,1); dup2(0,2); setsid(); } if (devlist == NULL) { mddev_ident_t mdlist = conf_get_ident(NULL); for (; mdlist; mdlist=mdlist->next) { struct state *st; if (mdlist->devname == NULL) continue; if (strcasecmp(mdlist->devname, "<ignore>") == 0) continue; st = malloc(sizeof *st); if (st == NULL) continue; if (mdlist->devname[0] == '/') st->devname = strdup(mdlist->devname); else { st->devname = malloc(8+strlen(mdlist->devname)+1); strcpy(strcpy(st->devname, "/dev/md/"), mdlist->devname); } st->utime = 0; st->next = statelist; st->err = 0; st->devnum = INT_MAX; st->percent = -2; st->expected_spares = mdlist->spare_disks; if (mdlist->spare_group) st->spare_group = strdup(mdlist->spare_group); else st->spare_group = NULL; statelist = st; } } else { mddev_dev_t dv; for (dv=devlist ; dv; dv=dv->next) { mddev_ident_t mdlist = conf_get_ident(dv->devname); struct state *st = malloc(sizeof *st); if (st == NULL) continue; st->devname = strdup(dv->devname); st->utime = 0; st->next = statelist; st->err = 0; st->devnum = INT_MAX; st->percent = -2; st->expected_spares = -1; st->spare_group = NULL; if (mdlist) { st->expected_spares = mdlist->spare_disks; if (mdlist->spare_group) st->spare_group = strdup(mdlist->spare_group); } statelist = st; } } while (! finished) { int new_found = 0; struct state *st; if (mdstat) free_mdstat(mdstat); mdstat = mdstat_read(oneshot?0:1, 0); for (st=statelist; st; st=st->next) { struct { int state, major, minor; } info[MaxDisks]; mdu_array_info_t array; struct mdstat_ent *mse = NULL, *mse2; char *dev = st->devname; int fd; int i; if (test) alert("TestMessage", dev, NULL, mailaddr, mailfrom, alert_cmd, dosyslog); fd = open(dev, O_RDONLY); if (fd < 0) { if (!st->err) alert("DeviceDisappeared", dev, NULL, mailaddr, mailfrom, alert_cmd, dosyslog); /* fprintf(stderr, Name ": cannot open %s: %s\n", dev, strerror(errno)); */ st->err=1; continue; } fcntl(fd, F_SETFD, FD_CLOEXEC); if (ioctl(fd, GET_ARRAY_INFO, &array)<0) { if (!st->err) alert("DeviceDisappeared", dev, NULL, mailaddr, mailfrom, alert_cmd, dosyslog); /* fprintf(stderr, Name ": cannot get array info for %s: %s\n", dev, strerror(errno)); */ st->err=1; close(fd); continue; } /* It's much easier to list what array levels can't * have a device disappear than all of them that can */ if (array.level == 0 || array.level == -1) { if (!st->err) alert("DeviceDisappeared", dev, "Wrong-Level", mailaddr, mailfrom, alert_cmd, dosyslog); st->err = 1; close(fd); continue; } if (st->devnum == INT_MAX) { struct stat stb; if (fstat(fd, &stb) == 0 && (S_IFMT&stb.st_mode)==S_IFBLK) { if (major(stb.st_rdev) == MD_MAJOR) st->devnum = minor(stb.st_rdev); else st->devnum = -1- (minor(stb.st_rdev)>>6); } } for (mse2 = mdstat ; mse2 ; mse2=mse2->next) if (mse2->devnum == st->devnum) { mse2->devnum = INT_MAX; /* flag it as "used" */ mse = mse2; } if (array.utime == 0) /* external arrays don't update utime */ array.utime = time(0); if (st->utime == array.utime && st->failed == array.failed_disks && st->working == array.working_disks && st->spare == array.spare_disks && (mse == NULL || ( mse->percent == st->percent ))) { close(fd); st->err = 0; continue; } if (st->utime == 0 && /* new array */ mse && /* is in /proc/mdstat */ mse->pattern && strchr(mse->pattern, '_') /* degraded */ ) alert("DegradedArray", dev, NULL, mailaddr, mailfrom, alert_cmd, dosyslog); if (st->utime == 0 && /* new array */ st->expected_spares > 0 && array.spare_disks < st->expected_spares) alert("SparesMissing", dev, NULL, mailaddr, mailfrom, alert_cmd, dosyslog); if (mse && st->percent == -1 && mse->percent >= 0) alert("RebuildStarted", dev, NULL, mailaddr, mailfrom, alert_cmd, dosyslog); if (mse && st->percent >= 0 && mse->percent >= 0 && (mse->percent / increments) > (st->percent / increments)) { char percentalert[15]; // "RebuildNN" (10 chars) or "RebuildStarted" (15 chars) if((mse->percent / increments) == 0) snprintf(percentalert, sizeof(percentalert), "RebuildStarted"); else snprintf(percentalert, sizeof(percentalert), "Rebuild%02d", mse->percent); alert(percentalert, dev, NULL, mailaddr, mailfrom, alert_cmd, dosyslog); } if (mse && mse->percent == -1 && st->percent >= 0) { /* Rebuild/sync/whatever just finished. * If there is a number in /mismatch_cnt, * we should report that. */ struct mdinfo *sra = sysfs_read(-1, st->devnum, GET_MISMATCH); if (sra && sra->mismatch_cnt > 0) { char cnt[40]; sprintf(cnt, " mismatches found: %d", sra->mismatch_cnt); alert("RebuildFinished", dev, cnt, mailaddr, mailfrom, alert_cmd, dosyslog); } else alert("RebuildFinished", dev, NULL, mailaddr, mailfrom, alert_cmd, dosyslog); if (sra) free(sra); } if (mse) st->percent = mse->percent; for (i=0; i<MaxDisks && i <= array.raid_disks + array.nr_disks; i++) { mdu_disk_info_t disc; disc.number = i; if (ioctl(fd, GET_DISK_INFO, &disc) >= 0) { info[i].state = disc.state; info[i].major = disc.major; info[i].minor = disc.minor; } else info[i].major = info[i].minor = 0; } close(fd); for (i=0; i<MaxDisks; i++) { mdu_disk_info_t disc = {0,0,0,0,0}; int newstate=0; int change; char *dv = NULL; disc.number = i; if (i > array.raid_disks + array.nr_disks) { newstate = 0; disc.major = disc.minor = 0; } else if (info[i].major || info[i].minor) { newstate = info[i].state; dv = map_dev(info[i].major, info[i].minor, 1); disc.state = newstate; disc.major = info[i].major; disc.minor = info[i].minor; } else if (mse && mse->pattern && i < (int)strlen(mse->pattern)) { switch(mse->pattern[i]) { case 'U': newstate = 6 /* ACTIVE/SYNC */; break; case '_': newstate = 0; break; } disc.major = disc.minor = 0; } if (dv == NULL && st->devid[i]) dv = map_dev(major(st->devid[i]), minor(st->devid[i]), 1); change = newstate ^ st->devstate[i]; if (st->utime && change && !st->err) { if (i < array.raid_disks && (((newstate&change)&(1<<MD_DISK_FAULTY)) || ((st->devstate[i]&change)&(1<<MD_DISK_ACTIVE)) || ((st->devstate[i]&change)&(1<<MD_DISK_SYNC))) ) alert("Fail", dev, dv, mailaddr, mailfrom, alert_cmd, dosyslog); else if (i >= array.raid_disks && (disc.major || disc.minor) && st->devid[i] == makedev(disc.major, disc.minor) && ((newstate&change)&(1<<MD_DISK_FAULTY)) ) alert("FailSpare", dev, dv, mailaddr, mailfrom, alert_cmd, dosyslog); else if (i < array.raid_disks && ! (newstate & (1<<MD_DISK_REMOVED)) && (((st->devstate[i]&change)&(1<<MD_DISK_FAULTY)) || ((newstate&change)&(1<<MD_DISK_ACTIVE)) || ((newstate&change)&(1<<MD_DISK_SYNC))) ) alert("SpareActive", dev, dv, mailaddr, mailfrom, alert_cmd, dosyslog); } st->devstate[i] = newstate; st->devid[i] = makedev(disc.major, disc.minor); } st->active = array.active_disks; st->working = array.working_disks; st->spare = array.spare_disks; st->failed = array.failed_disks; st->utime = array.utime; st->raid = array.raid_disks; st->err = 0; } /* now check if there are any new devices found in mdstat */ if (scan) { struct mdstat_ent *mse; for (mse=mdstat; mse; mse=mse->next) if (mse->devnum != INT_MAX && mse->level && (strcmp(mse->level, "raid0")!=0 && strcmp(mse->level, "linear")!=0) ) { struct state *st = malloc(sizeof *st); mdu_array_info_t array; int fd; if (st == NULL) continue; st->devname = strdup(get_md_name(mse->devnum)); if ((fd = open(st->devname, O_RDONLY)) < 0 || ioctl(fd, GET_ARRAY_INFO, &array)< 0) { /* no such array */ if (fd >=0) close(fd); put_md_name(st->devname); free(st->devname); free(st); continue; } close(fd); st->utime = 0; st->next = statelist; st->err = 1; st->devnum = mse->devnum; st->percent = -2; st->spare_group = NULL; st->expected_spares = -1; statelist = st; if (test) alert("TestMessage", st->devname, NULL, mailaddr, mailfrom, alert_cmd, dosyslog); alert("NewArray", st->devname, NULL, mailaddr, mailfrom, alert_cmd, dosyslog); new_found = 1; } } /* If an array has active < raid && spare == 0 && spare_group != NULL * Look for another array with spare > 0 and active == raid and same spare_group * if found, choose a device and hotremove/hotadd */ for (st = statelist; st; st=st->next) if (st->active < st->raid && st->spare == 0 && st->spare_group != NULL) { struct state *st2; for (st2=statelist ; st2 ; st2=st2->next) if (st2 != st && st2->spare > 0 && st2->active == st2->raid && st2->spare_group != NULL && strcmp(st->spare_group, st2->spare_group) == 0) { /* try to remove and add */ int fd1 = open(st->devname, O_RDONLY); int fd2 = open(st2->devname, O_RDONLY); int dev = -1; int d; if (fd1 < 0 || fd2 < 0) { if (fd1>=0) close(fd1); if (fd2>=0) close(fd2); continue; } for (d=st2->raid; d < MaxDisks; d++) { if (st2->devid[d] > 0 && st2->devstate[d] == 0) { dev = st2->devid[d]; break; } } if (dev > 0) { struct mddev_dev_s devlist; char devname[20]; devlist.next = NULL; devlist.used = 0; devlist.re_add = 0; devlist.writemostly = 0; devlist.devname = devname; sprintf(devname, "%d:%d", major(dev), minor(dev)); devlist.disposition = 'r'; if (Manage_subdevs(st2->devname, fd2, &devlist, -1, 0) == 0) { devlist.disposition = 'a'; if (Manage_subdevs(st->devname, fd1, &devlist, -1, 0) == 0) { alert("MoveSpare", st->devname, st2->devname, mailaddr, mailfrom, alert_cmd, dosyslog); close(fd1); close(fd2); break; } else Manage_subdevs(st2->devname, fd2, &devlist, -1, 0); } } close(fd1); close(fd2); } } if (!new_found) { if (oneshot) break; else mdstat_wait(period); } test = 0; } if (pidfile) unlink(pidfile); return 0; }
int main(int argc, char *argv[]) { /* md_device start length */ int *fds = NULL; char *buf = NULL; char **disk_name = NULL; unsigned long long *offsets = NULL; int raid_disks = 0; int active_disks; int chunk_size = 0; int layout = -1; int level = 6; int repair = 0; int failed_disk1, failed_disk2; unsigned long long start, length; int i; int mdfd; struct mdinfo *info = NULL, *comp = NULL; char *err = NULL; int exit_err = 0; int close_flag = 0; char *prg = strrchr(argv[0], '/'); if (prg == NULL) prg = argv[0]; else prg++; if (argc < 4) { fprintf(stderr, "Usage: %s md_device start_stripe length_stripes [autorepair]\n", prg); fprintf(stderr, " or: %s md_device repair stripe failed_slot_1 failed_slot_2\n", prg); exit_err = 1; goto exitHere; } mdfd = open(argv[1], O_RDONLY); if(mdfd < 0) { perror(argv[1]); fprintf(stderr, "%s: cannot open %s\n", prg, argv[1]); exit_err = 2; goto exitHere; } info = sysfs_read(mdfd, -1, GET_LEVEL| GET_LAYOUT| GET_DISKS| GET_DEGRADED | GET_COMPONENT| GET_CHUNK| GET_DEVS| GET_OFFSET| GET_SIZE); if(info == NULL) { fprintf(stderr, "%s: Error reading sysfs information of %s\n", prg, argv[1]); exit_err = 9; goto exitHere; } if(info->array.level != level) { fprintf(stderr, "%s: %s not a RAID-6\n", prg, argv[1]); exit_err = 3; goto exitHere; } if(info->array.failed_disks > 0) { fprintf(stderr, "%s: %s degraded array\n", prg, argv[1]); exit_err = 8; goto exitHere; } printf("layout: %d\n", info->array.layout); printf("disks: %d\n", info->array.raid_disks); printf("component size: %llu\n", info->component_size * 512); printf("total stripes: %llu\n", (info->component_size * 512) / info->array.chunk_size); printf("chunk size: %d\n", info->array.chunk_size); printf("\n"); comp = info->devs; for(i = 0, active_disks = 0; active_disks < info->array.raid_disks; i++) { printf("disk: %d - offset: %llu - size: %llu - name: %s - slot: %d\n", i, comp->data_offset * 512, comp->component_size * 512, map_dev(comp->disk.major, comp->disk.minor, 0), comp->disk.raid_disk); if(comp->disk.raid_disk >= 0) active_disks++; comp = comp->next; } printf("\n"); close(mdfd); raid_disks = info->array.raid_disks; chunk_size = info->array.chunk_size; layout = info->array.layout; if (strcmp(argv[2], "repair")==0) { if (argc < 6) { fprintf(stderr, "For repair mode, call %s md_device repair stripe failed_slot_1 failed_slot_2\n", prg); exit_err = 1; goto exitHere; } repair = 1; start = getnum(argv[3], &err); length = 1; failed_disk1 = getnum(argv[4], &err); failed_disk2 = getnum(argv[5], &err); if(failed_disk1 >= info->array.raid_disks) { fprintf(stderr, "%s: failed_slot_1 index is higher than number of devices in raid\n", prg); exit_err = 4; goto exitHere; } if(failed_disk2 >= info->array.raid_disks) { fprintf(stderr, "%s: failed_slot_2 index is higher than number of devices in raid\n", prg); exit_err = 4; goto exitHere; } if(failed_disk1 == failed_disk2) { fprintf(stderr, "%s: failed_slot_1 and failed_slot_2 are the same\n", prg); exit_err = 4; goto exitHere; } } else { start = getnum(argv[2], &err); length = getnum(argv[3], &err); if (argc >= 5 && strcmp(argv[4], "autorepair")==0) repair = 2; } if (err) { fprintf(stderr, "%s: Bad number: %s\n", prg, err); exit_err = 4; goto exitHere; } if(start > ((info->component_size * 512) / chunk_size)) { start = (info->component_size * 512) / chunk_size; fprintf(stderr, "%s: start beyond disks size\n", prg); } if((length == 0) || ((length + start) > ((info->component_size * 512) / chunk_size))) { length = (info->component_size * 512) / chunk_size - start; } disk_name = xmalloc(raid_disks * sizeof(*disk_name)); fds = xmalloc(raid_disks * sizeof(*fds)); offsets = xcalloc(raid_disks, sizeof(*offsets)); buf = xmalloc(raid_disks * chunk_size); for(i=0; i<raid_disks; i++) { fds[i] = -1; } close_flag = 1; comp = info->devs; for (i=0, active_disks=0; active_disks<raid_disks; i++) { int disk_slot = comp->disk.raid_disk; if(disk_slot >= 0) { disk_name[disk_slot] = map_dev(comp->disk.major, comp->disk.minor, 0); offsets[disk_slot] = comp->data_offset * 512; fds[disk_slot] = open(disk_name[disk_slot], O_RDWR); if (fds[disk_slot] < 0) { perror(disk_name[disk_slot]); fprintf(stderr,"%s: cannot open %s\n", prg, disk_name[disk_slot]); exit_err = 6; goto exitHere; } active_disks++; } comp = comp->next; } int rv = check_stripes(info, fds, offsets, raid_disks, chunk_size, level, layout, start, length, disk_name, repair, failed_disk1, failed_disk2); if (rv != 0) { fprintf(stderr, "%s: check_stripes returned %d\n", prg, rv); exit_err = 7; goto exitHere; } exitHere: if (close_flag) for(i = 0; i < raid_disks; i++) close(fds[i]); free(disk_name); free(fds); free(offsets); free(buf); exit(exit_err); }
int Manage_subdevs(char *devname, int fd, struct mddev_dev *devlist, int verbose, int test, char *update) { /* do something to each dev. * devmode can be * 'a' - add the device * try HOT_ADD_DISK * If that fails EINVAL, try ADD_NEW_DISK * 'r' - remove the device HOT_REMOVE_DISK * device can be 'faulty' or 'detached' in which case all * matching devices are removed. * 'f' - set the device faulty SET_DISK_FAULTY * device can be 'detached' in which case any device that * is inaccessible will be marked faulty. * For 'f' and 'r', the device can also be a kernel-internal * name such as 'sdb'. */ struct mddev_dev *add_devlist = NULL; mdu_array_info_t array; mdu_disk_info_t disc; unsigned long long array_size; struct mddev_dev *dv, *next = NULL; struct stat stb; int j, jnext = 0; int tfd = -1; struct supertype *st, *tst; char *subarray = NULL; int duuid[4]; int ouuid[4]; int lfd = -1; int sysfd = -1; int count = 0; /* number of actions taken */ if (ioctl(fd, GET_ARRAY_INFO, &array)) { fprintf(stderr, Name ": cannot get array info for %s\n", devname); return 1; } /* array.size is only 32 bit and may be truncated. * So read from sysfs if possible, and record number of sectors */ array_size = get_component_size(fd); if (array_size <= 0) array_size = array.size * 2; tst = super_by_fd(fd, &subarray); if (!tst) { fprintf(stderr, Name ": unsupport array - version %d.%d\n", array.major_version, array.minor_version); return 1; } stb.st_rdev = 0; for (dv = devlist, j=0 ; dv; dv = next, j = jnext) { unsigned long long ldsize; char dvname[20]; char *dnprintable = dv->devname; char *add_dev = dv->devname; int err; int re_add_failed = 0; next = dv->next; jnext = 0; if (strcmp(dv->devname, "failed")==0 || strcmp(dv->devname, "faulty")==0) { int remaining_disks = array.nr_disks; if (dv->disposition != 'r') { fprintf(stderr, Name ": %s only meaningful " "with -r, not -%c\n", dv->devname, dv->disposition); return 1; } for (; j < 1024 && remaining_disks > 0; j++) { unsigned dev; disc.number = j; if (ioctl(fd, GET_DISK_INFO, &disc)) continue; if (disc.major == 0 && disc.minor == 0) continue; remaining_disks --; if ((disc.state & 1) == 0) /* faulty */ continue; dev = makedev(disc.major, disc.minor); if (stb.st_rdev == dev) /* already did that one */ continue; stb.st_rdev = dev; next = dv; /* same slot again next time - things might * have reshuffled */ jnext = j; sprintf(dvname,"%d:%d", disc.major, disc.minor); dnprintable = dvname; break; } if (next != dv) continue; } else if (strcmp(dv->devname, "detached") == 0) { int remaining_disks = array.nr_disks; if (dv->disposition != 'r' && dv->disposition != 'f') { fprintf(stderr, Name ": %s only meaningful " "with -r of -f, not -%c\n", dv->devname, dv->disposition); return 1; } for (; j < 1024 && remaining_disks > 0; j++) { int sfd; unsigned dev; disc.number = j; if (ioctl(fd, GET_DISK_INFO, &disc)) continue; if (disc.major == 0 && disc.minor == 0) continue; remaining_disks --; sprintf(dvname,"%d:%d", disc.major, disc.minor); sfd = dev_open(dvname, O_RDONLY); if (sfd >= 0) { close(sfd); continue; } if (dv->disposition == 'f' && (disc.state & 1) == 1) /* already faulty */ continue; if (errno != ENXIO) continue; dev = makedev(disc.major, disc.minor); if (stb.st_rdev == dev) /* already did that one */ continue; stb.st_rdev = dev; next = dv; /* same slot again next time - things might * have reshuffled */ jnext = j; dnprintable = dvname; break; } if (next != dv) continue; } else if (strcmp(dv->devname, "missing") == 0) { if (dv->disposition != 'a' || dv->re_add == 0) { fprintf(stderr, Name ": 'missing' only meaningful " "with --re-add\n"); return 1; } if (add_devlist == NULL) add_devlist = conf_get_devs(); if (add_devlist == NULL) { fprintf(stderr, Name ": no devices to scan for missing members."); continue; } add_dev = add_devlist->devname; add_devlist = add_devlist->next; if (add_devlist != NULL) next = dv; if (stat(add_dev, &stb) < 0) continue; } else if (strchr(dv->devname, '/') == NULL && strchr(dv->devname, ':') == NULL && strlen(dv->devname) < 50) { /* Assume this is a kernel-internal name like 'sda1' */ int found = 0; char dname[55]; if (dv->disposition != 'r' && dv->disposition != 'f') { fprintf(stderr, Name ": %s only meaningful " "with -r or -f, not -%c\n", dv->devname, dv->disposition); return 1; } sprintf(dname, "dev-%s", dv->devname); sysfd = sysfs_open(fd2devnum(fd), dname, "block/dev"); if (sysfd >= 0) { char dn[20]; int mj,mn; if (sysfs_fd_get_str(sysfd, dn, 20) > 0 && sscanf(dn, "%d:%d", &mj,&mn) == 2) { stb.st_rdev = makedev(mj,mn); found = 1; } close(sysfd); sysfd = -1; } if (!found) { sysfd = sysfs_open(fd2devnum(fd), dname, "state"); if (sysfd < 0) { fprintf(stderr, Name ": %s does not appear " "to be a component of %s\n", dv->devname, devname); return 1; } } } else { j = 0; tfd = dev_open(dv->devname, O_RDONLY); if (tfd < 0 && dv->disposition == 'r' && lstat(dv->devname, &stb) == 0) /* Be happy, the lstat worked, that is * enough for --remove */ ; else { if (tfd < 0 || fstat(tfd, &stb) != 0) { fprintf(stderr, Name ": cannot find %s: %s\n", dv->devname, strerror(errno)); if (tfd >= 0) close(tfd); return 1; } close(tfd); tfd = -1; } if ((stb.st_mode & S_IFMT) != S_IFBLK) { fprintf(stderr, Name ": %s is not a " "block device.\n", dv->devname); return 1; } } switch(dv->disposition){ default: fprintf(stderr, Name ": internal error - devmode[%s]=%d\n", dv->devname, dv->disposition); return 1; case 'a': /* add the device */ if (subarray) { fprintf(stderr, Name ": Cannot add disks to a" " \'member\' array, perform this" " operation on the parent container\n"); return 1; } /* Make sure it isn't in use (in 2.6 or later) */ tfd = dev_open(add_dev, O_RDONLY|O_EXCL|O_DIRECT); if (tfd < 0 && add_dev != dv->devname) continue; if (tfd < 0) { fprintf(stderr, Name ": Cannot open %s: %s\n", dv->devname, strerror(errno)); return 1; } st = dup_super(tst); if (array.not_persistent==0) st->ss->load_super(st, tfd, NULL); if (add_dev == dv->devname) { if (!get_dev_size(tfd, dv->devname, &ldsize)) { close(tfd); return 1; } } else if (!get_dev_size(tfd, NULL, &ldsize)) { close(tfd); tfd = -1; continue; } if (!tst->ss->external && array.major_version == 0 && md_get_version(fd)%100 < 2) { close(tfd); tfd = -1; if (ioctl(fd, HOT_ADD_DISK, (unsigned long)stb.st_rdev)==0) { if (verbose >= 0) fprintf(stderr, Name ": hot added %s\n", add_dev); continue; } fprintf(stderr, Name ": hot add failed for %s: %s\n", add_dev, strerror(errno)); return 1; } if (array.not_persistent == 0 || tst->ss->external) { /* need to find a sample superblock to copy, and * a spare slot to use. * For 'external' array (well, container based), * We can just load the metadata for the array. */ if (tst->sb) /* already loaded */; else if (tst->ss->external) { tst->ss->load_container(tst, fd, NULL); } else for (j = 0; j < tst->max_devs; j++) { char *dev; int dfd; disc.number = j; if (ioctl(fd, GET_DISK_INFO, &disc)) continue; if (disc.major==0 && disc.minor==0) continue; if ((disc.state & 4)==0) continue; /* sync */ /* Looks like a good device to try */ dev = map_dev(disc.major, disc.minor, 1); if (!dev) continue; dfd = dev_open(dev, O_RDONLY); if (dfd < 0) continue; if (tst->ss->load_super(tst, dfd, NULL)) { close(dfd); continue; } close(dfd); break; } /* FIXME this is a bad test to be using */ if (!tst->sb) { close(tfd); fprintf(stderr, Name ": cannot load array metadata from %s\n", devname); return 1; } /* Make sure device is large enough */ if (tst->ss->avail_size(tst, ldsize/512) < array_size) { close(tfd); tfd = -1; if (add_dev != dv->devname) continue; fprintf(stderr, Name ": %s not large enough to join array\n", dv->devname); return 1; } /* Possibly this device was recently part of the array * and was temporarily removed, and is now being re-added. * If so, we can simply re-add it. */ tst->ss->uuid_from_super(tst, duuid); if (st->sb) { struct mdinfo mdi; st->ss->getinfo_super(st, &mdi, NULL); st->ss->uuid_from_super(st, ouuid); if ((mdi.disk.state & (1<<MD_DISK_ACTIVE)) && !(mdi.disk.state & (1<<MD_DISK_FAULTY)) && memcmp(duuid, ouuid, sizeof(ouuid))==0) { /* look like it is worth a try. Need to * make sure kernel will accept it though. */ /* re-add doesn't work for version-1 superblocks * before 2.6.18 :-( */ if (array.major_version == 1 && get_linux_version() <= 2006018) goto skip_re_add; disc.number = mdi.disk.number; if (ioctl(fd, GET_DISK_INFO, &disc) != 0 || disc.major != 0 || disc.minor != 0 || !enough_fd(fd)) goto skip_re_add; disc.major = major(stb.st_rdev); disc.minor = minor(stb.st_rdev); disc.number = mdi.disk.number; disc.raid_disk = mdi.disk.raid_disk; disc.state = mdi.disk.state; if (dv->writemostly == 1) disc.state |= 1 << MD_DISK_WRITEMOSTLY; if (dv->writemostly == 2) disc.state &= ~(1 << MD_DISK_WRITEMOSTLY); remove_partitions(tfd); close(tfd); tfd = -1; if (update) { int rv = -1; tfd = dev_open(dv->devname, O_RDWR); if (tfd >= 0) rv = st->ss->update_super( st, NULL, update, devname, verbose, 0, NULL); if (rv == 0) rv = st->ss->store_super(st, tfd); close(tfd); tfd = -1; if (rv != 0) { fprintf(stderr, Name ": failed to update" " superblock during re-add\n"); return 1; } } /* don't even try if disk is marked as faulty */ errno = 0; if (ioctl(fd, ADD_NEW_DISK, &disc) == 0) { if (verbose >= 0) fprintf(stderr, Name ": re-added %s\n", add_dev); count++; continue; } if (errno == ENOMEM || errno == EROFS) { fprintf(stderr, Name ": add new device failed for %s: %s\n", add_dev, strerror(errno)); if (add_dev != dv->devname) continue; return 1; } skip_re_add: re_add_failed = 1; } st->ss->free_super(st); } if (add_dev != dv->devname) { if (verbose > 0) fprintf(stderr, Name ": --re-add for %s to %s is not possible\n", add_dev, devname); if (tfd >= 0) { close(tfd); tfd = -1; } continue; } if (dv->re_add) { if (tfd >= 0) close(tfd); fprintf(stderr, Name ": --re-add for %s to %s is not possible\n", dv->devname, devname); return 1; } if (re_add_failed) { fprintf(stderr, Name ": %s reports being an active member for %s, but a --re-add fails.\n", dv->devname, devname); fprintf(stderr, Name ": not performing --add as that would convert %s in to a spare.\n", dv->devname); fprintf(stderr, Name ": To make this a spare, use \"mdadm --zero-superblock %s\" first.\n", dv->devname); if (tfd >= 0) close(tfd); return 1; } } else { /* non-persistent. Must ensure that new drive * is at least array.size big. */ if (ldsize/512 < array_size) { fprintf(stderr, Name ": %s not large enough to join array\n", dv->devname); if (tfd >= 0) close(tfd); return 1; } } /* committed to really trying this device now*/ if (tfd >= 0) { remove_partitions(tfd); close(tfd); tfd = -1; } /* in 2.6.17 and earlier, version-1 superblocks won't * use the number we write, but will choose a free number. * we must choose the same free number, which requires * starting at 'raid_disks' and counting up */ for (j = array.raid_disks; j< tst->max_devs; j++) { disc.number = j; if (ioctl(fd, GET_DISK_INFO, &disc)) break; if (disc.major==0 && disc.minor==0) break; if (disc.state & 8) /* removed */ break; } disc.major = major(stb.st_rdev); disc.minor = minor(stb.st_rdev); disc.number =j; disc.state = 0; if (array.not_persistent==0) { int dfd; if (dv->writemostly == 1) disc.state |= 1 << MD_DISK_WRITEMOSTLY; dfd = dev_open(dv->devname, O_RDWR | O_EXCL|O_DIRECT); if (tst->ss->add_to_super(tst, &disc, dfd, dv->devname)) { close(dfd); return 1; } if (tst->ss->write_init_super(tst)) { close(dfd); return 1; } } else if (dv->re_add) { /* this had better be raid1. * As we are "--re-add"ing we must find a spare slot * to fill. */ char *used = malloc(array.raid_disks); memset(used, 0, array.raid_disks); for (j=0; j< tst->max_devs; j++) { mdu_disk_info_t disc2; disc2.number = j; if (ioctl(fd, GET_DISK_INFO, &disc2)) continue; if (disc2.major==0 && disc2.minor==0) continue; if (disc2.state & 8) /* removed */ continue; if (disc2.raid_disk < 0) continue; if (disc2.raid_disk > array.raid_disks) continue; used[disc2.raid_disk] = 1; } for (j=0 ; j<array.raid_disks; j++) if (!used[j]) { disc.raid_disk = j; disc.state |= (1<<MD_DISK_SYNC); break; } free(used); } if (dv->writemostly == 1) disc.state |= (1 << MD_DISK_WRITEMOSTLY); if (tst->ss->external) { /* add a disk * to an external metadata container */ struct mdinfo new_mdi; struct mdinfo *sra; int container_fd; int devnum = fd2devnum(fd); int dfd; container_fd = open_dev_excl(devnum); if (container_fd < 0) { fprintf(stderr, Name ": add failed for %s:" " could not get exclusive access to container\n", dv->devname); tst->ss->free_super(tst); return 1; } dfd = dev_open(dv->devname, O_RDWR | O_EXCL|O_DIRECT); if (mdmon_running(tst->container_dev)) tst->update_tail = &tst->updates; if (tst->ss->add_to_super(tst, &disc, dfd, dv->devname)) { close(dfd); close(container_fd); return 1; } if (tst->update_tail) flush_metadata_updates(tst); else tst->ss->sync_metadata(tst); sra = sysfs_read(container_fd, -1, 0); if (!sra) { fprintf(stderr, Name ": add failed for %s: sysfs_read failed\n", dv->devname); close(container_fd); tst->ss->free_super(tst); return 1; } sra->array.level = LEVEL_CONTAINER; /* Need to set data_offset and component_size */ tst->ss->getinfo_super(tst, &new_mdi, NULL); new_mdi.disk.major = disc.major; new_mdi.disk.minor = disc.minor; new_mdi.recovery_start = 0; /* Make sure fds are closed as they are O_EXCL which * would block add_disk */ tst->ss->free_super(tst); if (sysfs_add_disk(sra, &new_mdi, 0) != 0) { fprintf(stderr, Name ": add new device to external metadata" " failed for %s\n", dv->devname); close(container_fd); sysfs_free(sra); return 1; } ping_monitor_by_id(devnum); sysfs_free(sra); close(container_fd); } else { tst->ss->free_super(tst); if (ioctl(fd, ADD_NEW_DISK, &disc)) { fprintf(stderr, Name ": add new device failed for %s as %d: %s\n", dv->devname, j, strerror(errno)); return 1; } } if (verbose >= 0) fprintf(stderr, Name ": added %s\n", dv->devname); break; case 'r': /* hot remove */ if (subarray) { fprintf(stderr, Name ": Cannot remove disks from a" " \'member\' array, perform this" " operation on the parent container\n"); if (sysfd >= 0) close(sysfd); return 1; } if (tst->ss->external) { /* To remove a device from a container, we must * check that it isn't in use in an array. * This involves looking in the 'holders' * directory - there must be just one entry, * the container. * To ensure that it doesn't get used as a * hold spare while we are checking, we * get an O_EXCL open on the container */ int dnum = fd2devnum(fd); lfd = open_dev_excl(dnum); if (lfd < 0) { fprintf(stderr, Name ": Cannot get exclusive access " " to container - odd\n"); if (sysfd >= 0) close(sysfd); return 1; } /* in the detached case it is not possible to * check if we are the unique holder, so just * rely on the 'detached' checks */ if (strcmp(dv->devname, "detached") == 0 || sysfd >= 0 || sysfs_unique_holder(dnum, stb.st_rdev)) /* pass */; else { fprintf(stderr, Name ": %s is %s, cannot remove.\n", dnprintable, errno == EEXIST ? "still in use": "not a member"); close(lfd); return 1; } } /* FIXME check that it is a current member */ if (sysfd >= 0) { /* device has been removed and we don't know * the major:minor number */ int n = write(sysfd, "remove", 6); if (n != 6) err = -1; else err = 0; close(sysfd); sysfd = -1; } else { err = ioctl(fd, HOT_REMOVE_DISK, (unsigned long)stb.st_rdev); if (err && errno == ENODEV) { /* Old kernels rejected this if no personality * registered */ struct mdinfo *sra = sysfs_read(fd, 0, GET_DEVS); struct mdinfo *dv = NULL; if (sra) dv = sra->devs; for ( ; dv ; dv=dv->next) if (dv->disk.major == (int)major(stb.st_rdev) && dv->disk.minor == (int)minor(stb.st_rdev)) break; if (dv) err = sysfs_set_str(sra, dv, "state", "remove"); else err = -1; if (sra) sysfs_free(sra); } } if (err) { fprintf(stderr, Name ": hot remove failed " "for %s: %s\n", dnprintable, strerror(errno)); if (lfd >= 0) close(lfd); return 1; } if (tst->ss->external) { /* * Before dropping our exclusive open we make an * attempt at preventing mdmon from seeing an * 'add' event before reconciling this 'remove' * event. */ char *name = devnum2devname(fd2devnum(fd)); if (!name) { fprintf(stderr, Name ": unable to get container name\n"); return 1; } ping_manager(name); free(name); } if (lfd >= 0) close(lfd); count++; if (verbose >= 0) fprintf(stderr, Name ": hot removed %s from %s\n", dnprintable, devname); break; case 'f': /* set faulty */ /* FIXME check current member */ if ((sysfd >= 0 && write(sysfd, "faulty", 6) != 6) || (sysfd < 0 && ioctl(fd, SET_DISK_FAULTY, (unsigned long) stb.st_rdev))) { fprintf(stderr, Name ": set device faulty failed for %s: %s\n", dnprintable, strerror(errno)); if (sysfd >= 0) close(sysfd); return 1; } if (sysfd >= 0) close(sysfd); sysfd = -1; count++; if (verbose >= 0) fprintf(stderr, Name ": set %s faulty in %s\n", dnprintable, devname); break; } } if (test && count == 0) return 2; return 0; }