/*
 * tools/lib/lvm_check_partitioned_dev.c
 *
 * Copyright (C) 1999 - 2001 Heinz Mauelshagen, Sistina Software
 *
 * March,July,August 1999
 * January,April 2000
 * February,November 2001
 * January 2002
 *
 *
 * This LVM library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This LVM library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this LVM library; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA
 *
 */

/*
 * Changelog
 *
 *    05/07/1999 - added 2.3.x new IDE majors
 *    20/08/1999 - added DAC960 majors
 *    31/01/2000 - use debug_enter()/debug_leave()
 *    26/04/2000 - added DASD major support
 *    07/02/2001 - added COMPAQ_CISS & SMART2 major support
 *    17/08/2001 - added dynamic major support (Stefan Bader)
 *    10/09/2001 - fixed support for *non* partitioned MDs (Luca Bera)
 *    27/11/2001 - added support for DAC960 in 2.2/2.4 (Adrian Phillips)
 *    27/11/2001 - fixed lvm_get_device_type() to avoid name/type mismatch
 *                 as that caused by the DAC960 fix (Andreas Dilger)
 *    16/01/2002 - ATARAID support
 *
 */

#include <liblvm.h>

int lvm_partition_count(dev_t);

const lvm_device_type_t lvm_get_device_type(dev_t device);

int lvm_check_partitioned_dev(dev_t st_rdev)
{
    int ret = FALSE;

    debug_enter("lvm_check_partitioned_dev -- CALLED\n");

    if (st_rdev == 0)
	ret = -LVM_EPARAM;
    else {
	switch (lvm_get_device_type(st_rdev)) {
	case LVM_DEVICE_TYPE_INVALID:
	case LVM_DEVICE_TYPE_NBD:
	case LVM_DEVICE_TYPE_LOOP:
	case LVM_DEVICE_TYPE_MD:
	case LVM_DEVICE_TYPE_ATARAID:
	    break;
	default:
	    ret = TRUE;
	}
    }

    debug_leave("lvm_check_partitioned_dev -- LEAVING with ret: %s\n",
		ret ? "TRUE" : "FALSE");
    return ret;
}


int lvm_check_whole_disk_dev(dev_t st_rdev)
{
    int ret = UNDEF;

    debug_enter("lvm_check_whole_disk_dev -- CALLED\n");

    if (st_rdev == 0) {
	ret = -LVM_EPARAM;
    } else if ((MINOR(st_rdev) % lvm_partition_count(st_rdev)) == 0) {
	ret = TRUE;
    }

    debug_leave("lvm_check_whole_disk_dev -- LEAVING with ret: %s\n",
		ret ? "TRUE" : "FALSE");
    return ret;
}


/* st_rdev must be valid if you are not checking the return value */
int lvm_partition_count(dev_t st_rdev)
{
    int ret = -LVM_EPARAM;

    debug_enter("lvm_partition_count -- CALLED for 0x%x\n", st_rdev);

    if (st_rdev != 0) {
	switch (lvm_get_device_type(st_rdev)) {
	case LVM_DEVICE_TYPE_DASD:
	    ret = 4;
	    break;
	case LVM_DEVICE_TYPE_DAC960:
	    ret = 8;
	    break;
	case LVM_DEVICE_TYPE_IDE:
	    ret = 64;
	    break;
	default:
	    ret = 16;
	}
    }

    debug_leave("lvm_partition_count -- LEAVING with ret: %d\n", ret);
    return ret;
}


int lvm_check_extended_partition(dev_t st_rdev)
{
    int ret = FALSE;

    debug_enter("lvm_check_extended_partition -- CALLED\n");

    if (st_rdev == 0)
	ret = -LVM_EPARAM;
    else if (MINOR(st_rdev) % lvm_partition_count(st_rdev) > 4)
	ret = TRUE;

    debug_leave("lvm_check_extended_partition -- LEAVING with ret: %d\n", ret);
    return ret;
}

/*
 * Given the device number, find out the type of a block device by looking
 * in /proc/devices and matching the major numbers.
 */
const lvm_device_type_t lvm_get_device_type(dev_t device)
{
    char line[80];
    struct devname_type {
	char		 *name;
	lvm_device_type_t type;
    } device_names[] = {
	{ "ide", LVM_DEVICE_TYPE_IDE },	  /* IDE disk			     */
	{ "sd", LVM_DEVICE_TYPE_SD },	  /* SCSI disk			     */
	{ "md", LVM_DEVICE_TYPE_MD },	  /* Multiple Disk driver (SoftRAID) */
	{ "loop", LVM_DEVICE_TYPE_LOOP }, /* Loop device		     */
	{ "dasd", LVM_DEVICE_TYPE_DASD }, /* DASD disk (IBM S/390, zSeries)  */
	{ "rd", LVM_DEVICE_TYPE_DAC960 }, /* DAC960 (2.2 kernel name)	     */
	{ "dac960", LVM_DEVICE_TYPE_DAC960 },/* DAC960 (2.4 kernel name)     */
	{ "nbd", LVM_DEVICE_TYPE_NBD },	  /* Network Block Device	     */
	{ "ida", LVM_DEVICE_TYPE_COMPAQ_SMART2 },/* Compaq SMART2	     */
	{ "cciss", LVM_DEVICE_TYPE_COMPAQ_CISS },/* Compaq CISS array	     */
	{ "ubd", LVM_DEVICE_TYPE_UBD },	  /* User-mode virtual block device  */
	{ "ataraid", LVM_DEVICE_TYPE_ATARAID }, /* ATA Raid                  */
	{ "i2o_block", LVM_DEVICE_TYPE_I2O }, /* i2o disk */
	{ NULL, LVM_DEVICE_TYPE_INVALID } /* Device not recognized	     */
    };
    FILE *procdevices = NULL;
    lvm_device_type_t ret = LVM_DEVICE_TYPE_INVALID;

    debug_enter("lvm_get_device_type called\n");

    if ((procdevices = fopen("/proc/devices", "r")) != NULL) {
	int seek_major = MAJOR(device);
	int blocksection = 0;

	while (fgets(line, 80, procdevices) != NULL) {
	    char devname[NAME_LEN];
	    unsigned line_major;

	    if (memcmp(line, "Block", 5) == 0) {
		blocksection = 1;
		continue;
	    }
	    /* We only want block devices ... */
	    if (!blocksection)
		continue;

	    if (sscanf(line, " %u %s", &line_major, devname) != 2)
		    continue;

	    if (line_major == seek_major) {
		int j;
		for (j = 0; device_names[j].name != NULL; j++) {
		    if (strncmp(device_names[j].name, devname,
				strlen(device_names[j].name)) == 0) {
			    ret = device_names[j].type;
			    break;
		    }
		}
		break;
	    }
	}
	fclose(procdevices);
    }
    debug_leave("lvm_get_device_type leaving with %d\n", ret);
    return (ret);
}
