]> git.ipfire.org Git - thirdparty/util-linux.git/blame - fdisks/fdiskdoslabel.c
build-sys: add -Werror to UL_WARN_ADD test
[thirdparty/util-linux.git] / fdisks / fdiskdoslabel.c
CommitLineData
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
9dea2923 16#define set_hsc(h,s,c,sector) { \
24cd580b
DB
17 s = sector % cxt->geom.sectors + 1; \
18 sector /= cxt->geom.sectors; \
19 h = sector % cxt->geom.heads; \
20 sector /= cxt->geom.heads; \
21 c = sector & 0xff; \
22 s |= (sector >> 2) & 0xc0; \
9dea2923
DB
23 }
24
e53ced85 25#define alignment_required (grain != cxt->sector_size)
9dea2923 26
96f817fb 27struct pte ptes[MAXIMUM_PARTS];
24def09d 28sector_t extended_offset;
96f817fb
KZ
29int ext_index;
30
e53ced85 31static int get_nonexisting_partition(struct fdisk_context *cxt, int warn, int max)
9dea2923
DB
32{
33 int pno = -1;
34 int i;
35 int dflt = 0;
36
37 for (i = 0; i < max; i++) {
38 struct pte *pe = &ptes[i];
39 struct partition *p = pe->part_table;
40
41 if (p && is_cleared_partition(p)) {
42 if (pno >= 0) {
43 dflt = pno + 1;
44 goto not_unique;
45 }
46 pno = i;
47 }
48 }
49 if (pno >= 0) {
50 printf(_("Selected partition %d\n"), pno+1);
51 return pno;
52 }
53 printf(_("All primary partitions have been defined already!\n"));
54 return -1;
55
56 not_unique:
e53ced85 57 return get_partition_dflt(cxt, warn, max, dflt);
9dea2923
DB
58}
59
60
e2ee9178 61/* Allocate a buffer and read a partition table sector */
24def09d 62static void read_pte(struct fdisk_context *cxt, int pno, sector_t offset)
e2ee9178
DB
63{
64 struct pte *pe = &ptes[pno];
65
66 pe->offset = offset;
e53ced85 67 pe->sectorbuffer = xmalloc(cxt->sector_size);
7737f698 68 read_sector(cxt, offset, pe->sectorbuffer);
e2ee9178
DB
69 pe->changed = 0;
70 pe->part_table = pe->ext_pointer = NULL;
71}
72
73static void dos_write_mbr_id(unsigned char *b, unsigned int id)
74{
75 store4_little_endian(&b[440], id);
76}
77
78static unsigned int dos_read_mbr_id(const unsigned char *b)
79{
80 return read4_little_endian(&b[440]);
81}
82
83static void clear_partition(struct partition *p)
84{
85 if (!p)
86 return;
87 p->boot_ind = 0;
88 p->head = 0;
89 p->sector = 0;
90 p->cyl = 0;
91 p->sys_ind = 0;
92 p->end_head = 0;
93 p->end_sector = 0;
94 p->end_cyl = 0;
95 set_start_sect(p,0);
96 set_nr_sects(p,0);
97}
98
e53ced85 99void dos_init(struct fdisk_context *cxt)
e2ee9178
DB
100{
101 int i;
102
103 disklabel = DOS_LABEL;
104 partitions = 4;
105 ext_index = 0;
106 extended_offset = 0;
107
108 for (i = 0; i < 4; i++) {
109 struct pte *pe = &ptes[i];
110
38b36353 111 pe->part_table = pt_offset(cxt->mbr, i);
e2ee9178
DB
112 pe->ext_pointer = NULL;
113 pe->offset = 0;
38b36353 114 pe->sectorbuffer = cxt->mbr;
e2ee9178
DB
115 pe->changed = 0;
116 }
117
24cd580b 118 warn_geometry(cxt);
e53ced85
DB
119 warn_limits(cxt);
120 warn_alignment(cxt);
e2ee9178
DB
121}
122
7737f698 123static void read_extended(struct fdisk_context *cxt, int ext)
e2ee9178
DB
124{
125 int i;
126 struct pte *pex;
127 struct partition *p, *q;
128
129 ext_index = ext;
130 pex = &ptes[ext];
131 pex->ext_pointer = pex->part_table;
132
133 p = pex->part_table;
134 if (!get_start_sect(p)) {
135 fprintf(stderr,
136 _("Bad offset in primary extended partition\n"));
137 return;
138 }
139
140 while (IS_EXTENDED (p->sys_ind)) {
141 struct pte *pe = &ptes[partitions];
142
143 if (partitions >= MAXIMUM_PARTS) {
144 /* This is not a Linux restriction, but
145 this program uses arrays of size MAXIMUM_PARTS.
146 Do not try to `improve' this test. */
147 struct pte *pre = &ptes[partitions-1];
148
149 fprintf(stderr,
150 _("Warning: omitting partitions after #%d.\n"
151 "They will be deleted "
152 "if you save this partition table.\n"),
153 partitions);
154 clear_partition(pre->ext_pointer);
155 pre->changed = 1;
156 return;
157 }
158
7737f698 159 read_pte(cxt, partitions, extended_offset + get_start_sect(p));
e2ee9178
DB
160
161 if (!extended_offset)
162 extended_offset = get_start_sect(p);
163
164 q = p = pt_offset(pe->sectorbuffer, 0);
165 for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) {
166 if (IS_EXTENDED (p->sys_ind)) {
167 if (pe->ext_pointer)
168 fprintf(stderr,
169 _("Warning: extra link "
170 "pointer in partition table"
171 " %d\n"), partitions + 1);
172 else
173 pe->ext_pointer = p;
174 } else if (p->sys_ind) {
175 if (pe->part_table)
176 fprintf(stderr,
177 _("Warning: ignoring extra "
178 "data in partition table"
179 " %d\n"), partitions + 1);
180 else
181 pe->part_table = p;
182 }
183 }
184
185 /* very strange code here... */
186 if (!pe->part_table) {
187 if (q != pe->ext_pointer)
188 pe->part_table = q;
189 else
190 pe->part_table = q + 1;
191 }
192 if (!pe->ext_pointer) {
193 if (q != pe->part_table)
194 pe->ext_pointer = q;
195 else
196 pe->ext_pointer = q + 1;
197 }
198
199 p = pe->ext_pointer;
200 partitions++;
201 }
202
203 /* remove empty links */
204 remove:
205 for (i = 4; i < partitions; i++) {
206 struct pte *pe = &ptes[i];
207
208 if (!get_nr_sects(pe->part_table) &&
209 (partitions > 5 || ptes[4].part_table->sys_ind)) {
210 printf(_("omitting empty partition (%d)\n"), i+1);
211 dos_delete_partition(i);
212 goto remove; /* numbering changed */
213 }
214 }
215}
216
38b36353 217void dos_print_mbr_id(struct fdisk_context *cxt)
e2ee9178 218{
38b36353 219 printf(_("Disk identifier: 0x%08x\n"), dos_read_mbr_id(cxt->mbr));
e2ee9178
DB
220}
221
e53ced85 222void create_doslabel(struct fdisk_context *cxt)
e2ee9178
DB
223{
224 unsigned int id;
225
226 /* random disk signature */
c544aa2c 227 random_get_bytes(&id, sizeof(id));
e2ee9178
DB
228
229 fprintf(stderr, _("Building a new DOS disklabel with disk identifier 0x%08x.\n"), id);
230
e53ced85 231 dos_init(cxt);
38b36353 232 fdisk_mbr_zeroize(cxt);
e2ee9178
DB
233 set_all_unchanged();
234 set_changed(0);
235
236 /* Generate an MBR ID for this disk */
38b36353 237 dos_write_mbr_id(cxt->mbr, id);
e2ee9178
DB
238
239 /* Put MBR signature */
38b36353 240 write_part_table_flag(cxt->mbr);
e2ee9178
DB
241}
242
38b36353 243void dos_set_mbr_id(struct fdisk_context *cxt)
e2ee9178
DB
244{
245 unsigned long new_id;
246 char *ep;
247 char ps[64];
248
249 snprintf(ps, sizeof ps, _("New disk identifier (current 0x%08x): "),
38b36353 250 dos_read_mbr_id(cxt->mbr));
e2ee9178
DB
251
252 if (read_chars(ps) == '\n')
253 return;
254
255 new_id = strtoul(line_ptr, &ep, 0);
256 if (*ep != '\n')
257 return;
258
38b36353 259 dos_write_mbr_id(cxt->mbr, new_id);
e2ee9178 260 MBRbuffer_changed = 1;
38b36353 261 dos_print_mbr_id(cxt);
e2ee9178
DB
262}
263
264void dos_delete_partition(int i)
265{
266 struct pte *pe = &ptes[i];
267 struct partition *p = pe->part_table;
268 struct partition *q = pe->ext_pointer;
269
270 /* Note that for the fifth partition (i == 4) we don't actually
271 decrement partitions. */
272
273 if (i < 4) {
274 if (IS_EXTENDED (p->sys_ind) && i == ext_index) {
275 partitions = 4;
276 ptes[ext_index].ext_pointer = NULL;
277 extended_offset = 0;
278 }
279 clear_partition(p);
280 } else if (!q->sys_ind && i > 4) {
281 /* the last one in the chain - just delete */
282 --partitions;
283 --i;
284 clear_partition(ptes[i].ext_pointer);
285 ptes[i].changed = 1;
286 } else {
287 /* not the last one - further ones will be moved down */
288 if (i > 4) {
289 /* delete this link in the chain */
290 p = ptes[i-1].ext_pointer;
291 *p = *q;
292 set_start_sect(p, get_start_sect(q));
293 set_nr_sects(p, get_nr_sects(q));
294 ptes[i-1].changed = 1;
295 } else if (partitions > 5) { /* 5 will be moved to 4 */
296 /* the first logical in a longer chain */
297 struct pte *pe = &ptes[5];
298
299 if (pe->part_table) /* prevent SEGFAULT */
300 set_start_sect(pe->part_table,
301 get_partition_start(pe) -
302 extended_offset);
303 pe->offset = extended_offset;
304 pe->changed = 1;
305 }
306
307 if (partitions > 5) {
308 partitions--;
309 while (i < partitions) {
310 ptes[i] = ptes[i+1];
311 i++;
312 }
313 } else
314 /* the only logical: clear only */
315 clear_partition(ptes[i].part_table);
316 }
317}
318
7737f698 319int check_dos_label(struct fdisk_context *cxt)
e2ee9178
DB
320{
321 int i;
322
38b36353 323 if (!valid_part_table_flag(cxt->mbr))
e2ee9178
DB
324 return 0;
325
e53ced85 326 dos_init(cxt);
e2ee9178
DB
327
328 for (i = 0; i < 4; i++) {
329 struct pte *pe = &ptes[i];
330
331 if (IS_EXTENDED (pe->part_table->sys_ind)) {
332 if (partitions != 4)
333 fprintf(stderr, _("Ignoring extra extended "
334 "partition %d\n"), i + 1);
335 else
7737f698 336 read_extended(cxt, i);
e2ee9178
DB
337 }
338 }
339
340 for (i = 3; i < partitions; i++) {
341 struct pte *pe = &ptes[i];
342
343 if (!valid_part_table_flag(pe->sectorbuffer)) {
344 fprintf(stderr,
345 _("Warning: invalid flag 0x%04x of partition "
346 "table %d will be corrected by w(rite)\n"),
347 part_table_flag(pe->sectorbuffer), i + 1);
348 pe->changed = 1;
349 }
350 }
351
352 return 1;
353}
354
355/*
356 * Avoid warning about DOS partitions when no DOS partition was changed.
357 * Here a heuristic "is probably dos partition".
358 * We might also do the opposite and warn in all cases except
359 * for "is probably nondos partition".
360 */
361int is_dos_partition(int t)
362{
363 return (t == 1 || t == 4 || t == 6 ||
364 t == 0x0b || t == 0x0c || t == 0x0e ||
365 t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 ||
366 t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 ||
367 t == 0xc1 || t == 0xc4 || t == 0xc6);
368}
9dea2923 369
e53ced85
DB
370static void set_partition(struct fdisk_context *cxt,
371 int i, int doext, sector_t start,
24def09d 372 sector_t stop, int sysid)
9dea2923
DB
373{
374 struct partition *p;
24def09d 375 sector_t offset;
9dea2923
DB
376
377 if (doext) {
378 p = ptes[i].ext_pointer;
379 offset = extended_offset;
380 } else {
381 p = ptes[i].part_table;
382 offset = ptes[i].offset;
383 }
384 p->boot_ind = 0;
385 p->sys_ind = sysid;
386 set_start_sect(p, start - offset);
387 set_nr_sects(p, stop - start + 1);
388
389 if (!doext)
e53ced85 390 print_partition_size(cxt, i + 1, start, stop, sysid);
9dea2923 391
24cd580b
DB
392 if (dos_compatible_flag && (start/(cxt->geom.sectors*cxt->geom.heads) > 1023))
393 start = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
9dea2923 394 set_hsc(p->head, p->sector, p->cyl, start);
24cd580b
DB
395 if (dos_compatible_flag && (stop/(cxt->geom.sectors*cxt->geom.heads) > 1023))
396 stop = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
9dea2923
DB
397 set_hsc(p->end_head, p->end_sector, p->end_cyl, stop);
398 ptes[i].changed = 1;
399}
400
24def09d
DB
401static sector_t get_unused_start(int part_n, sector_t start,
402 sector_t first[], sector_t last[])
9dea2923
DB
403{
404 int i;
405
406 for (i = 0; i < partitions; i++) {
24def09d 407 sector_t lastplusoff;
9dea2923
DB
408
409 if (start == ptes[i].offset)
410 start += sector_offset;
411 lastplusoff = last[i] + ((part_n < 4) ? 0 : sector_offset);
412 if (start >= first[i] && start <= lastplusoff)
413 start = lastplusoff + 1;
414 }
415
416 return start;
417}
418
e53ced85
DB
419static sector_t align_lba_in_range(struct fdisk_context *cxt,
420 sector_t lba, sector_t start, sector_t stop)
9dea2923 421{
e53ced85
DB
422 start = align_lba(cxt, start, ALIGN_UP);
423 stop = align_lba(cxt, stop, ALIGN_DOWN);
9dea2923 424
e53ced85 425 lba = align_lba(cxt, lba, ALIGN_NEAREST);
9dea2923
DB
426
427 if (lba < start)
428 return start;
429 else if (lba > stop)
430 return stop;
431 return lba;
432}
433
e53ced85 434void dos_add_partition(struct fdisk_context *cxt, int n, int sys)
9dea2923
DB
435{
436 char mesg[256]; /* 48 does not suffice in Japanese */
437 int i, read = 0;
438 struct partition *p = ptes[n].part_table;
439 struct partition *q = ptes[ext_index].part_table;
24def09d 440 sector_t start, stop = 0, limit, temp,
9dea2923
DB
441 first[partitions], last[partitions];
442
443 if (p && p->sys_ind) {
444 printf(_("Partition %d is already defined. Delete "
445 "it before re-adding it.\n"), n + 1);
446 return;
447 }
448 fill_bounds(first, last);
449 if (n < 4) {
450 start = sector_offset;
618882d6 451 if (display_in_cyl_units || !cxt->total_sectors)
24cd580b 452 limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
9dea2923 453 else
618882d6 454 limit = cxt->total_sectors - 1;
9dea2923
DB
455
456 if (limit > UINT_MAX)
457 limit = UINT_MAX;
458
459 if (extended_offset) {
460 first[ext_index] = extended_offset;
461 last[ext_index] = get_start_sect(q) +
462 get_nr_sects(q) - 1;
463 }
464 } else {
465 start = extended_offset + sector_offset;
466 limit = get_start_sect(q) + get_nr_sects(q) - 1;
467 }
468 if (display_in_cyl_units)
469 for (i = 0; i < partitions; i++)
470 first[i] = (cround(first[i]) - 1) * units_per_sector;
471
472 snprintf(mesg, sizeof(mesg), _("First %s"), str_units(SINGULAR));
473 do {
24def09d 474 sector_t dflt, aligned;
9dea2923
DB
475
476 temp = start;
477 dflt = start = get_unused_start(n, start, first, last);
478
479 /* the default sector should be aligned and unused */
480 do {
e53ced85 481 aligned = align_lba_in_range(cxt, dflt, dflt, limit);
9dea2923
DB
482 dflt = get_unused_start(n, aligned, first, last);
483 } while (dflt != aligned && dflt > aligned && dflt < limit);
484
485 if (dflt >= limit)
486 dflt = start;
487 if (start > limit)
488 break;
489 if (start >= temp+units_per_sector && read) {
490 printf(_("Sector %llu is already allocated\n"), temp);
491 temp = start;
492 read = 0;
493 }
494 if (!read && start == temp) {
24def09d 495 sector_t i = start;
9dea2923 496
e53ced85 497 start = read_int(cxt, cround(i), cround(dflt), cround(limit),
9dea2923
DB
498 0, mesg);
499 if (display_in_cyl_units) {
500 start = (start - 1) * units_per_sector;
501 if (start < i) start = i;
502 }
503 read = 1;
504 }
505 } while (start != temp || !read);
506 if (n > 4) { /* NOT for fifth partition */
507 struct pte *pe = &ptes[n];
508
509 pe->offset = start - sector_offset;
510 if (pe->offset == extended_offset) { /* must be corrected */
511 pe->offset++;
512 if (sector_offset == 1)
513 start++;
514 }
515 }
516
517 for (i = 0; i < partitions; i++) {
518 struct pte *pe = &ptes[i];
519
520 if (start < pe->offset && limit >= pe->offset)
521 limit = pe->offset - 1;
522 if (start < first[i] && limit >= first[i])
523 limit = first[i] - 1;
524 }
525 if (start > limit) {
526 printf(_("No free sectors available\n"));
527 if (n > 4)
528 partitions--;
529 return;
530 }
531 if (cround(start) == cround(limit)) {
532 stop = limit;
533 } else {
534 int is_suffix_used = 0;
535
536 snprintf(mesg, sizeof(mesg),
537 _("Last %1$s, +%2$s or +size{K,M,G}"),
538 str_units(SINGULAR), str_units(PLURAL));
539
e53ced85
DB
540 stop = read_int_with_suffix(cxt,
541 cround(start), cround(limit), cround(limit),
542 cround(start), mesg, &is_suffix_used);
9dea2923
DB
543 if (display_in_cyl_units) {
544 stop = stop * units_per_sector - 1;
545 if (stop >limit)
546 stop = limit;
547 }
548
549 if (is_suffix_used && alignment_required) {
550 /* the last sector has not been exactly requested (but
551 * defined by +size{K,M,G} convention), so be smart
552 * and align the end of the partition. The next
553 * partition will start at phy.block boundary.
554 */
e53ced85 555 stop = align_lba_in_range(cxt, stop, start, limit) - 1;
9dea2923
DB
556 if (stop > limit)
557 stop = limit;
558 }
559 }
560
e53ced85 561 set_partition(cxt, n, 0, start, stop, sys);
9dea2923 562 if (n > 4)
e53ced85 563 set_partition(cxt, n - 1, 1, ptes[n].offset, stop, EXTENDED);
9dea2923
DB
564
565 if (IS_EXTENDED (sys)) {
566 struct pte *pe4 = &ptes[4];
567 struct pte *pen = &ptes[n];
568
569 ext_index = n;
570 pen->ext_pointer = p;
571 pe4->offset = extended_offset = start;
e53ced85 572 pe4->sectorbuffer = xcalloc(1, cxt->sector_size);
9dea2923
DB
573 pe4->part_table = pt_offset(pe4->sectorbuffer, 0);
574 pe4->ext_pointer = pe4->part_table + 1;
575 pe4->changed = 1;
576 partitions = 5;
577 }
578}
579
e53ced85 580static void add_logical(struct fdisk_context *cxt)
9dea2923
DB
581{
582 if (partitions > 5 || ptes[4].part_table->sys_ind) {
583 struct pte *pe = &ptes[partitions];
584
e53ced85 585 pe->sectorbuffer = xcalloc(1, cxt->sector_size);
9dea2923
DB
586 pe->part_table = pt_offset(pe->sectorbuffer, 0);
587 pe->ext_pointer = pe->part_table + 1;
588 pe->offset = 0;
589 pe->changed = 1;
590 partitions++;
591 }
592 printf(_("Adding logical partition %d\n"), partitions);
e53ced85 593 dos_add_partition(cxt, partitions - 1, LINUX_NATIVE);
9dea2923
DB
594}
595
596/*
597 * Ask the user for new partition type information (logical, extended).
598 * This function calls the actual partition adding logic - dos_add_partition.
599 */
e53ced85 600void dos_new_partition(struct fdisk_context *cxt)
9dea2923
DB
601{
602 int i, free_primary = 0;
603
604 for (i = 0; i < 4; i++)
605 free_primary += !ptes[i].part_table->sys_ind;
606
607 if (!free_primary && partitions >= MAXIMUM_PARTS) {
608 printf(_("The maximum number of partitions has been created\n"));
609 return;
610 }
611
612 if (!free_primary) {
613 if (extended_offset) {
614 printf(_("All primary partitions are in use\n"));
e53ced85 615 add_logical(cxt);
9dea2923
DB
616 } else
617 printf(_("If you want to create more than four partitions, you must replace a\n"
618 "primary partition with an extended partition first.\n"));
619 } else if (partitions >= MAXIMUM_PARTS) {
620 printf(_("All logical partitions are in use\n"));
621 printf(_("Adding a primary partition\n"));
e53ced85 622 dos_add_partition(cxt, get_partition(cxt, 0, 4), LINUX_NATIVE);
9dea2923
DB
623 } else {
624 char c, dflt, line[LINE_LENGTH];
625
626 dflt = (free_primary == 1 && !extended_offset) ? 'e' : 'p';
627 snprintf(line, sizeof(line),
628 _("Partition type:\n"
629 " p primary (%d primary, %d extended, %d free)\n"
630 "%s\n"
631 "Select (default %c): "),
632 4 - (extended_offset ? 1 : 0) - free_primary, extended_offset ? 1 : 0, free_primary,
633 extended_offset ? _(" l logical (numbered from 5)") : _(" e extended"),
634 dflt);
635
636 c = tolower(read_chars(line));
637 if (c == '\n') {
638 c = dflt;
639 printf(_("Using default response %c\n"), c);
640 }
641 if (c == 'p') {
e53ced85 642 int i = get_nonexisting_partition(cxt, 0, 4);
9dea2923 643 if (i >= 0)
e53ced85 644 dos_add_partition(cxt, i, LINUX_NATIVE);
9dea2923
DB
645 return;
646 } else if (c == 'l' && extended_offset) {
e53ced85 647 add_logical(cxt);
9dea2923
DB
648 return;
649 } else if (c == 'e' && !extended_offset) {
e53ced85 650 int i = get_nonexisting_partition(cxt, 0, 4);
9dea2923 651 if (i >= 0)
e53ced85 652 dos_add_partition(cxt, i, EXTENDED);
9dea2923
DB
653 return;
654 } else
655 printf(_("Invalid partition type `%c'\n"), c);
656 }
657}
0dc13a38 658
7737f698 659void dos_write_table(struct fdisk_context *cxt)
0dc13a38
DB
660{
661 int i;
662
663 /* MBR (primary partitions) */
664 if (!MBRbuffer_changed) {
665 for (i = 0; i < 4; i++)
666 if (ptes[i].changed)
667 MBRbuffer_changed = 1;
668 }
669 if (MBRbuffer_changed) {
38b36353
DB
670 write_part_table_flag(cxt->mbr);
671 write_sector(cxt, 0, cxt->mbr);
0dc13a38
DB
672 }
673 /* EBR (logical partitions) */
674 for (i = 4; i < partitions; i++) {
675 struct pte *pe = &ptes[i];
676
677 if (pe->changed) {
678 write_part_table_flag(pe->sectorbuffer);
7737f698 679 write_sector(cxt, pe->offset, pe->sectorbuffer);
0dc13a38
DB
680 }
681 }
682}