(root)/
util-linux-2.39/
libblkid/
src/
partitions/
atari.c
       1  /*
       2   * atari partitions parsing code
       3   *
       4   * Copyright (C) 2018 Vaclav Dolezal <vdolezal@redhat.com>
       5   *
       6   * This file may be redistributed under the terms of the
       7   * GNU Lesser General Public License.
       8   *
       9   * Based on Linux kernel implementation and atari-fdisk
      10   */
      11  
      12  #include <stdio.h>
      13  #include <string.h>
      14  #include <stdlib.h>
      15  #include <stdint.h>
      16  
      17  #include "partitions.h"
      18  
      19  struct atari_part_def {
      20  	/*
      21  	 * flags:
      22  	 * 0 (LSB): active
      23  	 * 1-6:     (reserved)
      24  	 * 7 (MSB): bootable
      25  	 */
      26  	unsigned char flags;
      27  	char id[3];
      28  	uint32_t start;
      29  	uint32_t size;
      30  } __attribute__((packed));
      31  
      32  struct atari_rootsector {
      33  	char unused0[0x156]; /* boot code */
      34  	struct atari_part_def icd_part[8]; /* ICD partition entries */
      35  	char unused1[0xc];
      36  	uint32_t hd_size;
      37  	struct atari_part_def part[4]; /* primary partition entries */
      38  	uint32_t bsl_start; /* bad sector list start */
      39  	uint32_t bsl_len; /* bad sector list length */
      40  	uint16_t checksum;
      41  } __attribute__((packed));
      42  
      43  
      44  /*
      45   * Generated using linux kernel ctype.{c,h}
      46   *
      47   * Since kernel uses isalnum() to detect whether it is Atari PT, we need same
      48   * definition of alnum character to be consistent with kernel.
      49   */
      50  static const unsigned char _linux_isalnum[] = {
      51  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
      52  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
      53  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
      54  1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
      55  0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      56  1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
      57  0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      58  1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
      59  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
      60  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
      61  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
      62  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
      63  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      64  1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,
      65  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
      66  1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1
      67  };
      68  
      69  static int linux_isalnum(unsigned char c) {
      70  	return _linux_isalnum[c];
      71  }
      72  
      73  #define isalnum linux_isalnum
      74  
      75  #define IS_ACTIVE(partdef) ((partdef).flags & 1)
      76  
      77  static int is_valid_dimension(uint32_t start, uint32_t size, uint32_t maxoff)
      78  {
      79  	uint64_t end = start + size;
      80  
      81  	return  end >= start
      82  		&& 0 < start && start <= maxoff
      83  		&& 0 < size && size <= maxoff
      84  		&& 0 < end && end <= maxoff;
      85  }
      86  
      87  static int is_valid_partition(struct atari_part_def *part, uint32_t maxoff)
      88  {
      89  	uint32_t start = be32_to_cpu(part->start),
      90  		 size = be32_to_cpu(part->size);
      91  
      92  	return (part->flags & 1)
      93  		&& isalnum(part->id[0])
      94  		&& isalnum(part->id[1])
      95  		&& isalnum(part->id[2])
      96  		&& is_valid_dimension(start, size, maxoff);
      97  }
      98  
      99  static int is_id_common(char *id)
     100  {
     101  	const char *ids[] = {"GEM", "BGM", "LNX", "SWP", "RAW", };
     102  	unsigned i;
     103  
     104  	for (i = 0; i < ARRAY_SIZE(ids); i++) {
     105  		if (!memcmp(ids[i], id, 3))
     106  			return 1;
     107  	}
     108  	return 0;
     109  }
     110  
     111  static int parse_partition(blkid_partlist ls, blkid_parttable tab,
     112  	struct atari_part_def *part, uint32_t offset)
     113  {
     114  	blkid_partition par;
     115  	uint32_t start;
     116  	uint32_t size;
     117  
     118  	start = be32_to_cpu(part->start) + offset;
     119  	size = be32_to_cpu(part->size);
     120  
     121  	if (blkid_partlist_get_partition_by_start(ls, start)) {
     122  		/* Don't increment partno for extended parts */
     123  		if (!offset)
     124  			blkid_partlist_increment_partno(ls);
     125  		return 0;
     126  	}
     127  
     128  	par = blkid_partlist_add_partition(ls, tab, start, size);
     129  	if (!par)
     130  		return -ENOMEM;
     131  
     132  	blkid_partition_set_type_string(par, (unsigned char *) part->id,
     133  					sizeof(part->id));
     134  	return 1;
     135  }
     136  
     137  /*
     138   * \return 1: OK, 0: bad format or -errno
     139   */
     140  static int parse_extended(blkid_probe pr, blkid_partlist ls,
     141  	blkid_parttable tab, struct atari_part_def *part)
     142  {
     143  	uint32_t x0start, xstart;
     144  	unsigned ct = 0, i = 0;
     145  	int rc;
     146  
     147  	x0start = xstart = be32_to_cpu(part->start);
     148  	while (1) {
     149  		struct atari_rootsector *xrs;
     150  
     151  		if (++ct > 100)
     152  			break;
     153  
     154  		xrs = (struct atari_rootsector *) blkid_probe_get_sector(pr, xstart);
     155  		if (!xrs) {
     156  			if (errno)
     157  				return -errno;
     158  			return 0;
     159  		}
     160  
     161  		/*
     162  		 * There must be data partition followed by reference to next
     163  		 * XGM or inactive entry.
     164  		 */
     165  		for (i=0; ; i++) {
     166  			if (i >= ARRAY_SIZE(xrs->part) - 1)
     167  				return 0;
     168  			if (IS_ACTIVE(xrs->part[i]))
     169  				break;
     170  		}
     171  
     172  		if (!memcmp(xrs->part[i].id, "XGM", 3))
     173  			return 0;
     174  
     175  		rc = parse_partition(ls, tab, &xrs->part[i], xstart);
     176  		if (rc <= 0)
     177  			return rc;
     178  
     179  		if (!IS_ACTIVE(xrs->part[i+1]))
     180  			break;
     181  
     182  		if (memcmp(xrs->part[i+1].id, "XGM", 3) != 0)
     183  			return 0;
     184  
     185  		xstart = x0start + be32_to_cpu(xrs->part[i+1].start);
     186  	}
     187  
     188  	return 1;
     189  }
     190  
     191  static int probe_atari_pt(blkid_probe pr,
     192  		const struct blkid_idmag *mag __attribute__((__unused__)))
     193  {
     194  	struct atari_rootsector *rs;
     195  
     196  	blkid_parttable tab = NULL;
     197  	blkid_partlist ls;
     198  
     199  	unsigned i;
     200  	int has_xgm = 0;
     201  	int rc = 0;
     202  	uint32_t rssize;	/* size in sectors from root sector */
     203  	uint64_t size;		/* size in sectors from system */
     204  
     205  	/* Atari partition is not defined for other sector sizes */
     206  	if (blkid_probe_get_sectorsize(pr) != 512)
     207  		goto nothing;
     208  
     209  	size = blkid_probe_get_size(pr) / 512;
     210  
     211  	/* Atari is not well defined to support large disks */
     212  	if (size > INT32_MAX)
     213  		goto nothing;
     214  
     215  	/* read root sector */
     216  	rs = (struct atari_rootsector *) blkid_probe_get_sector(pr, 0);
     217  	if (!rs) {
     218  		if (errno)
     219  			return -errno;
     220  		goto nothing;
     221  	}
     222  
     223  	rssize = be32_to_cpu(rs->hd_size);
     224  
     225  	/* check number of sectors stored in the root sector */
     226  	if (rssize < 2 || rssize > size)
     227  		goto nothing;
     228  
     229  	/* check list of bad blocks */
     230  	if ((rs->bsl_start || rs->bsl_len)
     231  	    && !is_valid_dimension(be32_to_cpu(rs->bsl_start),
     232  				   be32_to_cpu(rs->bsl_len),
     233  				   rssize))
     234  		goto nothing;
     235  
     236  	/*
     237  	 * At least one valid partition required
     238  	 */
     239  	for (i = 0; i < 4; i++) {
     240  		if (is_valid_partition(&rs->part[i], rssize)) {
     241  			if (blkid_probe_set_magic(pr,
     242  					offsetof(struct atari_rootsector, part[i]),
     243  					sizeof(rs->part[i].flags) + sizeof(rs->part[i].id),
     244  					(unsigned char *) &rs->part[i]))
     245  				goto err;
     246  			break;
     247  		}
     248  	}
     249  
     250  	if (i == 4)
     251  		goto nothing;
     252  
     253  	if (blkid_partitions_need_typeonly(pr))
     254  		/* caller does not ask for details about partitions */
     255  		return BLKID_PROBE_OK;
     256  
     257  	ls = blkid_probe_get_partlist(pr);
     258  	if (!ls)
     259  		goto nothing;
     260  
     261  	tab = blkid_partlist_new_parttable(ls, "atari", 0);
     262  	if (!tab)
     263  		goto err;
     264  
     265  	for (i = 0; i < ARRAY_SIZE(rs->part); i++) {
     266  		struct atari_part_def *p = &rs->part[i];
     267  
     268  		if (!IS_ACTIVE(*p)) {
     269  			blkid_partlist_increment_partno(ls);
     270  			continue;
     271  		}
     272  		if (!memcmp(p->id, "XGM", 3)) {
     273  			has_xgm = 1;
     274  			rc = parse_extended(pr, ls, tab, p);
     275  		} else {
     276  			rc = parse_partition(ls, tab, p, 0);
     277  		}
     278  		if (rc < 0)
     279  			return rc;
     280  	}
     281  
     282  	/* if there are no XGM partitions, we can try ICD format */
     283  	/* if first ICD partition ID is not valid, assume no ICD format */
     284  	if (!has_xgm && is_id_common(rs->icd_part[0].id)) {
     285  		for (i = 0; i < ARRAY_SIZE(rs->icd_part); i++) {
     286  			struct atari_part_def *p = &rs->icd_part[i];
     287  
     288  			if (!IS_ACTIVE(*p) || !is_id_common(p->id)) {
     289  				blkid_partlist_increment_partno(ls);
     290  				continue;
     291  			}
     292  
     293  			rc = parse_partition(ls, tab, p, 0);
     294  			if (rc < 0)
     295  				return rc;
     296  		}
     297  	}
     298  
     299  	return BLKID_PROBE_OK;
     300  
     301  nothing:
     302  	return BLKID_PROBE_NONE;
     303  err:
     304  	return -ENOMEM;
     305  }
     306  
     307  const struct blkid_idinfo atari_pt_idinfo =
     308  {
     309  	.name		= "atari",
     310  	.probefunc	= probe_atari_pt,
     311  	.magics		= BLKID_NONE_MAGIC
     312  };