]>
Commit | Line | Data |
---|---|---|
e2ee9178 DB |
1 | /* |
2 | * Many, many hands. | |
3 | * Specific DOS label file - Davidlohr Bueso <dave@gnu.org> | |
4 | */ | |
5 | ||
6 | #include <unistd.h> | |
9dea2923 | 7 | #include <ctype.h> |
e2ee9178 DB |
8 | |
9 | #include "nls.h" | |
10 | #include "xalloc.h" | |
11 | #include "randutils.h" | |
12 | #include "common.h" | |
13 | #include "fdisk.h" | |
14 | #include "fdiskdoslabel.h" | |
15 | ||
749af4b6 KZ |
16 | static struct fdisk_parttype dos_parttypes[] = { |
17 | #include "dos_part_types.h" | |
18 | }; | |
19 | ||
9dea2923 | 20 | #define set_hsc(h,s,c,sector) { \ |
24cd580b DB |
21 | s = sector % cxt->geom.sectors + 1; \ |
22 | sector /= cxt->geom.sectors; \ | |
23 | h = sector % cxt->geom.heads; \ | |
24 | sector /= cxt->geom.heads; \ | |
25 | c = sector & 0xff; \ | |
26 | s |= (sector >> 2) & 0xc0; \ | |
9dea2923 DB |
27 | } |
28 | ||
b29ce87b | 29 | #define alignment_required (cxt->grain != cxt->sector_size) |
9dea2923 | 30 | |
96f817fb | 31 | struct pte ptes[MAXIMUM_PARTS]; |
24def09d | 32 | sector_t extended_offset; |
96f817fb KZ |
33 | int ext_index; |
34 | ||
e53ced85 | 35 | static int get_nonexisting_partition(struct fdisk_context *cxt, int warn, int max) |
9dea2923 DB |
36 | { |
37 | int pno = -1; | |
38 | int i; | |
39 | int dflt = 0; | |
40 | ||
41 | for (i = 0; i < max; i++) { | |
42 | struct pte *pe = &ptes[i]; | |
43 | struct partition *p = pe->part_table; | |
44 | ||
45 | if (p && is_cleared_partition(p)) { | |
46 | if (pno >= 0) { | |
47 | dflt = pno + 1; | |
48 | goto not_unique; | |
49 | } | |
50 | pno = i; | |
51 | } | |
52 | } | |
53 | if (pno >= 0) { | |
54 | printf(_("Selected partition %d\n"), pno+1); | |
55 | return pno; | |
56 | } | |
57 | printf(_("All primary partitions have been defined already!\n")); | |
58 | return -1; | |
59 | ||
60 | not_unique: | |
e53ced85 | 61 | return get_partition_dflt(cxt, warn, max, dflt); |
9dea2923 DB |
62 | } |
63 | ||
64 | ||
e2ee9178 | 65 | /* Allocate a buffer and read a partition table sector */ |
24def09d | 66 | static void read_pte(struct fdisk_context *cxt, int pno, sector_t offset) |
e2ee9178 DB |
67 | { |
68 | struct pte *pe = &ptes[pno]; | |
69 | ||
70 | pe->offset = offset; | |
e53ced85 | 71 | pe->sectorbuffer = xmalloc(cxt->sector_size); |
7737f698 | 72 | read_sector(cxt, offset, pe->sectorbuffer); |
e2ee9178 DB |
73 | pe->changed = 0; |
74 | pe->part_table = pe->ext_pointer = NULL; | |
75 | } | |
76 | ||
87a97832 | 77 | static void mbr_set_id(unsigned char *b, unsigned int id) |
e2ee9178 DB |
78 | { |
79 | store4_little_endian(&b[440], id); | |
80 | } | |
81 | ||
87a97832 KZ |
82 | static void mbr_set_magic(unsigned char *b) |
83 | { | |
84 | b[510] = 0x55; | |
85 | b[511] = 0xaa; | |
86 | } | |
87 | ||
67188340 KZ |
88 | int mbr_is_valid_magic(unsigned char *b) |
89 | { | |
90 | return (b[510] == 0x55 && b[511] == 0xaa); | |
91 | } | |
92 | ||
87a97832 | 93 | static unsigned int mbr_get_id(const unsigned char *b) |
e2ee9178 DB |
94 | { |
95 | return read4_little_endian(&b[440]); | |
96 | } | |
97 | ||
98 | static void clear_partition(struct partition *p) | |
99 | { | |
100 | if (!p) | |
101 | return; | |
102 | p->boot_ind = 0; | |
103 | p->head = 0; | |
104 | p->sector = 0; | |
105 | p->cyl = 0; | |
106 | p->sys_ind = 0; | |
107 | p->end_head = 0; | |
108 | p->end_sector = 0; | |
109 | p->end_cyl = 0; | |
110 | set_start_sect(p,0); | |
111 | set_nr_sects(p,0); | |
112 | } | |
113 | ||
e53ced85 | 114 | void dos_init(struct fdisk_context *cxt) |
e2ee9178 DB |
115 | { |
116 | int i; | |
117 | ||
118 | disklabel = DOS_LABEL; | |
119 | partitions = 4; | |
120 | ext_index = 0; | |
121 | extended_offset = 0; | |
122 | ||
123 | for (i = 0; i < 4; i++) { | |
124 | struct pte *pe = &ptes[i]; | |
125 | ||
67987b47 | 126 | pe->part_table = pt_offset(cxt->firstsector, i); |
e2ee9178 DB |
127 | pe->ext_pointer = NULL; |
128 | pe->offset = 0; | |
67987b47 | 129 | pe->sectorbuffer = cxt->firstsector; |
e2ee9178 DB |
130 | pe->changed = 0; |
131 | } | |
132 | ||
24cd580b | 133 | warn_geometry(cxt); |
e53ced85 DB |
134 | warn_limits(cxt); |
135 | warn_alignment(cxt); | |
e2ee9178 DB |
136 | } |
137 | ||
43b382e6 KZ |
138 | static void dos_delete_partition( |
139 | struct fdisk_context *cxt __attribute__ ((__unused__)), | |
140 | int partnum) | |
61c4cb85 DB |
141 | { |
142 | struct pte *pe = &ptes[partnum]; | |
143 | struct partition *p = pe->part_table; | |
144 | struct partition *q = pe->ext_pointer; | |
145 | ||
146 | /* Note that for the fifth partition (partnum == 4) we don't actually | |
147 | decrement partitions. */ | |
148 | ||
149 | if (partnum < 4) { | |
150 | if (IS_EXTENDED (p->sys_ind) && partnum == ext_index) { | |
151 | partitions = 4; | |
152 | ptes[ext_index].ext_pointer = NULL; | |
153 | extended_offset = 0; | |
154 | } | |
155 | clear_partition(p); | |
156 | } else if (!q->sys_ind && partnum > 4) { | |
157 | /* the last one in the chain - just delete */ | |
158 | --partitions; | |
159 | --partnum; | |
160 | clear_partition(ptes[partnum].ext_pointer); | |
161 | ptes[partnum].changed = 1; | |
162 | } else { | |
163 | /* not the last one - further ones will be moved down */ | |
164 | if (partnum > 4) { | |
165 | /* delete this link in the chain */ | |
166 | p = ptes[partnum-1].ext_pointer; | |
167 | *p = *q; | |
168 | set_start_sect(p, get_start_sect(q)); | |
169 | set_nr_sects(p, get_nr_sects(q)); | |
170 | ptes[partnum-1].changed = 1; | |
171 | } else if (partitions > 5) { /* 5 will be moved to 4 */ | |
172 | /* the first logical in a longer chain */ | |
173 | struct pte *pe = &ptes[5]; | |
174 | ||
175 | if (pe->part_table) /* prevent SEGFAULT */ | |
176 | set_start_sect(pe->part_table, | |
177 | get_partition_start(pe) - | |
178 | extended_offset); | |
179 | pe->offset = extended_offset; | |
180 | pe->changed = 1; | |
181 | } | |
182 | ||
183 | if (partitions > 5) { | |
184 | partitions--; | |
185 | while (partnum < partitions) { | |
186 | ptes[partnum] = ptes[partnum+1]; | |
187 | partnum++; | |
188 | } | |
189 | } else | |
190 | /* the only logical: clear only */ | |
191 | clear_partition(ptes[partnum].part_table); | |
192 | } | |
193 | } | |
194 | ||
7737f698 | 195 | static void read_extended(struct fdisk_context *cxt, int ext) |
e2ee9178 DB |
196 | { |
197 | int i; | |
198 | struct pte *pex; | |
199 | struct partition *p, *q; | |
200 | ||
201 | ext_index = ext; | |
202 | pex = &ptes[ext]; | |
203 | pex->ext_pointer = pex->part_table; | |
204 | ||
205 | p = pex->part_table; | |
206 | if (!get_start_sect(p)) { | |
207 | fprintf(stderr, | |
208 | _("Bad offset in primary extended partition\n")); | |
209 | return; | |
210 | } | |
211 | ||
212 | while (IS_EXTENDED (p->sys_ind)) { | |
213 | struct pte *pe = &ptes[partitions]; | |
214 | ||
215 | if (partitions >= MAXIMUM_PARTS) { | |
216 | /* This is not a Linux restriction, but | |
217 | this program uses arrays of size MAXIMUM_PARTS. | |
218 | Do not try to `improve' this test. */ | |
219 | struct pte *pre = &ptes[partitions-1]; | |
220 | ||
221 | fprintf(stderr, | |
222 | _("Warning: omitting partitions after #%d.\n" | |
223 | "They will be deleted " | |
224 | "if you save this partition table.\n"), | |
225 | partitions); | |
226 | clear_partition(pre->ext_pointer); | |
227 | pre->changed = 1; | |
228 | return; | |
229 | } | |
230 | ||
7737f698 | 231 | read_pte(cxt, partitions, extended_offset + get_start_sect(p)); |
e2ee9178 DB |
232 | |
233 | if (!extended_offset) | |
234 | extended_offset = get_start_sect(p); | |
235 | ||
236 | q = p = pt_offset(pe->sectorbuffer, 0); | |
237 | for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) { | |
238 | if (IS_EXTENDED (p->sys_ind)) { | |
239 | if (pe->ext_pointer) | |
240 | fprintf(stderr, | |
241 | _("Warning: extra link " | |
242 | "pointer in partition table" | |
243 | " %d\n"), partitions + 1); | |
244 | else | |
245 | pe->ext_pointer = p; | |
246 | } else if (p->sys_ind) { | |
247 | if (pe->part_table) | |
248 | fprintf(stderr, | |
249 | _("Warning: ignoring extra " | |
250 | "data in partition table" | |
251 | " %d\n"), partitions + 1); | |
252 | else | |
253 | pe->part_table = p; | |
254 | } | |
255 | } | |
256 | ||
257 | /* very strange code here... */ | |
258 | if (!pe->part_table) { | |
259 | if (q != pe->ext_pointer) | |
260 | pe->part_table = q; | |
261 | else | |
262 | pe->part_table = q + 1; | |
263 | } | |
264 | if (!pe->ext_pointer) { | |
265 | if (q != pe->part_table) | |
266 | pe->ext_pointer = q; | |
267 | else | |
268 | pe->ext_pointer = q + 1; | |
269 | } | |
270 | ||
271 | p = pe->ext_pointer; | |
272 | partitions++; | |
273 | } | |
274 | ||
275 | /* remove empty links */ | |
276 | remove: | |
277 | for (i = 4; i < partitions; i++) { | |
278 | struct pte *pe = &ptes[i]; | |
279 | ||
280 | if (!get_nr_sects(pe->part_table) && | |
281 | (partitions > 5 || ptes[4].part_table->sys_ind)) { | |
282 | printf(_("omitting empty partition (%d)\n"), i+1); | |
61c4cb85 | 283 | dos_delete_partition(cxt, i); |
e2ee9178 DB |
284 | goto remove; /* numbering changed */ |
285 | } | |
286 | } | |
287 | } | |
288 | ||
38b36353 | 289 | void dos_print_mbr_id(struct fdisk_context *cxt) |
e2ee9178 | 290 | { |
67987b47 | 291 | printf(_("Disk identifier: 0x%08x\n"), mbr_get_id(cxt->firstsector)); |
e2ee9178 DB |
292 | } |
293 | ||
639f1d56 | 294 | static int dos_create_disklabel(struct fdisk_context *cxt) |
e2ee9178 DB |
295 | { |
296 | unsigned int id; | |
297 | ||
298 | /* random disk signature */ | |
c544aa2c | 299 | random_get_bytes(&id, sizeof(id)); |
e2ee9178 DB |
300 | |
301 | fprintf(stderr, _("Building a new DOS disklabel with disk identifier 0x%08x.\n"), id); | |
302 | ||
e53ced85 | 303 | dos_init(cxt); |
67987b47 | 304 | fdisk_zeroize_firstsector(cxt); |
e2ee9178 DB |
305 | set_all_unchanged(); |
306 | set_changed(0); | |
307 | ||
308 | /* Generate an MBR ID for this disk */ | |
67987b47 | 309 | mbr_set_id(cxt->firstsector, id); |
e2ee9178 DB |
310 | |
311 | /* Put MBR signature */ | |
67987b47 | 312 | mbr_set_magic(cxt->firstsector); |
a71601af | 313 | return 0; |
e2ee9178 DB |
314 | } |
315 | ||
38b36353 | 316 | void dos_set_mbr_id(struct fdisk_context *cxt) |
e2ee9178 DB |
317 | { |
318 | unsigned long new_id; | |
319 | char *ep; | |
320 | char ps[64]; | |
321 | ||
322 | snprintf(ps, sizeof ps, _("New disk identifier (current 0x%08x): "), | |
67987b47 | 323 | mbr_get_id(cxt->firstsector)); |
e2ee9178 DB |
324 | |
325 | if (read_chars(ps) == '\n') | |
326 | return; | |
327 | ||
328 | new_id = strtoul(line_ptr, &ep, 0); | |
329 | if (*ep != '\n') | |
330 | return; | |
331 | ||
67987b47 | 332 | mbr_set_id(cxt->firstsector, new_id); |
e2ee9178 | 333 | MBRbuffer_changed = 1; |
38b36353 | 334 | dos_print_mbr_id(cxt); |
e2ee9178 DB |
335 | } |
336 | ||
9a5e29e9 KZ |
337 | static void get_partition_table_geometry(struct fdisk_context *cxt, |
338 | unsigned int *ph, unsigned int *ps) | |
339 | { | |
67987b47 | 340 | unsigned char *bufp = cxt->firstsector; |
9a5e29e9 KZ |
341 | struct partition *p; |
342 | int i, h, s, hh, ss; | |
343 | int first = 1; | |
344 | int bad = 0; | |
345 | ||
346 | hh = ss = 0; | |
347 | for (i=0; i<4; i++) { | |
348 | p = pt_offset(bufp, i); | |
349 | if (p->sys_ind != 0) { | |
350 | h = p->end_head + 1; | |
351 | s = (p->end_sector & 077); | |
352 | if (first) { | |
353 | hh = h; | |
354 | ss = s; | |
355 | first = 0; | |
356 | } else if (hh != h || ss != s) | |
357 | bad = 1; | |
358 | } | |
359 | } | |
360 | ||
361 | if (!first && !bad) { | |
362 | *ph = hh; | |
363 | *ps = ss; | |
364 | } | |
365 | } | |
366 | ||
367 | ||
da4ea9f5 | 368 | static int dos_probe_label(struct fdisk_context *cxt) |
e2ee9178 DB |
369 | { |
370 | int i; | |
9a5e29e9 | 371 | unsigned int h = 0, s = 0; |
e2ee9178 | 372 | |
67987b47 | 373 | if (!mbr_is_valid_magic(cxt->firstsector)) |
e2ee9178 DB |
374 | return 0; |
375 | ||
e53ced85 | 376 | dos_init(cxt); |
e2ee9178 | 377 | |
9a5e29e9 KZ |
378 | get_partition_table_geometry(cxt, &h, &s); |
379 | if (h && s) { | |
380 | cxt->geom.heads = h; | |
381 | cxt->geom.sectors = s; | |
382 | } | |
383 | ||
e2ee9178 DB |
384 | for (i = 0; i < 4; i++) { |
385 | struct pte *pe = &ptes[i]; | |
386 | ||
387 | if (IS_EXTENDED (pe->part_table->sys_ind)) { | |
388 | if (partitions != 4) | |
389 | fprintf(stderr, _("Ignoring extra extended " | |
390 | "partition %d\n"), i + 1); | |
391 | else | |
7737f698 | 392 | read_extended(cxt, i); |
e2ee9178 DB |
393 | } |
394 | } | |
395 | ||
396 | for (i = 3; i < partitions; i++) { | |
397 | struct pte *pe = &ptes[i]; | |
398 | ||
67188340 | 399 | if (!mbr_is_valid_magic(pe->sectorbuffer)) { |
e2ee9178 DB |
400 | fprintf(stderr, |
401 | _("Warning: invalid flag 0x%04x of partition " | |
402 | "table %d will be corrected by w(rite)\n"), | |
403 | part_table_flag(pe->sectorbuffer), i + 1); | |
404 | pe->changed = 1; | |
405 | } | |
406 | } | |
407 | ||
408 | return 1; | |
409 | } | |
410 | ||
411 | /* | |
412 | * Avoid warning about DOS partitions when no DOS partition was changed. | |
413 | * Here a heuristic "is probably dos partition". | |
414 | * We might also do the opposite and warn in all cases except | |
415 | * for "is probably nondos partition". | |
416 | */ | |
02460b8a | 417 | static int is_dos_partition(int t) |
e2ee9178 DB |
418 | { |
419 | return (t == 1 || t == 4 || t == 6 || | |
420 | t == 0x0b || t == 0x0c || t == 0x0e || | |
421 | t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 || | |
422 | t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 || | |
423 | t == 0xc1 || t == 0xc4 || t == 0xc6); | |
424 | } | |
9dea2923 | 425 | |
e53ced85 DB |
426 | static void set_partition(struct fdisk_context *cxt, |
427 | int i, int doext, sector_t start, | |
24def09d | 428 | sector_t stop, int sysid) |
9dea2923 DB |
429 | { |
430 | struct partition *p; | |
24def09d | 431 | sector_t offset; |
9dea2923 DB |
432 | |
433 | if (doext) { | |
434 | p = ptes[i].ext_pointer; | |
435 | offset = extended_offset; | |
436 | } else { | |
437 | p = ptes[i].part_table; | |
438 | offset = ptes[i].offset; | |
439 | } | |
440 | p->boot_ind = 0; | |
441 | p->sys_ind = sysid; | |
442 | set_start_sect(p, start - offset); | |
443 | set_nr_sects(p, stop - start + 1); | |
444 | ||
445 | if (!doext) | |
e53ced85 | 446 | print_partition_size(cxt, i + 1, start, stop, sysid); |
9dea2923 | 447 | |
24cd580b DB |
448 | if (dos_compatible_flag && (start/(cxt->geom.sectors*cxt->geom.heads) > 1023)) |
449 | start = cxt->geom.heads*cxt->geom.sectors*1024 - 1; | |
9dea2923 | 450 | set_hsc(p->head, p->sector, p->cyl, start); |
24cd580b DB |
451 | if (dos_compatible_flag && (stop/(cxt->geom.sectors*cxt->geom.heads) > 1023)) |
452 | stop = cxt->geom.heads*cxt->geom.sectors*1024 - 1; | |
9dea2923 DB |
453 | set_hsc(p->end_head, p->end_sector, p->end_cyl, stop); |
454 | ptes[i].changed = 1; | |
455 | } | |
456 | ||
24def09d DB |
457 | static sector_t get_unused_start(int part_n, sector_t start, |
458 | sector_t first[], sector_t last[]) | |
9dea2923 DB |
459 | { |
460 | int i; | |
461 | ||
462 | for (i = 0; i < partitions; i++) { | |
24def09d | 463 | sector_t lastplusoff; |
9dea2923 DB |
464 | |
465 | if (start == ptes[i].offset) | |
466 | start += sector_offset; | |
467 | lastplusoff = last[i] + ((part_n < 4) ? 0 : sector_offset); | |
468 | if (start >= first[i] && start <= lastplusoff) | |
469 | start = lastplusoff + 1; | |
470 | } | |
471 | ||
472 | return start; | |
473 | } | |
474 | ||
e53ced85 DB |
475 | static sector_t align_lba_in_range(struct fdisk_context *cxt, |
476 | sector_t lba, sector_t start, sector_t stop) | |
9dea2923 | 477 | { |
e53ced85 DB |
478 | start = align_lba(cxt, start, ALIGN_UP); |
479 | stop = align_lba(cxt, stop, ALIGN_DOWN); | |
9dea2923 | 480 | |
e53ced85 | 481 | lba = align_lba(cxt, lba, ALIGN_NEAREST); |
9dea2923 DB |
482 | |
483 | if (lba < start) | |
484 | return start; | |
485 | else if (lba > stop) | |
486 | return stop; | |
487 | return lba; | |
488 | } | |
489 | ||
ed470672 | 490 | static void add_partition(struct fdisk_context *cxt, int n, struct fdisk_parttype *t) |
9dea2923 DB |
491 | { |
492 | char mesg[256]; /* 48 does not suffice in Japanese */ | |
ed470672 | 493 | int i, sys, read = 0; |
9dea2923 DB |
494 | struct partition *p = ptes[n].part_table; |
495 | struct partition *q = ptes[ext_index].part_table; | |
24def09d | 496 | sector_t start, stop = 0, limit, temp, |
9dea2923 DB |
497 | first[partitions], last[partitions]; |
498 | ||
ed470672 KZ |
499 | sys = t ? t->type : LINUX_NATIVE; |
500 | ||
9dea2923 DB |
501 | if (p && p->sys_ind) { |
502 | printf(_("Partition %d is already defined. Delete " | |
503 | "it before re-adding it.\n"), n + 1); | |
504 | return; | |
505 | } | |
506 | fill_bounds(first, last); | |
507 | if (n < 4) { | |
508 | start = sector_offset; | |
618882d6 | 509 | if (display_in_cyl_units || !cxt->total_sectors) |
24cd580b | 510 | limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1; |
9dea2923 | 511 | else |
618882d6 | 512 | limit = cxt->total_sectors - 1; |
9dea2923 DB |
513 | |
514 | if (limit > UINT_MAX) | |
515 | limit = UINT_MAX; | |
516 | ||
517 | if (extended_offset) { | |
518 | first[ext_index] = extended_offset; | |
519 | last[ext_index] = get_start_sect(q) + | |
520 | get_nr_sects(q) - 1; | |
521 | } | |
522 | } else { | |
523 | start = extended_offset + sector_offset; | |
524 | limit = get_start_sect(q) + get_nr_sects(q) - 1; | |
525 | } | |
526 | if (display_in_cyl_units) | |
527 | for (i = 0; i < partitions; i++) | |
528 | first[i] = (cround(first[i]) - 1) * units_per_sector; | |
529 | ||
530 | snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR)); | |
531 | do { | |
24def09d | 532 | sector_t dflt, aligned; |
9dea2923 DB |
533 | |
534 | temp = start; | |
535 | dflt = start = get_unused_start(n, start, first, last); | |
536 | ||
537 | /* the default sector should be aligned and unused */ | |
538 | do { | |
e53ced85 | 539 | aligned = align_lba_in_range(cxt, dflt, dflt, limit); |
9dea2923 DB |
540 | dflt = get_unused_start(n, aligned, first, last); |
541 | } while (dflt != aligned && dflt > aligned && dflt < limit); | |
542 | ||
543 | if (dflt >= limit) | |
544 | dflt = start; | |
545 | if (start > limit) | |
546 | break; | |
547 | if (start >= temp+units_per_sector && read) { | |
548 | printf(_("Sector %llu is already allocated\n"), temp); | |
549 | temp = start; | |
550 | read = 0; | |
551 | } | |
552 | if (!read && start == temp) { | |
24def09d | 553 | sector_t i = start; |
9dea2923 | 554 | |
e53ced85 | 555 | start = read_int(cxt, cround(i), cround(dflt), cround(limit), |
9dea2923 DB |
556 | 0, mesg); |
557 | if (display_in_cyl_units) { | |
558 | start = (start - 1) * units_per_sector; | |
559 | if (start < i) start = i; | |
560 | } | |
561 | read = 1; | |
562 | } | |
563 | } while (start != temp || !read); | |
564 | if (n > 4) { /* NOT for fifth partition */ | |
565 | struct pte *pe = &ptes[n]; | |
566 | ||
567 | pe->offset = start - sector_offset; | |
568 | if (pe->offset == extended_offset) { /* must be corrected */ | |
569 | pe->offset++; | |
570 | if (sector_offset == 1) | |
571 | start++; | |
572 | } | |
573 | } | |
574 | ||
575 | for (i = 0; i < partitions; i++) { | |
576 | struct pte *pe = &ptes[i]; | |
577 | ||
578 | if (start < pe->offset && limit >= pe->offset) | |
579 | limit = pe->offset - 1; | |
580 | if (start < first[i] && limit >= first[i]) | |
581 | limit = first[i] - 1; | |
582 | } | |
583 | if (start > limit) { | |
584 | printf(_("No free sectors available\n")); | |
585 | if (n > 4) | |
586 | partitions--; | |
587 | return; | |
588 | } | |
589 | if (cround(start) == cround(limit)) { | |
590 | stop = limit; | |
591 | } else { | |
592 | int is_suffix_used = 0; | |
593 | ||
594 | snprintf(mesg, sizeof(mesg), | |
595 | _("Last %1$s, +%2$s or +size{K,M,G}"), | |
596 | str_units(SINGULAR), str_units(PLURAL)); | |
597 | ||
e53ced85 DB |
598 | stop = read_int_with_suffix(cxt, |
599 | cround(start), cround(limit), cround(limit), | |
600 | cround(start), mesg, &is_suffix_used); | |
9dea2923 DB |
601 | if (display_in_cyl_units) { |
602 | stop = stop * units_per_sector - 1; | |
603 | if (stop >limit) | |
604 | stop = limit; | |
605 | } | |
606 | ||
607 | if (is_suffix_used && alignment_required) { | |
608 | /* the last sector has not been exactly requested (but | |
609 | * defined by +size{K,M,G} convention), so be smart | |
610 | * and align the end of the partition. The next | |
611 | * partition will start at phy.block boundary. | |
612 | */ | |
e53ced85 | 613 | stop = align_lba_in_range(cxt, stop, start, limit) - 1; |
9dea2923 DB |
614 | if (stop > limit) |
615 | stop = limit; | |
616 | } | |
617 | } | |
618 | ||
e53ced85 | 619 | set_partition(cxt, n, 0, start, stop, sys); |
9dea2923 | 620 | if (n > 4) |
e53ced85 | 621 | set_partition(cxt, n - 1, 1, ptes[n].offset, stop, EXTENDED); |
9dea2923 DB |
622 | |
623 | if (IS_EXTENDED (sys)) { | |
624 | struct pte *pe4 = &ptes[4]; | |
625 | struct pte *pen = &ptes[n]; | |
626 | ||
627 | ext_index = n; | |
628 | pen->ext_pointer = p; | |
629 | pe4->offset = extended_offset = start; | |
e53ced85 | 630 | pe4->sectorbuffer = xcalloc(1, cxt->sector_size); |
9dea2923 DB |
631 | pe4->part_table = pt_offset(pe4->sectorbuffer, 0); |
632 | pe4->ext_pointer = pe4->part_table + 1; | |
633 | pe4->changed = 1; | |
634 | partitions = 5; | |
635 | } | |
636 | } | |
637 | ||
e53ced85 | 638 | static void add_logical(struct fdisk_context *cxt) |
9dea2923 DB |
639 | { |
640 | if (partitions > 5 || ptes[4].part_table->sys_ind) { | |
641 | struct pte *pe = &ptes[partitions]; | |
642 | ||
e53ced85 | 643 | pe->sectorbuffer = xcalloc(1, cxt->sector_size); |
9dea2923 DB |
644 | pe->part_table = pt_offset(pe->sectorbuffer, 0); |
645 | pe->ext_pointer = pe->part_table + 1; | |
646 | pe->offset = 0; | |
647 | pe->changed = 1; | |
648 | partitions++; | |
649 | } | |
650 | printf(_("Adding logical partition %d\n"), partitions); | |
ed470672 | 651 | add_partition(cxt, partitions - 1, NULL); |
9dea2923 DB |
652 | } |
653 | ||
2ca61a61 DB |
654 | static int dos_verify_disklabel(struct fdisk_context *cxt) |
655 | { | |
656 | int i, j; | |
657 | sector_t total = 1, n_sectors = cxt->total_sectors; | |
658 | unsigned long long first[partitions], last[partitions]; | |
659 | struct partition *p; | |
660 | ||
661 | fill_bounds(first, last); | |
662 | for (i = 0; i < partitions; i++) { | |
663 | struct pte *pe = &ptes[i]; | |
664 | ||
665 | p = pe->part_table; | |
666 | if (p->sys_ind && !IS_EXTENDED (p->sys_ind)) { | |
667 | check_consistency(cxt, p, i); | |
668 | check_alignment(cxt, get_partition_start(pe), i); | |
669 | if (get_partition_start(pe) < first[i]) | |
670 | printf(_("Warning: bad start-of-data in " | |
671 | "partition %d\n"), i + 1); | |
672 | check(cxt, i + 1, p->end_head, p->end_sector, p->end_cyl, | |
673 | last[i]); | |
674 | total += last[i] + 1 - first[i]; | |
675 | for (j = 0; j < i; j++) | |
676 | if ((first[i] >= first[j] && first[i] <= last[j]) | |
677 | || ((last[i] <= last[j] && last[i] >= first[j]))) { | |
678 | printf(_("Warning: partition %d overlaps " | |
679 | "partition %d.\n"), j + 1, i + 1); | |
680 | total += first[i] >= first[j] ? | |
681 | first[i] : first[j]; | |
682 | total -= last[i] <= last[j] ? | |
683 | last[i] : last[j]; | |
684 | } | |
685 | } | |
686 | } | |
687 | ||
688 | if (extended_offset) { | |
689 | struct pte *pex = &ptes[ext_index]; | |
690 | sector_t e_last = get_start_sect(pex->part_table) + | |
691 | get_nr_sects(pex->part_table) - 1; | |
692 | ||
693 | for (i = 4; i < partitions; i++) { | |
694 | total++; | |
695 | p = ptes[i].part_table; | |
696 | if (!p->sys_ind) { | |
697 | if (i != 4 || i + 1 < partitions) | |
698 | printf(_("Warning: partition %d " | |
699 | "is empty\n"), i + 1); | |
700 | } | |
701 | else if (first[i] < extended_offset || | |
702 | last[i] > e_last) | |
703 | printf(_("Logical partition %d not entirely in " | |
704 | "partition %d\n"), i + 1, ext_index + 1); | |
705 | } | |
706 | } | |
707 | ||
708 | if (total > n_sectors) | |
709 | printf(_("Total allocated sectors %llu greater than the maximum" | |
710 | " %llu\n"), total, n_sectors); | |
711 | else if (total < n_sectors) | |
712 | printf(_("Remaining %lld unallocated %ld-byte sectors\n"), | |
713 | n_sectors - total, cxt->sector_size); | |
714 | ||
715 | return 0; | |
716 | } | |
717 | ||
9dea2923 DB |
718 | /* |
719 | * Ask the user for new partition type information (logical, extended). | |
0f639e54 DB |
720 | * This function calls the actual partition adding logic - add_partition. |
721 | * | |
722 | * API callback. | |
9dea2923 | 723 | */ |
43b382e6 KZ |
724 | static void dos_add_partition( |
725 | struct fdisk_context *cxt, | |
726 | int partnum __attribute__ ((__unused__)), | |
ed470672 | 727 | struct fdisk_parttype *t) |
9dea2923 DB |
728 | { |
729 | int i, free_primary = 0; | |
730 | ||
731 | for (i = 0; i < 4; i++) | |
732 | free_primary += !ptes[i].part_table->sys_ind; | |
733 | ||
734 | if (!free_primary && partitions >= MAXIMUM_PARTS) { | |
735 | printf(_("The maximum number of partitions has been created\n")); | |
736 | return; | |
737 | } | |
738 | ||
739 | if (!free_primary) { | |
740 | if (extended_offset) { | |
741 | printf(_("All primary partitions are in use\n")); | |
e53ced85 | 742 | add_logical(cxt); |
9dea2923 DB |
743 | } else |
744 | printf(_("If you want to create more than four partitions, you must replace a\n" | |
745 | "primary partition with an extended partition first.\n")); | |
746 | } else if (partitions >= MAXIMUM_PARTS) { | |
747 | printf(_("All logical partitions are in use\n")); | |
748 | printf(_("Adding a primary partition\n")); | |
ed470672 | 749 | add_partition(cxt, get_partition(cxt, 0, 4), t); |
9dea2923 DB |
750 | } else { |
751 | char c, dflt, line[LINE_LENGTH]; | |
752 | ||
753 | dflt = (free_primary == 1 && !extended_offset) ? 'e' : 'p'; | |
754 | snprintf(line, sizeof(line), | |
755 | _("Partition type:\n" | |
756 | " p primary (%d primary, %d extended, %d free)\n" | |
757 | "%s\n" | |
758 | "Select (default %c): "), | |
759 | 4 - (extended_offset ? 1 : 0) - free_primary, extended_offset ? 1 : 0, free_primary, | |
760 | extended_offset ? _(" l logical (numbered from 5)") : _(" e extended"), | |
761 | dflt); | |
762 | ||
763 | c = tolower(read_chars(line)); | |
764 | if (c == '\n') { | |
765 | c = dflt; | |
766 | printf(_("Using default response %c\n"), c); | |
767 | } | |
768 | if (c == 'p') { | |
e53ced85 | 769 | int i = get_nonexisting_partition(cxt, 0, 4); |
9dea2923 | 770 | if (i >= 0) |
ed470672 | 771 | add_partition(cxt, i, t); |
9dea2923 DB |
772 | return; |
773 | } else if (c == 'l' && extended_offset) { | |
e53ced85 | 774 | add_logical(cxt); |
9dea2923 DB |
775 | return; |
776 | } else if (c == 'e' && !extended_offset) { | |
e53ced85 | 777 | int i = get_nonexisting_partition(cxt, 0, 4); |
ed470672 KZ |
778 | if (i >= 0) { |
779 | t = fdisk_get_parttype_from_code(cxt, EXTENDED); | |
780 | add_partition(cxt, i, t); | |
781 | } | |
9dea2923 DB |
782 | return; |
783 | } else | |
784 | printf(_("Invalid partition type `%c'\n"), c); | |
785 | } | |
786 | } | |
0dc13a38 | 787 | |
fae7b1bc DB |
788 | static int write_sector(struct fdisk_context *cxt, sector_t secno, |
789 | unsigned char *buf) | |
0dc13a38 | 790 | { |
fae7b1bc DB |
791 | seek_sector(cxt, secno); |
792 | if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size) | |
793 | return -errno; | |
794 | return 0; | |
795 | } | |
796 | ||
797 | static int dos_write_disklabel(struct fdisk_context *cxt) | |
798 | { | |
799 | int i, rc = 0; | |
0dc13a38 DB |
800 | |
801 | /* MBR (primary partitions) */ | |
802 | if (!MBRbuffer_changed) { | |
803 | for (i = 0; i < 4; i++) | |
804 | if (ptes[i].changed) | |
805 | MBRbuffer_changed = 1; | |
806 | } | |
807 | if (MBRbuffer_changed) { | |
67987b47 | 808 | mbr_set_magic(cxt->firstsector); |
fae7b1bc DB |
809 | rc = write_sector(cxt, 0, cxt->firstsector); |
810 | if (rc) | |
811 | goto done; | |
0dc13a38 DB |
812 | } |
813 | /* EBR (logical partitions) */ | |
814 | for (i = 4; i < partitions; i++) { | |
815 | struct pte *pe = &ptes[i]; | |
816 | ||
817 | if (pe->changed) { | |
87a97832 | 818 | mbr_set_magic(pe->sectorbuffer); |
fae7b1bc DB |
819 | rc = write_sector(cxt, pe->offset, pe->sectorbuffer); |
820 | if (rc) | |
821 | goto done; | |
0dc13a38 DB |
822 | } |
823 | } | |
fae7b1bc DB |
824 | |
825 | done: | |
826 | return rc; | |
0dc13a38 | 827 | } |
b8855c86 | 828 | |
010186f2 KZ |
829 | static struct fdisk_parttype *dos_get_parttype(struct fdisk_context *cxt, int partnum) |
830 | { | |
831 | struct fdisk_parttype *t; | |
832 | struct partition *p; | |
833 | ||
834 | if (partnum >= partitions) | |
835 | return NULL; | |
836 | ||
837 | p = ptes[partnum].part_table; | |
838 | t = fdisk_get_parttype_from_code(cxt, p->sys_ind); | |
839 | if (!t) | |
840 | t = fdisk_new_unknown_parttype(p->sys_ind, NULL); | |
841 | return t; | |
842 | } | |
843 | ||
02460b8a KZ |
844 | static int dos_set_parttype(struct fdisk_context *cxt, int partnum, |
845 | struct fdisk_parttype *t) | |
846 | { | |
847 | struct partition *p; | |
848 | ||
849 | if (partnum >= partitions || !t || t->type > UINT8_MAX) | |
850 | return -EINVAL; | |
851 | ||
852 | p = ptes[partnum].part_table; | |
853 | if (t->type == p->sys_ind) | |
854 | return 0; | |
855 | ||
856 | if (IS_EXTENDED(p->sys_ind) || IS_EXTENDED(t->type)) { | |
857 | printf(_("\nYou cannot change a partition into an extended one " | |
858 | "or vice versa.\nDelete it first.\n\n")); | |
859 | return -EINVAL; | |
860 | } | |
861 | ||
862 | if (is_dos_partition(t->type) || is_dos_partition(p->sys_ind)) | |
863 | printf( | |
864 | _("\nWARNING: If you have created or modified any DOS 6.x" | |
865 | "partitions, please see the fdisk manual page for additional" | |
866 | "information.\n\n")); | |
867 | ||
868 | p->sys_ind = t->type; | |
869 | return 0; | |
870 | } | |
871 | ||
b8855c86 DB |
872 | const struct fdisk_label dos_label = |
873 | { | |
874 | .name = "dos", | |
749af4b6 | 875 | .parttypes = dos_parttypes, |
559d921e KZ |
876 | .nparttypes = ARRAY_SIZE(dos_parttypes), |
877 | ||
da4ea9f5 | 878 | .probe = dos_probe_label, |
fae7b1bc | 879 | .write = dos_write_disklabel, |
2ca61a61 | 880 | .verify = dos_verify_disklabel, |
639f1d56 | 881 | .create = dos_create_disklabel, |
0f639e54 | 882 | .part_add = dos_add_partition, |
61c4cb85 | 883 | .part_delete = dos_delete_partition, |
010186f2 | 884 | .part_get_type = dos_get_parttype, |
02460b8a | 885 | .part_set_type = dos_set_parttype, |
b8855c86 | 886 | }; |