Пример #1
0
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));
}
Пример #2
0
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());
}
Пример #3
0
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);
}
Пример #4
0
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;
}
Пример #5
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);
}
Пример #6
0
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;
}