/* * atari partitions parsing code * * Copyright (C) 2018 Vaclav Dolezal * * This file may be redistributed under the terms of the * GNU Lesser General Public License. * * Based on Linux kernel implementation and atari-fdisk */ #include #include #include #include #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) #define IS_PARTDEF_VALID(partdef, hdsize) \ ( \ (partdef).flags & 1 && \ isalnum((partdef).id[0]) && \ isalnum((partdef).id[1]) && \ isalnum((partdef).id[2]) && \ be32_to_cpu((partdef).start) <= (hdsize) && \ be32_to_cpu((partdef).start) + \ be32_to_cpu((partdef).size) <= (hdsize) \ ) 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 i = 0; int rc; x0start = xstart = be32_to_cpu(part->start); while (1) { struct atari_rootsector *xrs; 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)) 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; off_t hdsize; /* Atari partition is not defined for other sector sizes */ if (blkid_probe_get_sectorsize(pr) != 512) goto nothing; rs = (struct atari_rootsector *) blkid_probe_get_sector(pr, 0); if (!rs) { if (errno) return -errno; goto nothing; } hdsize = blkid_probe_get_size(pr) / 512; /* Look for validly looking primary partition */ for (i = 0; ; i++) { if (i >= ARRAY_SIZE(rs->part)) goto nothing; if (IS_PARTDEF_VALID(rs->part[i], hdsize)) { 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]); break; } } 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 };