(root)/
util-linux-2.39/
libblkid/
src/
partitions/
dos.c
       1  /*
       2   * MS-DOS partition parsing code
       3   *
       4   * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
       5   *
       6   * This file may be redistributed under the terms of the
       7   * GNU Lesser General Public License.
       8   *
       9   * Inspired by fdisk, partx, Linux kernel and libparted.
      10   */
      11  #include <stdio.h>
      12  #include <string.h>
      13  #include <stdlib.h>
      14  #include <stdint.h>
      15  
      16  #include "partitions.h"
      17  #include "superblocks/superblocks.h"
      18  #include "aix.h"
      19  
      20  /* see superblocks/vfat.c */
      21  extern int blkid_probe_is_vfat(blkid_probe pr);
      22  /* see superblocks/exfat.c */
      23  extern int blkid_probe_is_exfat(blkid_probe pr);
      24  
      25  static const struct dos_subtypes {
      26  	unsigned char type;
      27  	const struct blkid_idinfo *id;
      28  } dos_nested[] = {
      29  	{ MBR_FREEBSD_PARTITION, &bsd_pt_idinfo },
      30  	{ MBR_NETBSD_PARTITION, &bsd_pt_idinfo },
      31  	{ MBR_OPENBSD_PARTITION, &bsd_pt_idinfo },
      32  	{ MBR_UNIXWARE_PARTITION, &unixware_pt_idinfo },
      33  	{ MBR_SOLARIS_X86_PARTITION, &solaris_x86_pt_idinfo },
      34  	{ MBR_MINIX_PARTITION, &minix_pt_idinfo }
      35  };
      36  
      37  static inline int is_extended(struct dos_partition *p)
      38  {
      39  	return (p->sys_ind == MBR_DOS_EXTENDED_PARTITION ||
      40  		p->sys_ind == MBR_W95_EXTENDED_PARTITION ||
      41  		p->sys_ind == MBR_LINUX_EXTENDED_PARTITION);
      42  }
      43  
      44  static int parse_dos_extended(blkid_probe pr, blkid_parttable tab,
      45  		uint32_t ex_start, uint32_t ex_size, int ssf)
      46  {
      47  	blkid_partlist ls = blkid_probe_get_partlist(pr);
      48  	uint32_t cur_start = ex_start, cur_size = ex_size;
      49  	unsigned char *data;
      50  	int ct_nodata = 0;	/* count ext.partitions without data partitions */
      51  	int i;
      52  
      53  	DBG(LOWPROBE, ul_debug("parse EBR [start=%d, size=%d]", ex_start/ssf, ex_size/ssf));
      54  	if (ex_start == 0) {
      55  		DBG(LOWPROBE, ul_debug("Bad offset in primary extended partition -- ignore"));
      56  		return 0;
      57  	}
      58  
      59  	while (1) {
      60  		struct dos_partition *p, *p0;
      61  		uint32_t start = 0, size;
      62  
      63  		if (++ct_nodata > 100)
      64  			return BLKID_PROBE_OK;
      65  		data = blkid_probe_get_sector(pr, cur_start);
      66  		if (!data) {
      67  			if (errno)
      68  				return -errno;
      69  			goto leave;	/* malformed partition? */
      70  		}
      71  
      72  		if (!mbr_is_valid_magic(data))
      73  			goto leave;
      74  
      75  		p0 = mbr_get_partition(data, 0);
      76  
      77  		/* Usually, the first entry is the real data partition,
      78  		 * the 2nd entry is the next extended partition, or empty,
      79  		 * and the 3rd and 4th entries are unused.
      80  		 * However, DRDOS sometimes has the extended partition as
      81  		 * the first entry (when the data partition is empty),
      82  		 * and OS/2 seems to use all four entries.
      83  		 * -- Linux kernel fs/partitions/dos.c
      84  		 *
      85  		 * See also http://en.wikipedia.org/wiki/Extended_boot_record
      86  		 */
      87  
      88  		/* Parse data partition */
      89  		for (p = p0, i = 0; i < 4; i++, p++) {
      90  			uint32_t abs_start;
      91  			blkid_partition par;
      92  
      93  			/* the start is relative to the parental ext.partition */
      94  			start = dos_partition_get_start(p) * ssf;
      95  			size = dos_partition_get_size(p) * ssf;
      96  			abs_start = cur_start + start;	/* absolute start */
      97  
      98  			if (!size || is_extended(p))
      99  				continue;
     100  			if (i >= 2) {
     101  				/* extra checks to detect real data on
     102  				 * 3rd and 4th entries */
     103  				if (start + size > cur_size)
     104  					continue;
     105  				if (abs_start < ex_start)
     106  					continue;
     107  				if (abs_start + size > ex_start + ex_size)
     108  					continue;
     109  			}
     110  
     111  			/* Avoid recursive non-empty links, see ct_nodata counter */
     112  			if (blkid_partlist_get_partition_by_start(ls, abs_start)) {
     113  				DBG(LOWPROBE, ul_debug("#%d: EBR duplicate data partition [abs start=%u] -- ignore",
     114  							i + 1, abs_start));
     115  				continue;
     116  			}
     117  
     118  			par = blkid_partlist_add_partition(ls, tab, abs_start, size);
     119  			if (!par)
     120  				return -ENOMEM;
     121  
     122  			blkid_partition_set_type(par, p->sys_ind);
     123  			blkid_partition_set_flags(par, p->boot_ind);
     124  			blkid_partition_gen_uuid(par);
     125  			ct_nodata = 0;
     126  		}
     127  		/* The first nested ext.partition should be a link to the next
     128  		 * logical partition. Everything other (recursive ext.partitions)
     129  		 * is junk.
     130  		 */
     131  		for (p = p0, i = 0; i < 4; i++, p++) {
     132  			start = dos_partition_get_start(p) * ssf;
     133  			size = dos_partition_get_size(p) * ssf;
     134  
     135  			if (size && is_extended(p)) {
     136  				if (start == 0)
     137  					DBG(LOWPROBE, ul_debug("#%d: EBR link offset is zero -- ignore", i + 1));
     138  				else
     139  					break;
     140  			}
     141  		}
     142  		if (i == 4)
     143  			goto leave;
     144  
     145  		cur_start = ex_start + start;
     146  		cur_size = size;
     147  	}
     148  leave:
     149  	return BLKID_PROBE_OK;
     150  }
     151  
     152  static inline int is_lvm(blkid_probe pr)
     153  {
     154  	struct blkid_prval *v = __blkid_probe_lookup_value(pr, "TYPE");
     155  
     156  	return (v && v->data && strcmp((char *) v->data, "LVM2_member") == 0);
     157  }
     158  
     159  static inline int is_empty_mbr(unsigned char *mbr)
     160  {
     161  	struct dos_partition *p = mbr_get_partition(mbr, 0);
     162  	int i, nparts = 0;
     163  
     164  	for (i = 0; i < 4; i++) {
     165  		if (dos_partition_get_size(p) > 0)
     166  			nparts++;
     167  		p++;
     168  	}
     169  
     170  	return nparts == 0;
     171  }
     172  
     173  static int probe_dos_pt(blkid_probe pr,
     174  		const struct blkid_idmag *mag __attribute__((__unused__)))
     175  {
     176  	int i;
     177  	int ssf;
     178  	blkid_parttable tab = NULL;
     179  	blkid_partlist ls;
     180  	struct dos_partition *p0, *p;
     181  	unsigned char *data;
     182  	uint32_t start, size, id;
     183  	char idstr[UUID_STR_LEN];
     184  
     185  
     186  	data = blkid_probe_get_sector(pr, 0);
     187  	if (!data) {
     188  		if (errno)
     189  			return -errno;
     190  		goto nothing;
     191  	}
     192  
     193  	/* ignore disks with AIX magic number -- for more details see aix.c */
     194  	if (memcmp(data, BLKID_AIX_MAGIC_STRING, BLKID_AIX_MAGIC_STRLEN) == 0)
     195  		goto nothing;
     196  
     197  	p0 = mbr_get_partition(data, 0);
     198  
     199  	/*
     200  	 * Reject PT where boot indicator is not 0 or 0x80.
     201  	 */
     202  	for (p = p0, i = 0; i < 4; i++, p++)
     203  		if (p->boot_ind != 0 && p->boot_ind != 0x80) {
     204  			DBG(LOWPROBE, ul_debug("missing boot indicator -- ignore"));
     205  			goto nothing;
     206  		}
     207  
     208  	/*
     209  	 * GPT uses valid MBR
     210  	 */
     211  	for (p = p0, i = 0; i < 4; i++, p++) {
     212  		if (p->sys_ind == MBR_GPT_PARTITION) {
     213  			DBG(LOWPROBE, ul_debug("probably GPT -- ignore"));
     214  			goto nothing;
     215  		}
     216  	}
     217  
     218  	/*
     219  	 * Now that the 55aa signature is present, this is probably
     220  	 * either the boot sector of a FAT filesystem or a DOS-type
     221  	 * partition table.
     222  	 */
     223  	if (blkid_probe_is_vfat(pr) == 1 || blkid_probe_is_exfat(pr) == 1) {
     224  		DBG(LOWPROBE, ul_debug("probably FAT -- ignore"));
     225  		goto nothing;
     226  	}
     227  
     228  	/* Another false positive is NTFS */
     229  	if (blkid_probe_is_ntfs(pr) == 1) {
     230  		DBG(LOWPROBE, ul_debug("probably NTFS -- ignore"));
     231  		goto nothing;
     232  	}
     233  
     234  	/*
     235  	 * Ugly exception, if the device contains a valid LVM physical volume
     236  	 * and empty MBR (=no partition defined) then it's LVM and MBR should
     237  	 * be ignored. Crazy people use it to boot from LVM devices.
     238  	 */
     239  	if (is_lvm(pr) && is_empty_mbr(data)) {
     240  		DBG(LOWPROBE, ul_debug("empty MBR on LVM device -- ignore"));
     241  		goto nothing;
     242  	}
     243  
     244  	blkid_probe_use_wiper(pr, MBR_PT_OFFSET, 512 - MBR_PT_OFFSET);
     245  
     246  	id = mbr_get_id(data);
     247  	if (id)
     248  		snprintf(idstr, sizeof(idstr), "%08x", id);
     249  
     250  	/*
     251  	 * Well, all checks pass, it's MS-DOS partition table
     252  	 */
     253  	if (blkid_partitions_need_typeonly(pr)) {
     254  		/* Non-binary interface -- caller does not ask for details
     255  		 * about partitions, just set generic variables only. */
     256  		if (id)
     257  			blkid_partitions_strcpy_ptuuid(pr, idstr);
     258  		return 0;
     259  	}
     260  
     261  	ls = blkid_probe_get_partlist(pr);
     262  	if (!ls)
     263  		goto nothing;
     264  
     265  	/* sector size factor (the start and size are in the real sectors, but
     266  	 * we need to convert all sizes to 512 logical sectors
     267  	 */
     268  	ssf = blkid_probe_get_sectorsize(pr) / 512;
     269  
     270  	/* allocate a new partition table */
     271  	tab = blkid_partlist_new_parttable(ls, "dos", MBR_PT_OFFSET);
     272  	if (!tab)
     273  		return -ENOMEM;
     274  
     275  	if (id)
     276  		blkid_parttable_set_id(tab, (unsigned char *) idstr);
     277  
     278  	/* Parse primary partitions */
     279  	for (p = p0, i = 0; i < 4; i++, p++) {
     280  		blkid_partition par;
     281  
     282  		start = dos_partition_get_start(p) * ssf;
     283  		size = dos_partition_get_size(p) * ssf;
     284  
     285  		if (!size) {
     286  			/* Linux kernel ignores empty partitions, but partno for
     287  			 * the empty primary partitions is not reused */
     288  			blkid_partlist_increment_partno(ls);
     289  			continue;
     290  		}
     291  		par = blkid_partlist_add_partition(ls, tab, start, size);
     292  		if (!par)
     293  			return -ENOMEM;
     294  
     295  		blkid_partition_set_type(par, p->sys_ind);
     296  		blkid_partition_set_flags(par, p->boot_ind);
     297  		blkid_partition_gen_uuid(par);
     298  	}
     299  
     300  	/* Linux uses partition numbers greater than 4
     301  	 * for all logical partition and all nested partition tables (bsd, ..)
     302  	 */
     303  	blkid_partlist_set_partno(ls, 5);
     304  
     305  	/* Parse logical partitions */
     306  	for (p = p0, i = 0; i < 4; i++, p++) {
     307  		start = dos_partition_get_start(p) * ssf;
     308  		size = dos_partition_get_size(p) * ssf;
     309  
     310  		if (!size)
     311  			continue;
     312  		if (is_extended(p) &&
     313  		    parse_dos_extended(pr, tab, start, size, ssf) == -1)
     314  			goto nothing;
     315  	}
     316  
     317  	/* Parse subtypes (nested partitions) on large disks */
     318  	if (!blkid_probe_is_tiny(pr)) {
     319  		int nparts = blkid_partlist_numof_partitions(ls);
     320  
     321  		DBG(LOWPROBE, ul_debug("checking for subtypes"));
     322  
     323  		for (i = 0; i < nparts; i++) {
     324  			size_t n;
     325  			int type;
     326  			blkid_partition pa = blkid_partlist_get_partition(ls, i);
     327  
     328  			if (pa == NULL
     329  			    || blkid_partition_get_size(pa) == 0
     330  			    || blkid_partition_is_extended(pa)
     331  			    || blkid_partition_is_logical(pa))
     332  				continue;
     333  
     334  			type = blkid_partition_get_type(pa);
     335  
     336  			for (n = 0; n < ARRAY_SIZE(dos_nested); n++) {
     337  				int rc;
     338  
     339  				if (dos_nested[n].type != type)
     340  					continue;
     341  
     342  				rc = blkid_partitions_do_subprobe(pr, pa,
     343  							dos_nested[n].id);
     344  				if (rc < 0)
     345  					return rc;
     346  				break;
     347  			}
     348  		}
     349  	}
     350  	return BLKID_PROBE_OK;
     351  
     352  nothing:
     353  	return BLKID_PROBE_NONE;
     354  }
     355  
     356  
     357  const struct blkid_idinfo dos_pt_idinfo =
     358  {
     359  	.name		= "dos",
     360  	.probefunc	= probe_dos_pt,
     361  	.magics		=
     362  	{
     363  		/* DOS master boot sector:
     364  		 *
     365  		 *     0 | Code Area
     366  		 *   440 | Optional Disk signature
     367  		 *   446 | Partition table
     368  		 *   510 | 0x55
     369  		 *   511 | 0xAA
     370  		 */
     371  		{ .magic = "\x55\xAA", .len = 2, .sboff = 510 },
     372  		{ NULL }
     373  	}
     374  };
     375