]> git.ipfire.org Git - thirdparty/util-linux.git/blame - fdisks/fdiskdoslabel.c
fdisk: (dos) use ask API for new partition dialog
[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
0c5d095e
KZ
16
17/*
18 * in-memory fdisk GPT stuff
19 */
20struct fdisk_dos_label {
21 struct fdisk_label head; /* generic part */
852ce62b
KZ
22
23 unsigned int compatible : 1; /* is DOS compatible? */
0c5d095e
KZ
24};
25
26/*
27 * Partition types
28 */
749af4b6
KZ
29static struct fdisk_parttype dos_parttypes[] = {
30 #include "dos_part_types.h"
31};
32
9dea2923 33#define set_hsc(h,s,c,sector) { \
24cd580b
DB
34 s = sector % cxt->geom.sectors + 1; \
35 sector /= cxt->geom.sectors; \
36 h = sector % cxt->geom.heads; \
37 sector /= cxt->geom.heads; \
38 c = sector & 0xff; \
39 s |= (sector >> 2) & 0xc0; \
9dea2923
DB
40 }
41
5dfca634
KZ
42
43#define sector(s) ((s) & 0x3f)
44#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
45
cf3808e4 46#define alignment_required(_x) ((_x)->grain != (_x)->sector_size)
9dea2923 47
96f817fb 48struct pte ptes[MAXIMUM_PARTS];
24def09d 49sector_t extended_offset;
e3661531
KZ
50
51static size_t ext_index;
96f817fb 52
bddd84e7
KZ
53static int MBRbuffer_changed;
54
ec10aa67
KZ
55#define cround(c, n) (fdisk_context_use_cylinders(c) ? \
56 ((n) / fdisk_context_get_units_per_sector(c)) + 1 : (n))
3c88fb56
KZ
57
58static void warn_alignment(struct fdisk_context *cxt)
59{
60 if (nowarn)
61 return;
62
63 if (cxt->sector_size != cxt->phy_sector_size)
64 fprintf(stderr, _("\n"
65"The device presents a logical sector size that is smaller than\n"
66"the physical sector size. Aligning to a physical sector (or optimal\n"
67"I/O) size boundary is recommended, or performance may be impacted.\n"));
68
852ce62b 69 if (is_dos_compatible(cxt))
3c88fb56
KZ
70 fprintf(stderr, _("\n"
71"WARNING: DOS-compatible mode is deprecated. It's strongly recommended to\n"
72" switch off the mode (with command 'c')."));
73
ec10aa67 74 if (fdisk_context_use_cylinders(cxt))
3c88fb56
KZ
75 fprintf(stderr, _("\n"
76"WARNING: cylinders as display units are deprecated. Use command 'u' to\n"
77" change units to sectors.\n"));
78
79}
80
1bf3a034 81static int get_partition_unused_primary(struct fdisk_context *cxt)
9dea2923 82{
1bf3a034
KZ
83 size_t orgmax = cxt->label->nparts_max;
84 size_t n;
85 int rc;
9dea2923 86
1bf3a034
KZ
87 cxt->label->nparts_max = 4;
88 rc = fdisk_ask_partnum(cxt, &n, TRUE);
89 cxt->label->nparts_max = orgmax;
90
91 switch (rc) {
92 case 1:
93 fdisk_info(cxt, _("All primary partitions have been defined already"));
94 return -1;
95 case 0:
96 return n;
97 default:
98 return rc;
9dea2923 99 }
9dea2923
DB
100}
101
102
e2ee9178 103/* Allocate a buffer and read a partition table sector */
24def09d 104static void read_pte(struct fdisk_context *cxt, int pno, sector_t offset)
e2ee9178
DB
105{
106 struct pte *pe = &ptes[pno];
107
108 pe->offset = offset;
cc7b6e92 109 pe->sectorbuffer = xcalloc(1, cxt->sector_size);
21770662
KZ
110
111 if (read_sector(cxt, offset, pe->sectorbuffer) != 0)
112 fprintf(stderr, _("Failed to read extended partition table (offset=%jd)\n"),
113 (uintmax_t) offset);
e2ee9178
DB
114 pe->changed = 0;
115 pe->part_table = pe->ext_pointer = NULL;
116}
117
87a97832 118static void mbr_set_id(unsigned char *b, unsigned int id)
e2ee9178
DB
119{
120 store4_little_endian(&b[440], id);
121}
122
87a97832
KZ
123static void mbr_set_magic(unsigned char *b)
124{
125 b[510] = 0x55;
126 b[511] = 0xaa;
127}
128
67188340
KZ
129int mbr_is_valid_magic(unsigned char *b)
130{
131 return (b[510] == 0x55 && b[511] == 0xaa);
132}
133
87a97832 134static unsigned int mbr_get_id(const unsigned char *b)
e2ee9178
DB
135{
136 return read4_little_endian(&b[440]);
137}
138
139static void clear_partition(struct partition *p)
140{
141 if (!p)
142 return;
143 p->boot_ind = 0;
144 p->head = 0;
145 p->sector = 0;
146 p->cyl = 0;
147 p->sys_ind = 0;
148 p->end_head = 0;
149 p->end_sector = 0;
150 p->end_cyl = 0;
151 set_start_sect(p,0);
152 set_nr_sects(p,0);
153}
154
e53ced85 155void dos_init(struct fdisk_context *cxt)
e2ee9178 156{
e3661531 157 size_t i;
e2ee9178 158
e3661531 159 cxt->label->nparts_max = 4; /* default, unlimited number of logical */
e2ee9178
DB
160 ext_index = 0;
161 extended_offset = 0;
162
163 for (i = 0; i < 4; i++) {
164 struct pte *pe = &ptes[i];
165
67987b47 166 pe->part_table = pt_offset(cxt->firstsector, i);
e2ee9178
DB
167 pe->ext_pointer = NULL;
168 pe->offset = 0;
67987b47 169 pe->sectorbuffer = cxt->firstsector;
e2ee9178
DB
170 pe->changed = 0;
171 }
172
24cd580b 173 warn_geometry(cxt);
e53ced85
DB
174 warn_limits(cxt);
175 warn_alignment(cxt);
e2ee9178
DB
176}
177
9ffeb235 178static int dos_delete_partition(struct fdisk_context *cxt, size_t partnum)
61c4cb85
DB
179{
180 struct pte *pe = &ptes[partnum];
181 struct partition *p = pe->part_table;
182 struct partition *q = pe->ext_pointer;
183
184 /* Note that for the fifth partition (partnum == 4) we don't actually
185 decrement partitions. */
186
187 if (partnum < 4) {
e3661531
KZ
188 if (IS_EXTENDED(p->sys_ind) && partnum == ext_index) {
189 cxt->label->nparts_max = 4;
61c4cb85
DB
190 ptes[ext_index].ext_pointer = NULL;
191 extended_offset = 0;
192 }
76b86412 193 ptes[partnum].changed = 1;
61c4cb85
DB
194 clear_partition(p);
195 } else if (!q->sys_ind && partnum > 4) {
196 /* the last one in the chain - just delete */
e3661531 197 --cxt->label->nparts_max;
61c4cb85
DB
198 --partnum;
199 clear_partition(ptes[partnum].ext_pointer);
200 ptes[partnum].changed = 1;
201 } else {
202 /* not the last one - further ones will be moved down */
203 if (partnum > 4) {
204 /* delete this link in the chain */
205 p = ptes[partnum-1].ext_pointer;
206 *p = *q;
207 set_start_sect(p, get_start_sect(q));
208 set_nr_sects(p, get_nr_sects(q));
209 ptes[partnum-1].changed = 1;
e3661531 210 } else if (cxt->label->nparts_max > 5) { /* 5 will be moved to 4 */
61c4cb85 211 /* the first logical in a longer chain */
c9f63764 212 struct pte *pete = &ptes[5];
61c4cb85 213
c9f63764
SK
214 if (pete->part_table) /* prevent SEGFAULT */
215 set_start_sect(pete->part_table,
216 get_partition_start(pete) -
61c4cb85 217 extended_offset);
c9f63764
SK
218 pete->offset = extended_offset;
219 pete->changed = 1;
61c4cb85
DB
220 }
221
e3661531
KZ
222 if (cxt->label->nparts_max > 5) {
223 cxt->label->nparts_max--;
224 while (partnum < cxt->label->nparts_max) {
61c4cb85
DB
225 ptes[partnum] = ptes[partnum+1];
226 partnum++;
227 }
228 } else
229 /* the only logical: clear only */
230 clear_partition(ptes[partnum].part_table);
231 }
1f5eb51b 232
9ffeb235 233 fdisk_label_set_changed(cxt->label, 1);
1f5eb51b 234 return 0;
61c4cb85
DB
235}
236
7737f698 237static void read_extended(struct fdisk_context *cxt, int ext)
e2ee9178 238{
e3661531 239 size_t i;
e2ee9178
DB
240 struct pte *pex;
241 struct partition *p, *q;
242
243 ext_index = ext;
244 pex = &ptes[ext];
245 pex->ext_pointer = pex->part_table;
246
247 p = pex->part_table;
248 if (!get_start_sect(p)) {
249 fprintf(stderr,
250 _("Bad offset in primary extended partition\n"));
251 return;
252 }
253
254 while (IS_EXTENDED (p->sys_ind)) {
e3661531 255 struct pte *pe = &ptes[cxt->label->nparts_max];
e2ee9178 256
e3661531 257 if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
e2ee9178
DB
258 /* This is not a Linux restriction, but
259 this program uses arrays of size MAXIMUM_PARTS.
260 Do not try to `improve' this test. */
e3661531 261 struct pte *pre = &ptes[cxt->label->nparts_max - 1];
e2ee9178
DB
262
263 fprintf(stderr,
e3661531 264 _("Warning: omitting partitions after #%zd.\n"
e2ee9178
DB
265 "They will be deleted "
266 "if you save this partition table.\n"),
e3661531 267 cxt->label->nparts_max);
e2ee9178
DB
268 clear_partition(pre->ext_pointer);
269 pre->changed = 1;
270 return;
271 }
272
e3661531 273 read_pte(cxt, cxt->label->nparts_max, extended_offset + get_start_sect(p));
e2ee9178
DB
274
275 if (!extended_offset)
276 extended_offset = get_start_sect(p);
277
278 q = p = pt_offset(pe->sectorbuffer, 0);
279 for (i = 0; i < 4; i++, p++) if (get_nr_sects(p)) {
280 if (IS_EXTENDED (p->sys_ind)) {
281 if (pe->ext_pointer)
282 fprintf(stderr,
283 _("Warning: extra link "
284 "pointer in partition table"
e3661531 285 " %zd\n"), cxt->label->nparts_max + 1);
e2ee9178
DB
286 else
287 pe->ext_pointer = p;
288 } else if (p->sys_ind) {
289 if (pe->part_table)
290 fprintf(stderr,
291 _("Warning: ignoring extra "
292 "data in partition table"
e3661531 293 " %zd\n"), cxt->label->nparts_max + 1);
e2ee9178
DB
294 else
295 pe->part_table = p;
296 }
297 }
298
299 /* very strange code here... */
300 if (!pe->part_table) {
301 if (q != pe->ext_pointer)
302 pe->part_table = q;
303 else
304 pe->part_table = q + 1;
305 }
306 if (!pe->ext_pointer) {
307 if (q != pe->part_table)
308 pe->ext_pointer = q;
309 else
310 pe->ext_pointer = q + 1;
311 }
312
313 p = pe->ext_pointer;
e3661531 314 cxt->label->nparts_cur = ++cxt->label->nparts_max;
e2ee9178
DB
315 }
316
317 /* remove empty links */
318 remove:
e3661531 319 for (i = 4; i < cxt->label->nparts_max; i++) {
e2ee9178
DB
320 struct pte *pe = &ptes[i];
321
322 if (!get_nr_sects(pe->part_table) &&
e3661531
KZ
323 (cxt->label->nparts_max > 5 || ptes[4].part_table->sys_ind)) {
324 printf(_("omitting empty partition (%zd)\n"), i+1);
9ffeb235 325 dos_delete_partition(cxt, i);
e2ee9178
DB
326 goto remove; /* numbering changed */
327 }
328 }
329}
330
38b36353 331void dos_print_mbr_id(struct fdisk_context *cxt)
e2ee9178 332{
67987b47 333 printf(_("Disk identifier: 0x%08x\n"), mbr_get_id(cxt->firstsector));
e2ee9178
DB
334}
335
9ffeb235 336static int dos_create_disklabel(struct fdisk_context *cxt)
e2ee9178
DB
337{
338 unsigned int id;
339
9ffeb235
KZ
340 assert(cxt);
341 assert(cxt->label);
342 assert(fdisk_is_disklabel(cxt, DOS));
343
e2ee9178 344 /* random disk signature */
c544aa2c 345 random_get_bytes(&id, sizeof(id));
e2ee9178
DB
346
347 fprintf(stderr, _("Building a new DOS disklabel with disk identifier 0x%08x.\n"), id);
348
e53ced85 349 dos_init(cxt);
67987b47 350 fdisk_zeroize_firstsector(cxt);
bddd84e7 351 fdisk_label_set_changed(cxt->label, 1);
e2ee9178
DB
352
353 /* Generate an MBR ID for this disk */
67987b47 354 mbr_set_id(cxt->firstsector, id);
e2ee9178
DB
355
356 /* Put MBR signature */
67987b47 357 mbr_set_magic(cxt->firstsector);
a71601af 358 return 0;
e2ee9178
DB
359}
360
0a49e5d5 361int dos_set_mbr_id(struct fdisk_context *cxt)
e2ee9178 362{
0a49e5d5
KZ
363 char *end = NULL, *str = NULL;
364 unsigned int id, old;
365 int rc;
e2ee9178 366
0a49e5d5
KZ
367 old = mbr_get_id(cxt->firstsector);
368 rc = fdisk_ask_string(cxt,
369 _("Enter of the new disk identifier"), &str);
370 if (rc)
371 return rc;
e2ee9178 372
0a49e5d5
KZ
373 errno = 0;
374 id = strtoul(str, &end, 0);
375 if (errno || str == end || (end && *end)) {
376 fdisk_warnx(cxt, _("Incorrect value."));
377 return -EINVAL;
378 }
e2ee9178 379
0a49e5d5
KZ
380 fdisk_info(cxt, _("Changing disk identifier from 0x%08x to 0x%08x."),
381 old, id);
e2ee9178 382
0a49e5d5 383 mbr_set_id(cxt->firstsector, id);
e2ee9178 384 MBRbuffer_changed = 1;
bddd84e7 385 fdisk_label_set_changed(cxt->label, 1);
0a49e5d5 386 return 0;
e2ee9178
DB
387}
388
9a5e29e9
KZ
389static void get_partition_table_geometry(struct fdisk_context *cxt,
390 unsigned int *ph, unsigned int *ps)
391{
67987b47 392 unsigned char *bufp = cxt->firstsector;
9a5e29e9
KZ
393 struct partition *p;
394 int i, h, s, hh, ss;
395 int first = 1;
396 int bad = 0;
397
398 hh = ss = 0;
399 for (i=0; i<4; i++) {
400 p = pt_offset(bufp, i);
401 if (p->sys_ind != 0) {
402 h = p->end_head + 1;
403 s = (p->end_sector & 077);
404 if (first) {
405 hh = h;
406 ss = s;
407 first = 0;
408 } else if (hh != h || ss != s)
409 bad = 1;
410 }
411 }
412
413 if (!first && !bad) {
414 *ph = hh;
415 *ps = ss;
416 }
cf3808e4
KZ
417
418 DBG(CONTEXT, dbgprint("DOS PT geometry: heads=%u, sectors=%u", *ph, *ps));
9a5e29e9
KZ
419}
420
9ffeb235 421static int dos_reset_alignment(struct fdisk_context *cxt)
cf3808e4 422{
9ffeb235
KZ
423 assert(cxt);
424 assert(cxt->label);
425 assert(fdisk_is_disklabel(cxt, DOS));
426
cf3808e4 427 /* overwrite necessary stuff by DOS deprecated stuff */
852ce62b 428 if (is_dos_compatible(cxt)) {
cf3808e4
KZ
429 if (cxt->geom.sectors)
430 cxt->first_lba = cxt->geom.sectors; /* usually 63 */
431
432 cxt->grain = cxt->sector_size; /* usually 512 */
433 }
cf3808e4
KZ
434
435 return 0;
436}
9a5e29e9 437
9ed268d0
KZ
438/* TODO: move to include/pt-dos.h and share with libblkid */
439#define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1"
440#define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1)
441
9ffeb235 442static int dos_probe_label(struct fdisk_context *cxt)
e2ee9178 443{
e3661531 444 size_t i;
9a5e29e9 445 unsigned int h = 0, s = 0;
e2ee9178 446
e3661531
KZ
447 assert(cxt);
448 assert(cxt->label);
9ffeb235 449 assert(fdisk_is_disklabel(cxt, DOS));
e3661531 450
9ed268d0
KZ
451 /* ignore disks with AIX magic number */
452 if (memcmp(cxt->firstsector, AIX_MAGIC_STRING, AIX_MAGIC_STRLEN) == 0)
453 return 0;
454
67987b47 455 if (!mbr_is_valid_magic(cxt->firstsector))
e2ee9178
DB
456 return 0;
457
e53ced85 458 dos_init(cxt);
e2ee9178 459
9a5e29e9
KZ
460 get_partition_table_geometry(cxt, &h, &s);
461 if (h && s) {
462 cxt->geom.heads = h;
463 cxt->geom.sectors = s;
464 }
465
e2ee9178
DB
466 for (i = 0; i < 4; i++) {
467 struct pte *pe = &ptes[i];
468
e3661531
KZ
469 if (!is_cleared_partition(pe->part_table))
470 cxt->label->nparts_cur++;
471
e2ee9178 472 if (IS_EXTENDED (pe->part_table->sys_ind)) {
e3661531 473 if (cxt->label->nparts_max != 4)
e2ee9178 474 fprintf(stderr, _("Ignoring extra extended "
e3661531 475 "partition %zd\n"), i + 1);
e2ee9178 476 else
7737f698 477 read_extended(cxt, i);
e2ee9178
DB
478 }
479 }
480
e3661531 481 for (i = 3; i < cxt->label->nparts_max; i++) {
e2ee9178
DB
482 struct pte *pe = &ptes[i];
483
67188340 484 if (!mbr_is_valid_magic(pe->sectorbuffer)) {
e2ee9178
DB
485 fprintf(stderr,
486 _("Warning: invalid flag 0x%04x of partition "
e3661531 487 "table %zd will be corrected by w(rite)\n"),
e2ee9178
DB
488 part_table_flag(pe->sectorbuffer), i + 1);
489 pe->changed = 1;
9ffeb235 490 fdisk_label_set_changed(cxt->label, 1);
e2ee9178
DB
491 }
492 }
493
494 return 1;
495}
496
497/*
498 * Avoid warning about DOS partitions when no DOS partition was changed.
499 * Here a heuristic "is probably dos partition".
500 * We might also do the opposite and warn in all cases except
501 * for "is probably nondos partition".
502 */
02460b8a 503static int is_dos_partition(int t)
e2ee9178
DB
504{
505 return (t == 1 || t == 4 || t == 6 ||
506 t == 0x0b || t == 0x0c || t == 0x0e ||
507 t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 ||
508 t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 ||
509 t == 0xc1 || t == 0xc4 || t == 0xc6);
510}
9dea2923 511
e53ced85
DB
512static void set_partition(struct fdisk_context *cxt,
513 int i, int doext, sector_t start,
24def09d 514 sector_t stop, int sysid)
9dea2923
DB
515{
516 struct partition *p;
24def09d 517 sector_t offset;
9dea2923
DB
518
519 if (doext) {
520 p = ptes[i].ext_pointer;
521 offset = extended_offset;
522 } else {
523 p = ptes[i].part_table;
524 offset = ptes[i].offset;
525 }
526 p->boot_ind = 0;
527 p->sys_ind = sysid;
528 set_start_sect(p, start - offset);
529 set_nr_sects(p, stop - start + 1);
530
b4bfbadd
KZ
531 if (!doext) {
532 struct fdisk_parttype *t = fdisk_get_parttype_from_code(cxt, sysid);
533 fdisk_info_new_partition(cxt, i + 1, start, stop, t);
534 }
852ce62b 535 if (is_dos_compatible(cxt) && (start/(cxt->geom.sectors*cxt->geom.heads) > 1023))
24cd580b 536 start = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
9dea2923 537 set_hsc(p->head, p->sector, p->cyl, start);
852ce62b 538 if (is_dos_compatible(cxt) && (stop/(cxt->geom.sectors*cxt->geom.heads) > 1023))
24cd580b 539 stop = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
9dea2923
DB
540 set_hsc(p->end_head, p->end_sector, p->end_cyl, stop);
541 ptes[i].changed = 1;
542}
543
cf3808e4
KZ
544static sector_t get_unused_start(struct fdisk_context *cxt,
545 int part_n, sector_t start,
24def09d 546 sector_t first[], sector_t last[])
9dea2923 547{
e3661531 548 size_t i;
9dea2923 549
e3661531 550 for (i = 0; i < cxt->label->nparts_max; i++) {
24def09d 551 sector_t lastplusoff;
9dea2923
DB
552
553 if (start == ptes[i].offset)
cf3808e4
KZ
554 start += cxt->first_lba;
555 lastplusoff = last[i] + ((part_n < 4) ? 0 : cxt->first_lba);
9dea2923
DB
556 if (start >= first[i] && start <= lastplusoff)
557 start = lastplusoff + 1;
558 }
559
560 return start;
561}
562
e3661531
KZ
563static void fill_bounds(struct fdisk_context *cxt,
564 sector_t *first, sector_t *last)
cc0c4e56 565{
e3661531 566 size_t i;
cc0c4e56
KZ
567 struct pte *pe = &ptes[0];
568 struct partition *p;
569
e3661531 570 for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
cc0c4e56
KZ
571 p = pe->part_table;
572 if (!p->sys_ind || IS_EXTENDED (p->sys_ind)) {
573 first[i] = 0xffffffff;
574 last[i] = 0;
575 } else {
576 first[i] = get_partition_start(pe);
577 last[i] = first[i] + get_nr_sects(p) - 1;
578 }
579 }
580}
581
8254c3a5 582static int add_partition(struct fdisk_context *cxt, int n, struct fdisk_parttype *t)
9dea2923 583{
0e07540d 584 int sys, read = 0, rc;
e3661531 585 size_t i;
9dea2923
DB
586 struct partition *p = ptes[n].part_table;
587 struct partition *q = ptes[ext_index].part_table;
24def09d 588 sector_t start, stop = 0, limit, temp,
e3661531
KZ
589 first[cxt->label->nparts_max],
590 last[cxt->label->nparts_max];
9dea2923 591
ed470672
KZ
592 sys = t ? t->type : LINUX_NATIVE;
593
9dea2923
DB
594 if (p && p->sys_ind) {
595 printf(_("Partition %d is already defined. Delete "
596 "it before re-adding it.\n"), n + 1);
8254c3a5 597 return -EINVAL;
9dea2923 598 }
e3661531 599 fill_bounds(cxt, first, last);
9dea2923 600 if (n < 4) {
cf3808e4 601 start = cxt->first_lba;
ec10aa67 602 if (fdisk_context_use_cylinders(cxt) || !cxt->total_sectors)
24cd580b 603 limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
9dea2923 604 else
618882d6 605 limit = cxt->total_sectors - 1;
9dea2923
DB
606
607 if (limit > UINT_MAX)
608 limit = UINT_MAX;
609
610 if (extended_offset) {
611 first[ext_index] = extended_offset;
612 last[ext_index] = get_start_sect(q) +
613 get_nr_sects(q) - 1;
614 }
615 } else {
cf3808e4 616 start = extended_offset + cxt->first_lba;
9dea2923
DB
617 limit = get_start_sect(q) + get_nr_sects(q) - 1;
618 }
ec10aa67 619 if (fdisk_context_use_cylinders(cxt))
e3661531 620 for (i = 0; i < cxt->label->nparts_max; i++)
ec10aa67 621 first[i] = (cround(cxt, first[i]) - 1) * fdisk_context_get_units_per_sector(cxt);
9dea2923 622
0e07540d
KZ
623 /*
624 * Ask for first sector
625 */
9dea2923 626 do {
24def09d 627 sector_t dflt, aligned;
9dea2923
DB
628
629 temp = start;
cf3808e4 630 dflt = start = get_unused_start(cxt, n, start, first, last);
9dea2923
DB
631
632 /* the default sector should be aligned and unused */
633 do {
9475cc78 634 aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
cf3808e4 635 dflt = get_unused_start(cxt, n, aligned, first, last);
9dea2923
DB
636 } while (dflt != aligned && dflt > aligned && dflt < limit);
637
638 if (dflt >= limit)
639 dflt = start;
640 if (start > limit)
641 break;
ec10aa67 642 if (start >= temp+fdisk_context_get_units_per_sector(cxt) && read) {
9dea2923
DB
643 printf(_("Sector %llu is already allocated\n"), temp);
644 temp = start;
645 read = 0;
646 }
0e07540d 647
9dea2923 648 if (!read && start == temp) {
c9f63764 649 sector_t j = start;
0e07540d
KZ
650 struct fdisk_ask *ask = fdisk_new_ask();
651
652 if (fdisk_context_use_cylinders(cxt))
653 fdisk_ask_set_query(ask, _("First cylinder"));
654 else
655 fdisk_ask_set_query(ask, _("First sector"));
656
657 fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
658 fdisk_ask_number_set_low(ask, cround(cxt, j));
659 fdisk_ask_number_set_default(ask, cround(cxt, dflt));
660 fdisk_ask_number_set_high(ask, cround(cxt, limit));
661
662 rc = fdisk_do_ask(cxt, ask);
663 if (!rc)
664 start = fdisk_ask_number_get_result(ask);
665 fdisk_free_ask(ask);
666 if (rc)
667 return rc;
9dea2923 668
ec10aa67
KZ
669 if (fdisk_context_use_cylinders(cxt)) {
670 start = (start - 1) * fdisk_context_get_units_per_sector(cxt);
0e07540d
KZ
671 if (start < j)
672 start = j;
9dea2923
DB
673 }
674 read = 1;
675 }
676 } while (start != temp || !read);
0e07540d 677
9dea2923
DB
678 if (n > 4) { /* NOT for fifth partition */
679 struct pte *pe = &ptes[n];
680
cf3808e4 681 pe->offset = start - cxt->first_lba;
9dea2923
DB
682 if (pe->offset == extended_offset) { /* must be corrected */
683 pe->offset++;
cf3808e4 684 if (cxt->first_lba == 1)
9dea2923
DB
685 start++;
686 }
687 }
688
e3661531 689 for (i = 0; i < cxt->label->nparts_max; i++) {
9dea2923
DB
690 struct pte *pe = &ptes[i];
691
692 if (start < pe->offset && limit >= pe->offset)
693 limit = pe->offset - 1;
694 if (start < first[i] && limit >= first[i])
695 limit = first[i] - 1;
696 }
697 if (start > limit) {
698 printf(_("No free sectors available\n"));
699 if (n > 4)
e3661531 700 cxt->label->nparts_max--;
8254c3a5 701 return -ENOSPC;
9dea2923 702 }
ec10aa67 703 if (cround(cxt, start) == cround(cxt, limit)) {
9dea2923
DB
704 stop = limit;
705 } else {
0e07540d
KZ
706 /*
707 * Ask for last sector
708 */
709 struct fdisk_ask *ask = fdisk_new_ask();
9dea2923 710
0e07540d 711 fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
9dea2923 712
ec10aa67 713 if (fdisk_context_use_cylinders(cxt)) {
0e07540d
KZ
714 fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
715 fdisk_ask_number_set_unit(ask,
716 cxt->sector_size *
717 fdisk_context_get_units_per_sector(cxt));
718 } else {
719 fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
720 fdisk_ask_number_set_unit(ask,cxt->sector_size);
721 }
722
723 fdisk_ask_number_set_low(ask, cround(cxt, start));
724 fdisk_ask_number_set_default(ask, cround(cxt, limit));
725 fdisk_ask_number_set_high(ask, cround(cxt, limit));
726 fdisk_ask_number_set_base(ask, cround(cxt, start)); /* base for relative input */
727 fdisk_ask_number_set_unit(ask, cxt->sector_size);
728
729 rc = fdisk_do_ask(cxt, ask);
730 if (rc) {
731 fdisk_free_ask(ask);
732 return rc;
9dea2923
DB
733 }
734
0e07540d
KZ
735 stop = fdisk_ask_number_get_result(ask);
736
737 if (fdisk_ask_number_is_relative(ask)
738 && alignment_required(cxt)) {
9dea2923 739 /* the last sector has not been exactly requested (but
0e07540d
KZ
740 * defined by +size{K,M,G} convention), so be smart and
741 * align the end of the partition. The next partition
742 * will start at phy.block boundary.
9dea2923 743 */
9475cc78 744 stop = fdisk_align_lba_in_range(cxt, stop, start, limit) - 1;
9dea2923
DB
745 if (stop > limit)
746 stop = limit;
747 }
0e07540d 748 fdisk_free_ask(ask);
9dea2923
DB
749 }
750
e53ced85 751 set_partition(cxt, n, 0, start, stop, sys);
9dea2923 752 if (n > 4)
e53ced85 753 set_partition(cxt, n - 1, 1, ptes[n].offset, stop, EXTENDED);
9dea2923
DB
754
755 if (IS_EXTENDED (sys)) {
756 struct pte *pe4 = &ptes[4];
757 struct pte *pen = &ptes[n];
758
759 ext_index = n;
760 pen->ext_pointer = p;
761 pe4->offset = extended_offset = start;
e53ced85 762 pe4->sectorbuffer = xcalloc(1, cxt->sector_size);
9dea2923
DB
763 pe4->part_table = pt_offset(pe4->sectorbuffer, 0);
764 pe4->ext_pointer = pe4->part_table + 1;
765 pe4->changed = 1;
e3661531 766 cxt->label->nparts_max = 5;
9dea2923 767 }
8254c3a5 768
bddd84e7 769 fdisk_label_set_changed(cxt->label, 1);
8254c3a5 770 return 0;
9dea2923
DB
771}
772
8254c3a5 773static int add_logical(struct fdisk_context *cxt)
9dea2923 774{
e3661531
KZ
775 assert(cxt);
776 assert(cxt->label);
777
778 if (cxt->label->nparts_max > 5 || ptes[4].part_table->sys_ind) {
779 struct pte *pe = &ptes[cxt->label->nparts_max];
9dea2923 780
e53ced85 781 pe->sectorbuffer = xcalloc(1, cxt->sector_size);
9dea2923
DB
782 pe->part_table = pt_offset(pe->sectorbuffer, 0);
783 pe->ext_pointer = pe->part_table + 1;
784 pe->offset = 0;
785 pe->changed = 1;
e3661531 786 cxt->label->nparts_max++;
9dea2923 787 }
e3661531
KZ
788 printf(_("Adding logical partition %zd\n"), cxt->label->nparts_max);
789 return add_partition(cxt, cxt->label->nparts_max - 1, NULL);
9dea2923
DB
790}
791
e3661531 792static void check(struct fdisk_context *cxt, size_t n,
5dfca634
KZ
793 unsigned int h, unsigned int s, unsigned int c,
794 unsigned int start)
795{
796 unsigned int total, real_s, real_c;
797
798 real_s = sector(s) - 1;
799 real_c = cylinder(s, c);
800 total = (real_c * cxt->geom.sectors + real_s) * cxt->geom.heads + h;
801 if (!total)
e3661531 802 fprintf(stderr, _("Warning: partition %zd contains sector 0\n"), n);
5dfca634
KZ
803 if (h >= cxt->geom.heads)
804 fprintf(stderr,
e3661531 805 _("Partition %zd: head %d greater than maximum %d\n"),
5dfca634
KZ
806 n, h + 1, cxt->geom.heads);
807 if (real_s >= cxt->geom.sectors)
e3661531 808 fprintf(stderr, _("Partition %zd: sector %d greater than "
5dfca634
KZ
809 "maximum %llu\n"), n, s, cxt->geom.sectors);
810 if (real_c >= cxt->geom.cylinders)
a48c321d 811 fprintf(stderr, _("Partition %zd: cylinder %d greater than "
5dfca634
KZ
812 "maximum %llu\n"), n, real_c + 1, cxt->geom.cylinders);
813 if (cxt->geom.cylinders <= 1024 && start != total)
814 fprintf(stderr,
e3661531 815 _("Partition %zd: previous sectors %d disagrees with "
5dfca634
KZ
816 "total %d\n"), n, start, total);
817}
818
819/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
820 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
821 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
822 * Lubkin Oct. 1991). */
823
824static void
825long2chs(struct fdisk_context *cxt, unsigned long ls,
826 unsigned int *c, unsigned int *h, unsigned int *s) {
827 int spc = cxt->geom.heads * cxt->geom.sectors;
828
829 *c = ls / spc;
830 ls = ls % spc;
831 *h = ls / cxt->geom.sectors;
832 *s = ls % cxt->geom.sectors + 1; /* sectors count from 1 */
833}
834
e3661531
KZ
835static void check_consistency(struct fdisk_context *cxt, struct partition *p,
836 size_t partition)
5dfca634
KZ
837{
838 unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */
839 unsigned int pec, peh, pes; /* physical ending c, h, s */
840 unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */
841 unsigned int lec, leh, les; /* logical ending c, h, s */
842
843 if (!is_dos_compatible(cxt))
844 return;
845
846 if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
847 return; /* do not check extended partitions */
848
849/* physical beginning c, h, s */
850 pbc = (p->cyl & 0xff) | ((p->sector << 2) & 0x300);
851 pbh = p->head;
852 pbs = p->sector & 0x3f;
853
854/* physical ending c, h, s */
855 pec = (p->end_cyl & 0xff) | ((p->end_sector << 2) & 0x300);
856 peh = p->end_head;
857 pes = p->end_sector & 0x3f;
858
859/* compute logical beginning (c, h, s) */
860 long2chs(cxt, get_start_sect(p), &lbc, &lbh, &lbs);
861
862/* compute logical ending (c, h, s) */
863 long2chs(cxt, get_start_sect(p) + get_nr_sects(p) - 1, &lec, &leh, &les);
864
865/* Same physical / logical beginning? */
866 if (cxt->geom.cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
e3661531 867 printf(_("Partition %zd has different physical/logical "
5dfca634
KZ
868 "beginnings (non-Linux?):\n"), partition + 1);
869 printf(_(" phys=(%d, %d, %d) "), pbc, pbh, pbs);
870 printf(_("logical=(%d, %d, %d)\n"),lbc, lbh, lbs);
871 }
872
873/* Same physical / logical ending? */
874 if (cxt->geom.cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
e3661531 875 printf(_("Partition %zd has different physical/logical "
5dfca634
KZ
876 "endings:\n"), partition + 1);
877 printf(_(" phys=(%d, %d, %d) "), pec, peh, pes);
878 printf(_("logical=(%d, %d, %d)\n"),lec, leh, les);
879 }
880
881/* Ending on cylinder boundary? */
882 if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
e3661531 883 printf(_("Partition %zd does not end on cylinder boundary.\n"),
5dfca634
KZ
884 partition + 1);
885 }
886}
887
9ffeb235 888static int dos_verify_disklabel(struct fdisk_context *cxt)
2ca61a61 889{
e3661531 890 size_t i, j;
2ca61a61 891 sector_t total = 1, n_sectors = cxt->total_sectors;
e3661531
KZ
892 unsigned long long first[cxt->label->nparts_max],
893 last[cxt->label->nparts_max];
2ca61a61
DB
894 struct partition *p;
895
9ffeb235
KZ
896 assert(cxt);
897 assert(cxt->label);
898 assert(fdisk_is_disklabel(cxt, DOS));
899
e3661531
KZ
900 fill_bounds(cxt, first, last);
901 for (i = 0; i < cxt->label->nparts_max; i++) {
2ca61a61
DB
902 struct pte *pe = &ptes[i];
903
904 p = pe->part_table;
905 if (p->sys_ind && !IS_EXTENDED (p->sys_ind)) {
906 check_consistency(cxt, p, i);
9475cc78 907 fdisk_warn_alignment(cxt, get_partition_start(pe), i);
2ca61a61
DB
908 if (get_partition_start(pe) < first[i])
909 printf(_("Warning: bad start-of-data in "
e3661531 910 "partition %zd\n"), i + 1);
2ca61a61
DB
911 check(cxt, i + 1, p->end_head, p->end_sector, p->end_cyl,
912 last[i]);
913 total += last[i] + 1 - first[i];
914 for (j = 0; j < i; j++)
915 if ((first[i] >= first[j] && first[i] <= last[j])
916 || ((last[i] <= last[j] && last[i] >= first[j]))) {
e3661531
KZ
917 printf(_("Warning: partition %zd overlaps "
918 "partition %zd.\n"), j + 1, i + 1);
2ca61a61
DB
919 total += first[i] >= first[j] ?
920 first[i] : first[j];
921 total -= last[i] <= last[j] ?
922 last[i] : last[j];
923 }
924 }
925 }
926
927 if (extended_offset) {
928 struct pte *pex = &ptes[ext_index];
929 sector_t e_last = get_start_sect(pex->part_table) +
930 get_nr_sects(pex->part_table) - 1;
931
e3661531 932 for (i = 4; i < cxt->label->nparts_max; i++) {
2ca61a61
DB
933 total++;
934 p = ptes[i].part_table;
935 if (!p->sys_ind) {
e3661531
KZ
936 if (i != 4 || i + 1 < cxt->label->nparts_max)
937 printf(_("Warning: partition %zd "
2ca61a61
DB
938 "is empty\n"), i + 1);
939 }
940 else if (first[i] < extended_offset ||
941 last[i] > e_last)
e3661531
KZ
942 printf(_("Logical partition %zd not entirely in "
943 "partition %zd\n"), i + 1, ext_index + 1);
2ca61a61
DB
944 }
945 }
946
947 if (total > n_sectors)
948 printf(_("Total allocated sectors %llu greater than the maximum"
949 " %llu\n"), total, n_sectors);
950 else if (total < n_sectors)
951 printf(_("Remaining %lld unallocated %ld-byte sectors\n"),
952 n_sectors - total, cxt->sector_size);
953
954 return 0;
955}
956
9dea2923
DB
957/*
958 * Ask the user for new partition type information (logical, extended).
0f639e54
DB
959 * This function calls the actual partition adding logic - add_partition.
960 *
961 * API callback.
9dea2923 962 */
8254c3a5 963static int dos_add_partition(
43b382e6 964 struct fdisk_context *cxt,
9ffeb235 965 size_t partnum __attribute__ ((__unused__)),
ed470672 966 struct fdisk_parttype *t)
9dea2923 967{
e3661531
KZ
968 size_t i, free_primary = 0;
969 int rc = 0;
9dea2923 970
9ffeb235
KZ
971 assert(cxt);
972 assert(cxt->label);
973 assert(fdisk_is_disklabel(cxt, DOS));
974
9dea2923
DB
975 for (i = 0; i < 4; i++)
976 free_primary += !ptes[i].part_table->sys_ind;
977
e3661531 978 if (!free_primary && cxt->label->nparts_max >= MAXIMUM_PARTS) {
9dea2923 979 printf(_("The maximum number of partitions has been created\n"));
8254c3a5 980 return -EINVAL;
9dea2923 981 }
1bf3a034 982 rc = 1;
9dea2923
DB
983 if (!free_primary) {
984 if (extended_offset) {
985 printf(_("All primary partitions are in use\n"));
8254c3a5 986 rc = add_logical(cxt);
9dea2923
DB
987 } else
988 printf(_("If you want to create more than four partitions, you must replace a\n"
989 "primary partition with an extended partition first.\n"));
e3661531 990 } else if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
c9f63764 991 int j;
1bf3a034 992
9dea2923
DB
993 printf(_("All logical partitions are in use\n"));
994 printf(_("Adding a primary partition\n"));
1bf3a034 995
c9f63764
SK
996 j = get_partition_unused_primary(cxt);
997 if (j >= 0)
998 rc = add_partition(cxt, j, t);
9dea2923 999 } else {
e3661531
KZ
1000 char c, line[LINE_LENGTH];
1001 int dflt;
9dea2923
DB
1002
1003 dflt = (free_primary == 1 && !extended_offset) ? 'e' : 'p';
1004 snprintf(line, sizeof(line),
1005 _("Partition type:\n"
e3661531 1006 " p primary (%zd primary, %d extended, %zd free)\n"
9dea2923
DB
1007 "%s\n"
1008 "Select (default %c): "),
e3661531
KZ
1009 4 - (extended_offset ? 1 : 0) - free_primary,
1010 extended_offset ? 1 : 0, free_primary,
9dea2923
DB
1011 extended_offset ? _(" l logical (numbered from 5)") : _(" e extended"),
1012 dflt);
1013
bddd84e7 1014 c = tolower(read_chars(cxt, line));
9dea2923
DB
1015 if (c == '\n') {
1016 c = dflt;
1017 printf(_("Using default response %c\n"), c);
1018 }
1019 if (c == 'p') {
c9f63764
SK
1020 int j = get_partition_unused_primary(cxt);
1021 if (j >= 0)
1022 rc = add_partition(cxt, j, t);
8254c3a5 1023 goto done;
9dea2923 1024 } else if (c == 'l' && extended_offset) {
8254c3a5
DB
1025 rc = add_logical(cxt);
1026 goto done;
9dea2923 1027 } else if (c == 'e' && !extended_offset) {
c9f63764
SK
1028 int j = get_partition_unused_primary(cxt);
1029 if (j >= 0) {
ed470672 1030 t = fdisk_get_parttype_from_code(cxt, EXTENDED);
c9f63764 1031 rc = add_partition(cxt, j, t);
ed470672 1032 }
8254c3a5 1033 goto done;
9dea2923
DB
1034 } else
1035 printf(_("Invalid partition type `%c'\n"), c);
1036 }
8254c3a5 1037done:
e3661531
KZ
1038 if (rc == 0)
1039 cxt->label->nparts_cur++;
8254c3a5 1040 return rc;
9dea2923 1041}
0dc13a38 1042
fae7b1bc
DB
1043static int write_sector(struct fdisk_context *cxt, sector_t secno,
1044 unsigned char *buf)
0dc13a38 1045{
21770662
KZ
1046 int rc;
1047
1048 rc = seek_sector(cxt, secno);
1049 if (rc != 0) {
1050 fprintf(stderr, _("write sector %jd failed: seek failed"),
1051 (uintmax_t) secno);
1052 return rc;
1053 }
fae7b1bc
DB
1054 if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
1055 return -errno;
1056 return 0;
1057}
1058
9ffeb235 1059static int dos_write_disklabel(struct fdisk_context *cxt)
fae7b1bc 1060{
e3661531
KZ
1061 size_t i;
1062 int rc = 0;
0dc13a38 1063
9ffeb235
KZ
1064 assert(cxt);
1065 assert(cxt->label);
1066 assert(fdisk_is_disklabel(cxt, DOS));
1067
0dc13a38
DB
1068 /* MBR (primary partitions) */
1069 if (!MBRbuffer_changed) {
1070 for (i = 0; i < 4; i++)
1071 if (ptes[i].changed)
1072 MBRbuffer_changed = 1;
1073 }
1074 if (MBRbuffer_changed) {
67987b47 1075 mbr_set_magic(cxt->firstsector);
fae7b1bc
DB
1076 rc = write_sector(cxt, 0, cxt->firstsector);
1077 if (rc)
1078 goto done;
0dc13a38
DB
1079 }
1080 /* EBR (logical partitions) */
e3661531 1081 for (i = 4; i < cxt->label->nparts_max; i++) {
0dc13a38
DB
1082 struct pte *pe = &ptes[i];
1083
1084 if (pe->changed) {
87a97832 1085 mbr_set_magic(pe->sectorbuffer);
fae7b1bc
DB
1086 rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
1087 if (rc)
1088 goto done;
0dc13a38
DB
1089 }
1090 }
fae7b1bc
DB
1091
1092done:
1093 return rc;
0dc13a38 1094}
b8855c86 1095
8a95621d
KZ
1096static struct fdisk_parttype *dos_get_parttype(
1097 struct fdisk_context *cxt,
9ffeb235 1098 size_t partnum)
010186f2
KZ
1099{
1100 struct fdisk_parttype *t;
1101 struct partition *p;
1102
9ffeb235
KZ
1103 assert(cxt);
1104 assert(cxt->label);
e09435aa 1105 assert(fdisk_is_disklabel(cxt, DOS));
9ffeb235
KZ
1106
1107 if (partnum >= cxt->label->nparts_max)
010186f2
KZ
1108 return NULL;
1109
1110 p = ptes[partnum].part_table;
1111 t = fdisk_get_parttype_from_code(cxt, p->sys_ind);
1112 if (!t)
1113 t = fdisk_new_unknown_parttype(p->sys_ind, NULL);
1114 return t;
1115}
1116
8a95621d 1117static int dos_set_parttype(
e3661531 1118 struct fdisk_context *cxt,
9ffeb235 1119 size_t partnum,
8a95621d 1120 struct fdisk_parttype *t)
02460b8a
KZ
1121{
1122 struct partition *p;
1123
9ffeb235
KZ
1124 assert(cxt);
1125 assert(cxt->label);
e09435aa 1126 assert(fdisk_is_disklabel(cxt, DOS));
9ffeb235
KZ
1127
1128 if (partnum >= cxt->label->nparts_max || !t || t->type > UINT8_MAX)
02460b8a
KZ
1129 return -EINVAL;
1130
1131 p = ptes[partnum].part_table;
1132 if (t->type == p->sys_ind)
1133 return 0;
1134
1135 if (IS_EXTENDED(p->sys_ind) || IS_EXTENDED(t->type)) {
1136 printf(_("\nYou cannot change a partition into an extended one "
1137 "or vice versa.\nDelete it first.\n\n"));
1138 return -EINVAL;
1139 }
1140
1141 if (is_dos_partition(t->type) || is_dos_partition(p->sys_ind))
1142 printf(
ef75bc88
YC
1143 _("\nWARNING: If you have created or modified any DOS 6.x "
1144 "partitions, please see the fdisk manual page for additional "
02460b8a
KZ
1145 "information.\n\n"));
1146
76b86412 1147 ptes[partnum].changed = 1;
02460b8a 1148 p->sys_ind = t->type;
9ffeb235 1149 fdisk_label_set_changed(cxt->label, 1);
02460b8a
KZ
1150 return 0;
1151}
1152
dfc96cbf
KZ
1153/*
1154 * Check whether partition entries are ordered by their starting positions.
1155 * Return 0 if OK. Return i if partition i should have been earlier.
1156 * Two separate checks: primary and logical partitions.
1157 */
e3661531 1158static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
dfc96cbf
KZ
1159{
1160 struct pte *pe;
1161 struct partition *p;
e3661531
KZ
1162 size_t last_p_start_pos = 0, p_start_pos;
1163 size_t i, last_i = 0;
dfc96cbf 1164
e3661531 1165 for (i = 0 ; i < cxt->label->nparts_max; i++) {
dfc96cbf
KZ
1166 if (i == 4) {
1167 last_i = 4;
1168 last_p_start_pos = 0;
1169 }
1170 pe = &ptes[i];
1171 if ((p = pe->part_table)->sys_ind) {
1172 p_start_pos = get_partition_start(pe);
1173
1174 if (last_p_start_pos > p_start_pos) {
1175 if (prev)
1176 *prev = last_i;
1177 return i;
1178 }
1179
1180 last_p_start_pos = p_start_pos;
1181 last_i = i;
1182 }
1183 }
1184 return 0;
1185}
6d864a49
KZ
1186
1187static int is_garbage_table(void)
1188{
e3661531 1189 size_t i;
6d864a49
KZ
1190
1191 for (i = 0; i < 4; i++) {
1192 struct pte *pe = &ptes[i];
1193 struct partition *p = pe->part_table;
1194
1195 if (p->boot_ind != 0 && p->boot_ind != 0x80)
1196 return 1;
1197 }
1198 return 0;
1199}
1200
1201int dos_list_table(struct fdisk_context *cxt,
1202 int xtra __attribute__ ((__unused__)))
1203{
1204 struct partition *p;
e3661531 1205 size_t i, w;
6d864a49
KZ
1206
1207 assert(cxt);
9ffeb235 1208 assert(cxt->label);
6d864a49
KZ
1209 assert(fdisk_is_disklabel(cxt, DOS));
1210
1211 if (is_garbage_table()) {
1212 printf(_("This doesn't look like a partition table\n"
1213 "Probably you selected the wrong device.\n\n"));
1214 }
1215
1216 /* Heuristic: we list partition 3 of /dev/foo as /dev/foo3,
1217 but if the device name ends in a digit, say /dev/foo1,
1218 then the partition is called /dev/foo1p3. */
1219 w = strlen(cxt->dev_path);
1220 if (w && isdigit(cxt->dev_path[w-1]))
1221 w++;
1222 if (w < 5)
1223 w = 5;
1224
1225 printf(_("%*s Boot Start End Blocks Id System\n"),
e3661531 1226 (int) w + 1, _("Device"));
6d864a49 1227
e3661531 1228 for (i = 0; i < cxt->label->nparts_max; i++) {
6d864a49
KZ
1229 struct pte *pe = &ptes[i];
1230
1231 p = pe->part_table;
1232 if (p && !is_cleared_partition(p)) {
1233 unsigned int psects = get_nr_sects(p);
1234 unsigned int pblocks = psects;
1235 unsigned int podd = 0;
1236 struct fdisk_parttype *type =
1237 fdisk_get_parttype_from_code(cxt, p->sys_ind);
1238
1239 if (cxt->sector_size < 1024) {
1240 pblocks /= (1024 / cxt->sector_size);
1241 podd = psects % (1024 / cxt->sector_size);
1242 }
1243 if (cxt->sector_size > 1024)
1244 pblocks *= (cxt->sector_size / 1024);
1245 printf(
1246 "%s %c %11lu %11lu %11lu%c %2x %s\n",
1247 partname(cxt->dev_path, i+1, w+2),
1248/* boot flag */ !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG
1249 ? '*' : '?',
ec10aa67
KZ
1250/* start */ (unsigned long) cround(cxt, get_partition_start(pe)),
1251/* end */ (unsigned long) cround(cxt, get_partition_start(pe) + psects
6d864a49
KZ
1252 - (psects ? 1 : 0)),
1253/* odd flag on end */ (unsigned long) pblocks, podd ? '+' : ' ',
1254/* type id */ p->sys_ind,
1255/* type name */ type ? type->name : _("Unknown"));
1256 check_consistency(cxt, p, i);
1257 fdisk_warn_alignment(cxt, get_partition_start(pe), i);
1258 }
1259 }
1260
1261 /* Is partition table in disk order? It need not be, but... */
1262 /* partition table entries are not checked for correct order if this
d23c661c 1263 is a sgi, sun labeled disk... */
e3661531 1264 if (wrong_p_order(cxt, NULL))
6d864a49
KZ
1265 printf(_("\nPartition table entries are not in disk order\n"));
1266
1267 return 0;
1268}
1269
5dfca634
KZ
1270/*
1271 * TODO: merge into dos_list_table
1272 */
1273void dos_list_table_expert(struct fdisk_context *cxt, int extend)
1274{
1275 struct pte *pe;
1276 struct partition *p;
e3661531 1277 size_t i;
5dfca634
KZ
1278
1279 printf(_("\nDisk %s: %d heads, %llu sectors, %llu cylinders\n\n"),
1280 cxt->dev_path, cxt->geom.heads, cxt->geom.sectors, cxt->geom.cylinders);
1281 printf(_("Nr AF Hd Sec Cyl Hd Sec Cyl Start Size ID\n"));
e3661531 1282 for (i = 0 ; i < cxt->label->nparts_max; i++) {
5dfca634
KZ
1283 pe = &ptes[i];
1284 p = (extend ? pe->ext_pointer : pe->part_table);
1285 if (p != NULL) {
e3661531 1286 printf("%2zd %02x%4d%4d%5d%4d%4d%5d%11lu%11lu %02x\n",
5dfca634
KZ
1287 i + 1, p->boot_ind, p->head,
1288 sector(p->sector),
1289 cylinder(p->sector, p->cyl), p->end_head,
1290 sector(p->end_sector),
1291 cylinder(p->end_sector, p->end_cyl),
1292 (unsigned long) get_start_sect(p),
1293 (unsigned long) get_nr_sects(p), p->sys_ind);
1294 if (p->sys_ind) {
1295 check_consistency(cxt, p, i);
1296 fdisk_warn_alignment(cxt, get_partition_start(pe), i);
1297 }
1298 }
1299 }
1300}
1301
dfc96cbf
KZ
1302/*
1303 * Fix the chain of logicals.
1304 * extended_offset is unchanged, the set of sectors used is unchanged
1305 * The chain is sorted so that sectors increase, and so that
1306 * starting sectors increase.
1307 *
1308 * After this it may still be that cfdisk doesn't like the table.
1309 * (This is because cfdisk considers expanded parts, from link to
1310 * end of partition, and these may still overlap.)
1311 * Now
1312 * sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
1313 * may help.
1314 */
e3661531 1315static void fix_chain_of_logicals(struct fdisk_context *cxt)
dfc96cbf 1316{
e3661531 1317 size_t j, oj, ojj, sj, sjj;
dfc96cbf
KZ
1318 struct partition *pj,*pjj,tmp;
1319
1320 /* Stage 1: sort sectors but leave sector of part 4 */
1321 /* (Its sector is the global extended_offset.) */
1322 stage1:
e3661531 1323 for (j = 5; j < cxt->label->nparts_max - 1; j++) {
dfc96cbf
KZ
1324 oj = ptes[j].offset;
1325 ojj = ptes[j+1].offset;
1326 if (oj > ojj) {
1327 ptes[j].offset = ojj;
1328 ptes[j+1].offset = oj;
1329 pj = ptes[j].part_table;
1330 set_start_sect(pj, get_start_sect(pj)+oj-ojj);
1331 pjj = ptes[j+1].part_table;
1332 set_start_sect(pjj, get_start_sect(pjj)+ojj-oj);
1333 set_start_sect(ptes[j-1].ext_pointer,
1334 ojj-extended_offset);
1335 set_start_sect(ptes[j].ext_pointer,
1336 oj-extended_offset);
1337 goto stage1;
1338 }
1339 }
1340
1341 /* Stage 2: sort starting sectors */
1342 stage2:
e3661531 1343 for (j = 4; j < cxt->label->nparts_max - 1; j++) {
dfc96cbf
KZ
1344 pj = ptes[j].part_table;
1345 pjj = ptes[j+1].part_table;
1346 sj = get_start_sect(pj);
1347 sjj = get_start_sect(pjj);
1348 oj = ptes[j].offset;
1349 ojj = ptes[j+1].offset;
1350 if (oj+sj > ojj+sjj) {
1351 tmp = *pj;
1352 *pj = *pjj;
1353 *pjj = tmp;
1354 set_start_sect(pj, ojj+sjj-oj);
1355 set_start_sect(pjj, oj+sj-ojj);
1356 goto stage2;
1357 }
1358 }
1359
1360 /* Probably something was changed */
e3661531 1361 for (j = 4; j < cxt->label->nparts_max; j++)
dfc96cbf
KZ
1362 ptes[j].changed = 1;
1363}
1364
e3661531 1365void dos_fix_partition_table_order(struct fdisk_context *cxt)
dfc96cbf
KZ
1366{
1367 struct pte *pei, *pek;
e3661531 1368 size_t i,k;
dfc96cbf 1369
e3661531 1370 if (!wrong_p_order(cxt, NULL)) {
dfc96cbf
KZ
1371 printf(_("Nothing to do. Ordering is correct already.\n\n"));
1372 return;
1373 }
1374
e3661531 1375 while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
dfc96cbf
KZ
1376 /* partition i should have come earlier, move it */
1377 /* We have to move data in the MBR */
1378 struct partition *pi, *pk, *pe, pbuf;
1379 pei = &ptes[i];
1380 pek = &ptes[k];
1381
1382 pe = pei->ext_pointer;
1383 pei->ext_pointer = pek->ext_pointer;
1384 pek->ext_pointer = pe;
1385
1386 pi = pei->part_table;
1387 pk = pek->part_table;
1388
1389 memmove(&pbuf, pi, sizeof(struct partition));
1390 memmove(pi, pk, sizeof(struct partition));
1391 memmove(pk, &pbuf, sizeof(struct partition));
1392
1393 pei->changed = pek->changed = 1;
1394 }
1395
1396 if (i)
e3661531 1397 fix_chain_of_logicals(cxt);
dfc96cbf
KZ
1398
1399 printf(_("Done.\n"));
1400
1401}
6d864a49 1402
71ee484e
KZ
1403void dos_move_begin(struct fdisk_context *cxt, int i)
1404{
1405 struct pte *pe = &ptes[i];
1406 struct partition *p = pe->part_table;
1407 unsigned int new, free_start, curr_start, last;
e3661531 1408 size_t x;
71ee484e
KZ
1409
1410 assert(cxt);
1411 assert(fdisk_is_disklabel(cxt, DOS));
1412
1413 if (warn_geometry(cxt))
1414 return;
1415 if (!p->sys_ind || !get_nr_sects(p) || IS_EXTENDED (p->sys_ind)) {
1416 printf(_("Partition %d has no data area\n"), i + 1);
1417 return;
1418 }
1419
1420 /* the default start is at the second sector of the disk or at the
1421 * second sector of the extended partition
1422 */
1423 free_start = pe->offset ? pe->offset + 1 : 1;
1424
1425 curr_start = get_partition_start(pe);
1426
1427 /* look for a free space before the current start of the partition */
e3661531 1428 for (x = 0; x < cxt->label->nparts_max; x++) {
71ee484e
KZ
1429 unsigned int end;
1430 struct pte *prev_pe = &ptes[x];
1431 struct partition *prev_p = prev_pe->part_table;
1432
1433 if (!prev_p)
1434 continue;
1435 end = get_partition_start(prev_pe) + get_nr_sects(prev_p);
1436
1437 if (!is_cleared_partition(prev_p) &&
1438 end > free_start && end <= curr_start)
1439 free_start = end;
1440 }
1441
1442 last = get_partition_start(pe) + get_nr_sects(p) - 1;
1443
1444 new = read_int(cxt, free_start, curr_start, last, free_start,
1445 _("New beginning of data")) - pe->offset;
1446
1447 if (new != get_nr_sects(p)) {
1448 unsigned int sects = get_nr_sects(p) + get_start_sect(p) - new;
1449 set_nr_sects(p, sects);
1450 set_start_sect(p, new);
1451 pe->changed = 1;
1452 }
1453}
6d864a49 1454
47b8e7c0
KZ
1455static int dos_get_partition_status(
1456 struct fdisk_context *cxt,
9ffeb235 1457 size_t i,
47b8e7c0
KZ
1458 int *status)
1459{
1460 struct pte *pe;
1461 struct partition *p;
1462
1463 assert(cxt);
9ffeb235 1464 assert(cxt->label);
47b8e7c0
KZ
1465 assert(fdisk_is_disklabel(cxt, DOS));
1466
9ffeb235 1467 if (!status || i >= cxt->label->nparts_max)
47b8e7c0
KZ
1468 return -EINVAL;
1469
1470 *status = FDISK_PARTSTAT_NONE;
1471 pe = &ptes[i];
1472 p = pe->part_table;
1473
1474 if (p && !is_cleared_partition(p))
1475 *status = FDISK_PARTSTAT_USED;
1476
1477 return 0;
1478}
1479
fb1caca7
KZ
1480static int dos_toggle_partition_flag(
1481 struct fdisk_context *cxt,
1482 size_t i,
1483 unsigned long flag)
1484{
1485 struct pte *pe;
1486 struct partition *p;
1487
1488 assert(cxt);
1489 assert(cxt->label);
1490 assert(fdisk_is_disklabel(cxt, DOS));
1491
1492 if (i >= cxt->label->nparts_max)
1493 return -EINVAL;
1494
1495 pe = &ptes[i];
1496 p = pe->part_table;
1497
1498 switch (flag) {
1499 case DOS_FLAG_ACTIVE:
1500 if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
1501 fdisk_warnx(cxt, _("WARNING: Partition %d is an extended partition"), i + 1);
1502
1503 p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
1504 pe->changed = 1;
1505 fdisk_label_set_changed(cxt->label, 1);
1506 break;
1507 default:
1508 return 1;
1509 }
1510
1511 return 0;
1512}
1513
0c5d095e 1514static const struct fdisk_label_operations dos_operations =
b8855c86 1515{
0c5d095e
KZ
1516 .probe = dos_probe_label,
1517 .write = dos_write_disklabel,
1518 .verify = dos_verify_disklabel,
1519 .create = dos_create_disklabel,
1520 .part_add = dos_add_partition,
1521 .part_delete = dos_delete_partition,
1522 .part_get_type = dos_get_parttype,
1523 .part_set_type = dos_set_parttype,
47b8e7c0 1524
fb1caca7 1525 .part_toggle_flag = dos_toggle_partition_flag,
47b8e7c0
KZ
1526 .part_get_status = dos_get_partition_status,
1527
cf3808e4 1528 .reset_alignment = dos_reset_alignment,
b8855c86 1529};
0c5d095e
KZ
1530
1531/*
1532 * allocates DOS in-memory stuff
1533 */
1534struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt)
1535{
1536 struct fdisk_label *lb;
1537 struct fdisk_dos_label *dos;
1538
1539 assert(cxt);
1540
1541 dos = calloc(1, sizeof(*dos));
1542 if (!dos)
1543 return NULL;
1544
1545 /* initialize generic part of the driver */
1546 lb = (struct fdisk_label *) dos;
1547 lb->name = "dos";
53b422ab 1548 lb->id = FDISK_DISKLABEL_DOS;
0c5d095e
KZ
1549 lb->op = &dos_operations;
1550 lb->parttypes = dos_parttypes;
1551 lb->nparttypes = ARRAY_SIZE(dos_parttypes);
1552
baa7cccc
KZ
1553 /* don't ask for partition number for op->part_add() */
1554 lb->flags = FDISK_LABEL_FL_ADDPART_NOPARTNO;
1555
0c5d095e
KZ
1556 return lb;
1557}
852ce62b
KZ
1558
1559/*
1560 * Public label specific functions
1561 */
1562
1563int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
1564{
1565 struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
1566
1567 if (!lb)
1568 return -EINVAL;
1569
1570 dos->compatible = enable;
1571 return 0;
1572}
1573
1574int fdisk_dos_is_compatible(struct fdisk_label *lb)
1575{
1576 return ((struct fdisk_dos_label *) lb)->compatible;
1577}