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 };