diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 19:10:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 19:10:49 +0000 |
commit | cfe5e3905201349e9cf3f95d52ff4bd100bde37d (patch) | |
tree | d0baf160cbee3195249d095f85e52d20c21acf02 /libblkid/src/partitions/atari.c | |
parent | Initial commit. (diff) | |
download | util-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.tar.xz util-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.zip |
Adding upstream version 2.39.3.upstream/2.39.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libblkid/src/partitions/atari.c')
-rw-r--r-- | libblkid/src/partitions/atari.c | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/libblkid/src/partitions/atari.c b/libblkid/src/partitions/atari.c new file mode 100644 index 0000000..314f047 --- /dev/null +++ b/libblkid/src/partitions/atari.c @@ -0,0 +1,312 @@ +/* + * atari partitions parsing code + * + * Copyright (C) 2018 Vaclav Dolezal <vdolezal@redhat.com> + * + * This file may be redistributed under the terms of the + * GNU Lesser General Public License. + * + * Based on Linux kernel implementation and atari-fdisk + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#include "partitions.h" + +struct atari_part_def { + /* + * flags: + * 0 (LSB): active + * 1-6: (reserved) + * 7 (MSB): bootable + */ + unsigned char flags; + char id[3]; + uint32_t start; + uint32_t size; +} __attribute__((packed)); + +struct atari_rootsector { + char unused0[0x156]; /* boot code */ + struct atari_part_def icd_part[8]; /* ICD partition entries */ + char unused1[0xc]; + uint32_t hd_size; + struct atari_part_def part[4]; /* primary partition entries */ + uint32_t bsl_start; /* bad sector list start */ + uint32_t bsl_len; /* bad sector list length */ + uint16_t checksum; +} __attribute__((packed)); + + +/* + * Generated using linux kernel ctype.{c,h} + * + * Since kernel uses isalnum() to detect whether it is Atari PT, we need same + * definition of alnum character to be consistent with kernel. + */ +static const unsigned char _linux_isalnum[] = { +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, +0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, +0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1 +}; + +static int linux_isalnum(unsigned char c) { + return _linux_isalnum[c]; +} + +#define isalnum linux_isalnum + +#define IS_ACTIVE(partdef) ((partdef).flags & 1) + +static int is_valid_dimension(uint32_t start, uint32_t size, uint32_t maxoff) +{ + uint64_t end = start + size; + + return end >= start + && 0 < start && start <= maxoff + && 0 < size && size <= maxoff + && 0 < end && end <= maxoff; +} + +static int is_valid_partition(struct atari_part_def *part, uint32_t maxoff) +{ + uint32_t start = be32_to_cpu(part->start), + size = be32_to_cpu(part->size); + + return (part->flags & 1) + && isalnum(part->id[0]) + && isalnum(part->id[1]) + && isalnum(part->id[2]) + && is_valid_dimension(start, size, maxoff); +} + +static int is_id_common(char *id) +{ + const char *ids[] = {"GEM", "BGM", "LNX", "SWP", "RAW", }; + unsigned i; + + for (i = 0; i < ARRAY_SIZE(ids); i++) { + if (!memcmp(ids[i], id, 3)) + return 1; + } + return 0; +} + +static int parse_partition(blkid_partlist ls, blkid_parttable tab, + struct atari_part_def *part, uint32_t offset) +{ + blkid_partition par; + uint32_t start; + uint32_t size; + + start = be32_to_cpu(part->start) + offset; + size = be32_to_cpu(part->size); + + if (blkid_partlist_get_partition_by_start(ls, start)) { + /* Don't increment partno for extended parts */ + if (!offset) + blkid_partlist_increment_partno(ls); + return 0; + } + + par = blkid_partlist_add_partition(ls, tab, start, size); + if (!par) + return -ENOMEM; + + blkid_partition_set_type_string(par, (unsigned char *) part->id, + sizeof(part->id)); + return 1; +} + +/* + * \return 1: OK, 0: bad format or -errno + */ +static int parse_extended(blkid_probe pr, blkid_partlist ls, + blkid_parttable tab, struct atari_part_def *part) +{ + uint32_t x0start, xstart; + unsigned ct = 0, i = 0; + int rc; + + x0start = xstart = be32_to_cpu(part->start); + while (1) { + struct atari_rootsector *xrs; + + if (++ct > 100) + break; + + xrs = (struct atari_rootsector *) blkid_probe_get_sector(pr, xstart); + if (!xrs) { + if (errno) + return -errno; + return 0; + } + + /* + * There must be data partition followed by reference to next + * XGM or inactive entry. + */ + for (i=0; ; i++) { + if (i >= ARRAY_SIZE(xrs->part) - 1) + return 0; + if (IS_ACTIVE(xrs->part[i])) + break; + } + + if (!memcmp(xrs->part[i].id, "XGM", 3)) + return 0; + + rc = parse_partition(ls, tab, &xrs->part[i], xstart); + if (rc <= 0) + return rc; + + if (!IS_ACTIVE(xrs->part[i+1])) + break; + + if (memcmp(xrs->part[i+1].id, "XGM", 3) != 0) + return 0; + + xstart = x0start + be32_to_cpu(xrs->part[i+1].start); + } + + return 1; +} + +static int probe_atari_pt(blkid_probe pr, + const struct blkid_idmag *mag __attribute__((__unused__))) +{ + struct atari_rootsector *rs; + + blkid_parttable tab = NULL; + blkid_partlist ls; + + unsigned i; + int has_xgm = 0; + int rc = 0; + uint32_t rssize; /* size in sectors from root sector */ + uint64_t size; /* size in sectors from system */ + + /* Atari partition is not defined for other sector sizes */ + if (blkid_probe_get_sectorsize(pr) != 512) + goto nothing; + + size = blkid_probe_get_size(pr) / 512; + + /* Atari is not well defined to support large disks */ + if (size > INT32_MAX) + goto nothing; + + /* read root sector */ + rs = (struct atari_rootsector *) blkid_probe_get_sector(pr, 0); + if (!rs) { + if (errno) + return -errno; + goto nothing; + } + + rssize = be32_to_cpu(rs->hd_size); + + /* check number of sectors stored in the root sector */ + if (rssize < 2 || rssize > size) + goto nothing; + + /* check list of bad blocks */ + if ((rs->bsl_start || rs->bsl_len) + && !is_valid_dimension(be32_to_cpu(rs->bsl_start), + be32_to_cpu(rs->bsl_len), + rssize)) + goto nothing; + + /* + * At least one valid partition required + */ + for (i = 0; i < 4; i++) { + if (is_valid_partition(&rs->part[i], rssize)) { + if (blkid_probe_set_magic(pr, + offsetof(struct atari_rootsector, part[i]), + sizeof(rs->part[i].flags) + sizeof(rs->part[i].id), + (unsigned char *) &rs->part[i])) + goto err; + break; + } + } + + if (i == 4) + goto nothing; + + if (blkid_partitions_need_typeonly(pr)) + /* caller does not ask for details about partitions */ + return BLKID_PROBE_OK; + + ls = blkid_probe_get_partlist(pr); + if (!ls) + goto nothing; + + tab = blkid_partlist_new_parttable(ls, "atari", 0); + if (!tab) + goto err; + + for (i = 0; i < ARRAY_SIZE(rs->part); i++) { + struct atari_part_def *p = &rs->part[i]; + + if (!IS_ACTIVE(*p)) { + blkid_partlist_increment_partno(ls); + continue; + } + if (!memcmp(p->id, "XGM", 3)) { + has_xgm = 1; + rc = parse_extended(pr, ls, tab, p); + } else { + rc = parse_partition(ls, tab, p, 0); + } + if (rc < 0) + return rc; + } + + /* if there are no XGM partitions, we can try ICD format */ + /* if first ICD partition ID is not valid, assume no ICD format */ + if (!has_xgm && is_id_common(rs->icd_part[0].id)) { + for (i = 0; i < ARRAY_SIZE(rs->icd_part); i++) { + struct atari_part_def *p = &rs->icd_part[i]; + + if (!IS_ACTIVE(*p) || !is_id_common(p->id)) { + blkid_partlist_increment_partno(ls); + continue; + } + + rc = parse_partition(ls, tab, p, 0); + if (rc < 0) + return rc; + } + } + + return BLKID_PROBE_OK; + +nothing: + return BLKID_PROBE_NONE; +err: + return -ENOMEM; +} + +const struct blkid_idinfo atari_pt_idinfo = +{ + .name = "atari", + .probefunc = probe_atari_pt, + .magics = BLKID_NONE_MAGIC +}; |