]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libfdisk/src/dos.c
libfdisk: (dos) all disklabel locate function
[thirdparty/util-linux.git] / libfdisk / src / dos.c
CommitLineData
e2ee9178 1/*
c10937dc 2 *
50992267
KZ
3 * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
4 * 2012 Davidlohr Bueso <dave@gnu.org>
c10937dc
KZ
5 *
6 * This is re-written version for libfdisk, the original was fdiskdoslabel.c
7 * from util-linux fdisk.
e2ee9178 8 */
ddec0e40 9#include "c.h"
e2ee9178 10#include "nls.h"
e2ee9178 11#include "randutils.h"
bb58112e 12#include "pt-mbr.h"
ac1a559a 13#include "strutils.h"
bb58112e 14
c10937dc
KZ
15#include "fdiskP.h"
16
235adaa6
KZ
17#include <ctype.h>
18
bb58112e 19#define MAXIMUM_PARTS 60
bb58112e
KZ
20#define ACTIVE_FLAG 0x80
21
22#define IS_EXTENDED(i) \
23 ((i) == MBR_DOS_EXTENDED_PARTITION \
24 || (i) == MBR_W95_EXTENDED_PARTITION \
25 || (i) == MBR_LINUX_EXTENDED_PARTITION)
26
af0df606
KZ
27/*
28 * per partition table entry data
29 *
30 * The four primary partitions have the same sectorbuffer
31 * and have NULL ex_entry.
32 *
33 * Each logical partition table entry has two pointers, one for the
34 * partition and one link to the next one.
35 */
36struct pte {
37 struct dos_partition *pt_entry; /* on-disk MBR entry */
38 struct dos_partition *ex_entry; /* on-disk EBR entry */
af0df606
KZ
39 sector_t offset; /* disk sector number */
40 unsigned char *sectorbuffer; /* disk sector contents */
ddec0e40
KZ
41
42 unsigned int changed : 1,
43 private_sectorbuffer : 1;
af0df606 44};
0c5d095e
KZ
45
46/*
47 * in-memory fdisk GPT stuff
48 */
49struct fdisk_dos_label {
50 struct fdisk_label head; /* generic part */
852ce62b 51
af0df606 52 struct pte ptes[MAXIMUM_PARTS]; /* partition */
fcea064b
KZ
53 sector_t ext_offset;
54 size_t ext_index;
8b3d9241
KZ
55 unsigned int compatible : 1, /* is DOS compatible? */
56 non_pt_changed : 1; /* MBR, but no PT changed */
0c5d095e
KZ
57};
58
59/*
60 * Partition types
61 */
749af4b6 62static struct fdisk_parttype dos_parttypes[] = {
50992267 63 #include "pt-mbr-partnames.h"
749af4b6
KZ
64};
65
9dea2923 66#define set_hsc(h,s,c,sector) { \
24cd580b
DB
67 s = sector % cxt->geom.sectors + 1; \
68 sector /= cxt->geom.sectors; \
69 h = sector % cxt->geom.heads; \
70 sector /= cxt->geom.heads; \
71 c = sector & 0xff; \
72 s |= (sector >> 2) & 0xc0; \
9dea2923
DB
73 }
74
5dfca634
KZ
75
76#define sector(s) ((s) & 0x3f)
77#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
78
cf3808e4 79#define alignment_required(_x) ((_x)->grain != (_x)->sector_size)
9dea2923 80
28b85e60
KZ
81#define is_dos_compatible(_x) \
82 (fdisk_is_disklabel(_x, DOS) && \
83 fdisk_dos_is_compatible(fdisk_context_get_label(_x, NULL)))
96f817fb 84
80063996 85#define cround(c, n) fdisk_cround(c, n)
3c88fb56 86
af0df606
KZ
87
88static inline struct fdisk_dos_label *self_label(struct fdisk_context *cxt)
89{
90 assert(cxt);
91 assert(cxt->label);
92 assert(fdisk_is_disklabel(cxt, DOS));
93
94 return (struct fdisk_dos_label *) cxt->label;
95}
96
97static inline struct pte *self_pte(struct fdisk_context *cxt, size_t i)
98{
99 struct fdisk_dos_label *l = self_label(cxt);
100
101 if (i >= ARRAY_SIZE(l->ptes))
102 return NULL;
103
104 return &l->ptes[i];
105}
106
107static inline struct dos_partition *self_partition(
108 struct fdisk_context *cxt,
109 size_t i)
110{
111 struct pte *pe = self_pte(cxt, i);
112 return pe ? pe->pt_entry : NULL;
113}
114
115struct dos_partition *fdisk_dos_get_partition(
116 struct fdisk_context *cxt,
117 size_t i)
118{
60c1b036
KZ
119 assert(cxt);
120 assert(cxt->label);
121 assert(fdisk_is_disklabel(cxt, DOS));
122
af0df606
KZ
123 return self_partition(cxt, i);
124}
125
126static void partition_set_changed(
127 struct fdisk_context *cxt,
128 size_t i,
129 int changed)
130{
131 struct pte *pe = self_pte(cxt, i);
132
133 if (!pe)
134 return;
135
9c825fa0
KZ
136 DBG(LABEL, dbgprint("DOS: setting %zu partition changed", i));
137
ddec0e40 138 pe->changed = changed ? 1 : 0;
af0df606
KZ
139 if (changed)
140 fdisk_label_set_changed(cxt->label, 1);
141}
142
61eaad50
KZ
143static sector_t get_abs_partition_start(struct pte *pe)
144{
60c1b036
KZ
145 assert(pe);
146 assert(pe->pt_entry);
147
61eaad50
KZ
148 return pe->offset + dos_partition_get_start(pe->pt_entry);
149}
150
a64aa3b8
KZ
151static int is_cleared_partition(struct dos_partition *p)
152{
153 return !(!p || p->boot_ind || p->bh || p->bs || p->bc ||
154 p->sys_ind || p->eh || p->es || p->ec ||
155 dos_partition_get_start(p) || dos_partition_get_size(p));
156}
157
1bf3a034 158static int get_partition_unused_primary(struct fdisk_context *cxt)
9dea2923 159{
1bf3a034
KZ
160 size_t orgmax = cxt->label->nparts_max;
161 size_t n;
162 int rc;
9dea2923 163
1bf3a034
KZ
164 cxt->label->nparts_max = 4;
165 rc = fdisk_ask_partnum(cxt, &n, TRUE);
166 cxt->label->nparts_max = orgmax;
167
168 switch (rc) {
169 case 1:
ac1a559a 170 fdisk_info(cxt, _("All primary partitions have been defined already."));
1bf3a034
KZ
171 return -1;
172 case 0:
173 return n;
174 default:
175 return rc;
9dea2923 176 }
9dea2923
DB
177}
178
c37ebc12
KZ
179static int seek_sector(struct fdisk_context *cxt, sector_t secno)
180{
181 off_t offset = (off_t) secno * cxt->sector_size;
182
183 return lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1 ? -errno : 0;
184}
185
186static int read_sector(struct fdisk_context *cxt, sector_t secno,
187 unsigned char *buf)
188{
189 int rc = seek_sector(cxt, secno);
190
191 if (rc < 0)
192 return rc;
193
194 return read(cxt->dev_fd, buf, cxt->sector_size) !=
195 (ssize_t) cxt->sector_size ? -errno : 0;
196}
9dea2923 197
e2ee9178 198/* Allocate a buffer and read a partition table sector */
af0df606 199static int read_pte(struct fdisk_context *cxt, int pno, sector_t offset)
e2ee9178 200{
af0df606
KZ
201 unsigned char *buf;
202 struct pte *pe = self_pte(cxt, pno);
203
204 buf = calloc(1, cxt->sector_size);
205 if (!buf)
206 return -ENOMEM;
e2ee9178 207
9c825fa0
KZ
208 DBG(LABEL, dbgprint("DOS: reading pte %d sector buffer %p", pno, buf));
209
e2ee9178 210 pe->offset = offset;
af0df606 211 pe->sectorbuffer = buf;
ddec0e40 212 pe->private_sectorbuffer = 1;
21770662
KZ
213
214 if (read_sector(cxt, offset, pe->sectorbuffer) != 0)
fcea064b
KZ
215 fdisk_warn(cxt, _("Failed to read extended partition table "
216 "(offset=%jd)"), (uintmax_t) offset);
e2ee9178 217 pe->changed = 0;
6864f168 218 pe->pt_entry = pe->ex_entry = NULL;
af0df606 219 return 0;
e2ee9178
DB
220}
221
e2ee9178 222
f540fe15 223static void clear_partition(struct dos_partition *p)
e2ee9178
DB
224{
225 if (!p)
226 return;
227 p->boot_ind = 0;
da967236
KZ
228 p->bh = 0;
229 p->bs = 0;
230 p->bc = 0;
e2ee9178 231 p->sys_ind = 0;
da967236
KZ
232 p->eh = 0;
233 p->es = 0;
234 p->ec = 0;
b44244cb
KZ
235 dos_partition_set_start(p,0);
236 dos_partition_set_size(p,0);
e2ee9178
DB
237}
238
ddec0e40 239static void dos_init(struct fdisk_context *cxt)
e2ee9178 240{
fcea064b 241 struct fdisk_dos_label *l = self_label(cxt);
e3661531 242 size_t i;
e2ee9178 243
60c1b036
KZ
244 assert(cxt);
245 assert(cxt->label);
246 assert(fdisk_is_disklabel(cxt, DOS));
247
9c825fa0
KZ
248 DBG(LABEL, dbgprint("DOS: initialize, first sector buffer %p", cxt->firstsector));
249
e3661531 250 cxt->label->nparts_max = 4; /* default, unlimited number of logical */
ddec0e40 251
fcea064b
KZ
252 l->ext_index = 0;
253 l->ext_offset = 0;
ddec0e40
KZ
254 l->non_pt_changed = 0;
255
256 memset(l->ptes, 0, sizeof(l->ptes));
e2ee9178
DB
257
258 for (i = 0; i < 4; i++) {
af0df606 259 struct pte *pe = self_pte(cxt, i);
e2ee9178 260
e8bf93de 261 pe->pt_entry = mbr_get_partition(cxt->firstsector, i);
6864f168 262 pe->ex_entry = NULL;
e2ee9178 263 pe->offset = 0;
67987b47 264 pe->sectorbuffer = cxt->firstsector;
9c825fa0 265 pe->private_sectorbuffer = 0;
e2ee9178
DB
266 pe->changed = 0;
267 }
268
4f79f887
KZ
269 if (fdisk_context_listonly(cxt))
270 return;
271 /*
272 * Various warnings...
273 */
274 if (fdisk_missing_geometry(cxt))
275 fdisk_warnx(cxt, _("You can set geometry from the extra functions menu."));
276
277 if (is_dos_compatible(cxt)) {
278 fdisk_warnx(cxt, _("DOS-compatible mode is deprecated."));
279
280 if (cxt->sector_size != cxt->phy_sector_size)
281 fdisk_info(cxt, _(
282 "The device presents a logical sector size that is smaller than "
283 "the physical sector size. Aligning to a physical sector (or optimal "
284 "I/O) size boundary is recommended, or performance may be impacted."));
285 }
286
287 if (fdisk_context_use_cylinders(cxt))
288 fdisk_warnx(cxt, _("Cylinders as display units are deprecated."));
0b52b94c 289
4f79f887 290 if (cxt->total_sectors > UINT_MAX) {
ac1a559a
KZ
291 uint64_t bytes = cxt->total_sectors * cxt->sector_size;
292 char *szstr = size_to_human_string(SIZE_SUFFIX_SPACE
293 | SIZE_SUFFIX_3LETTER, bytes);
fcea064b 294 fdisk_warnx(cxt,
ac1a559a 295 _("The size of this disk is %s (%llu bytes). DOS "
fcea064b
KZ
296 "partition table format can not be used on drives for "
297 "volumes larger than (%llu bytes) for %ld-byte "
298 "sectors. Use GUID partition table format (GPT)."),
ac1a559a 299 szstr, bytes,
0b52b94c
KZ
300 (sector_t ) UINT_MAX * cxt->sector_size,
301 cxt->sector_size);
ac1a559a 302 free(szstr);
0b52b94c 303 }
e2ee9178
DB
304}
305
ddec0e40
KZ
306/* callback called by libfdisk */
307static void dos_deinit(struct fdisk_label *lb)
308{
309 size_t i;
310 struct fdisk_dos_label *l = (struct fdisk_dos_label *) lb;
311
312 for (i = 0; i < ARRAY_SIZE(l->ptes); i++) {
313 struct pte *pe = &l->ptes[i];
314
9c825fa0
KZ
315 if (pe->private_sectorbuffer && pe->sectorbuffer) {
316 DBG(LABEL, dbgprint("DOS: freeing pte %zu sector buffer %p",
317 i, pe->sectorbuffer));
ddec0e40 318 free(pe->sectorbuffer);
9c825fa0 319 }
ddec0e40 320 pe->sectorbuffer = NULL;
9c825fa0 321 pe->private_sectorbuffer = 0;
ddec0e40
KZ
322 }
323
324 memset(l->ptes, 0, sizeof(l->ptes));
325}
326
9ffeb235 327static int dos_delete_partition(struct fdisk_context *cxt, size_t partnum)
61c4cb85 328{
60c1b036
KZ
329 struct fdisk_dos_label *l;
330 struct pte *pe;
af0df606
KZ
331 struct dos_partition *p;
332 struct dos_partition *q;
333
60c1b036
KZ
334 assert(cxt);
335 assert(cxt->label);
336 assert(fdisk_is_disklabel(cxt, DOS));
337
338 pe = self_pte(cxt, partnum);
af0df606
KZ
339 if (!pe)
340 return -EINVAL;
341
9c825fa0
KZ
342 DBG(LABEL, dbgprint("DOS: delete partiton %zu", partnum));
343
60c1b036 344 l = self_label(cxt);
af0df606
KZ
345 p = pe->pt_entry;
346 q = pe->ex_entry;
61c4cb85
DB
347
348 /* Note that for the fifth partition (partnum == 4) we don't actually
349 decrement partitions. */
61c4cb85 350 if (partnum < 4) {
9c825fa0 351 DBG(LABEL, dbgprint("--> delete primary"));
fcea064b 352 if (IS_EXTENDED(p->sys_ind) && partnum == l->ext_index) {
e3661531 353 cxt->label->nparts_max = 4;
fcea064b
KZ
354 l->ptes[l->ext_index].ex_entry = NULL;
355 l->ext_offset = 0;
61c4cb85 356 }
af0df606 357 partition_set_changed(cxt, partnum, 1);
61c4cb85
DB
358 clear_partition(p);
359 } else if (!q->sys_ind && partnum > 4) {
9c825fa0 360 DBG(LABEL, dbgprint("--> delete logical [last in the chain]"));
e3661531 361 --cxt->label->nparts_max;
61c4cb85 362 --partnum;
af0df606
KZ
363 clear_partition(l->ptes[partnum].ex_entry);
364 partition_set_changed(cxt, partnum, 1);
61c4cb85 365 } else {
9c825fa0 366 DBG(LABEL, dbgprint("--> delete logical [non-last, move down]"));
61c4cb85
DB
367 if (partnum > 4) {
368 /* delete this link in the chain */
af0df606 369 p = l->ptes[partnum - 1].ex_entry;
61c4cb85 370 *p = *q;
b44244cb
KZ
371 dos_partition_set_start(p, dos_partition_get_start(q));
372 dos_partition_set_size(p, dos_partition_get_size(q));
af0df606 373 partition_set_changed(cxt, partnum - 1, 1);
e3661531 374 } else if (cxt->label->nparts_max > 5) { /* 5 will be moved to 4 */
61c4cb85 375 /* the first logical in a longer chain */
af0df606 376 pe = &l->ptes[5];
61c4cb85 377
af0df606
KZ
378 if (pe->pt_entry) /* prevent SEGFAULT */
379 dos_partition_set_start(pe->pt_entry,
380 get_abs_partition_start(pe) -
fcea064b
KZ
381 l->ext_offset);
382 pe->offset = l->ext_offset;
af0df606 383 partition_set_changed(cxt, 5, 1);
61c4cb85
DB
384 }
385
e3661531
KZ
386 if (cxt->label->nparts_max > 5) {
387 cxt->label->nparts_max--;
9c825fa0
KZ
388 if (l->ptes[partnum].private_sectorbuffer) {
389 DBG(LABEL, dbgprint("--> freeing pte %zu sector buffer %p",
390 partnum, l->ptes[partnum].sectorbuffer));
391 free(l->ptes[partnum].sectorbuffer);
392 }
e3661531 393 while (partnum < cxt->label->nparts_max) {
9c825fa0 394 DBG(LABEL, dbgprint("--> moving pte %zu <-- %zd", partnum, partnum + 1));
af0df606 395 l->ptes[partnum] = l->ptes[partnum + 1];
61c4cb85
DB
396 partnum++;
397 }
9c825fa0 398 memset(&l->ptes[partnum], 0, sizeof(struct pte));
61c4cb85
DB
399 } else
400 /* the only logical: clear only */
af0df606 401 clear_partition(l->ptes[partnum].pt_entry);
61c4cb85 402 }
1f5eb51b 403
9ffeb235 404 fdisk_label_set_changed(cxt->label, 1);
1f5eb51b 405 return 0;
61c4cb85
DB
406}
407
7737f698 408static void read_extended(struct fdisk_context *cxt, int ext)
e2ee9178 409{
e3661531 410 size_t i;
e2ee9178 411 struct pte *pex;
f540fe15 412 struct dos_partition *p, *q;
fcea064b 413 struct fdisk_dos_label *l = self_label(cxt);
e2ee9178 414
fcea064b 415 l->ext_index = ext;
af0df606 416 pex = self_pte(cxt, ext);
6864f168 417 pex->ex_entry = pex->pt_entry;
e2ee9178 418
6864f168 419 p = pex->pt_entry;
b44244cb 420 if (!dos_partition_get_start(p)) {
ac1a559a 421 fdisk_warnx(cxt, _("Bad offset in primary extended partition."));
e2ee9178
DB
422 return;
423 }
424
9c825fa0
KZ
425 DBG(LABEL, dbgprint("DOS: REading extended %d", ext));
426
e2ee9178 427 while (IS_EXTENDED (p->sys_ind)) {
af0df606 428 struct pte *pe = self_pte(cxt, cxt->label->nparts_max);
e2ee9178 429
e3661531 430 if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
e2ee9178
DB
431 /* This is not a Linux restriction, but
432 this program uses arrays of size MAXIMUM_PARTS.
433 Do not try to `improve' this test. */
af0df606
KZ
434 struct pte *pre = self_pte(cxt,
435 cxt->label->nparts_max - 1);
fcea064b
KZ
436 fdisk_warnx(cxt,
437 _("Omitting partitions after #%zd. They will be deleted "
438 "if you save this partition table."),
e3661531 439 cxt->label->nparts_max);
fcea064b 440
6864f168 441 clear_partition(pre->ex_entry);
af0df606
KZ
442 partition_set_changed(cxt,
443 cxt->label->nparts_max - 1, 1);
e2ee9178
DB
444 return;
445 }
446
fcea064b
KZ
447 read_pte(cxt, cxt->label->nparts_max,
448 l->ext_offset + dos_partition_get_start(p));
e2ee9178 449
fcea064b
KZ
450 if (!l->ext_offset)
451 l->ext_offset = dos_partition_get_start(p);
e2ee9178 452
e8bf93de 453 q = p = mbr_get_partition(pe->sectorbuffer, 0);
fcea064b 454
b44244cb 455 for (i = 0; i < 4; i++, p++) if (dos_partition_get_size(p)) {
e2ee9178 456 if (IS_EXTENDED (p->sys_ind)) {
6864f168 457 if (pe->ex_entry)
fcea064b
KZ
458 fdisk_warnx(cxt, _(
459 "Extra link pointer in partition "
ac1a559a 460 "table %zd."),
fcea064b 461 cxt->label->nparts_max + 1);
e2ee9178 462 else
6864f168 463 pe->ex_entry = p;
e2ee9178 464 } else if (p->sys_ind) {
6864f168 465 if (pe->pt_entry)
fcea064b
KZ
466 fdisk_warnx(cxt, _(
467 "Ignoring extra data in partition "
ac1a559a 468 "table %zd."),
fcea064b 469 cxt->label->nparts_max + 1);
e2ee9178 470 else
6864f168 471 pe->pt_entry = p;
e2ee9178
DB
472 }
473 }
474
475 /* very strange code here... */
6864f168
KZ
476 if (!pe->pt_entry) {
477 if (q != pe->ex_entry)
478 pe->pt_entry = q;
e2ee9178 479 else
6864f168 480 pe->pt_entry = q + 1;
e2ee9178 481 }
6864f168
KZ
482 if (!pe->ex_entry) {
483 if (q != pe->pt_entry)
484 pe->ex_entry = q;
e2ee9178 485 else
6864f168 486 pe->ex_entry = q + 1;
e2ee9178
DB
487 }
488
6864f168 489 p = pe->ex_entry;
e3661531 490 cxt->label->nparts_cur = ++cxt->label->nparts_max;
e2ee9178
DB
491 }
492
493 /* remove empty links */
494 remove:
af0df606 495 q = self_partition(cxt, 4);
e3661531 496 for (i = 4; i < cxt->label->nparts_max; i++) {
af0df606 497 p = self_partition(cxt, i);
e2ee9178 498
af0df606
KZ
499 if (!dos_partition_get_size(p) &&
500 (cxt->label->nparts_max > 5 || q->sys_ind)) {
fcea064b 501 fdisk_info(cxt, _("omitting empty partition (%zd)"), i+1);
9ffeb235 502 dos_delete_partition(cxt, i);
e2ee9178
DB
503 goto remove; /* numbering changed */
504 }
505 }
506}
507
21fe3dde 508static int dos_get_disklabel_id(struct fdisk_context *cxt, char **id)
e2ee9178 509{
21fe3dde
KZ
510 unsigned int num;
511
512 assert(cxt);
513 assert(id);
514 assert(cxt->label);
515 assert(fdisk_is_disklabel(cxt, DOS));
516
517 num = mbr_get_id(cxt->firstsector);
518 if (asprintf(id, "0x%08x", num) > 0)
519 return 0;
520
521 return -ENOMEM;
e2ee9178
DB
522}
523
9ffeb235 524static int dos_create_disklabel(struct fdisk_context *cxt)
e2ee9178
DB
525{
526 unsigned int id;
527
9ffeb235
KZ
528 assert(cxt);
529 assert(cxt->label);
530 assert(fdisk_is_disklabel(cxt, DOS));
531
9c825fa0
KZ
532 DBG(LABEL, dbgprint("DOS: creating new disklabel"));
533
e2ee9178 534 /* random disk signature */
c544aa2c 535 random_get_bytes(&id, sizeof(id));
e2ee9178 536
e53ced85 537 dos_init(cxt);
67987b47 538 fdisk_zeroize_firstsector(cxt);
bddd84e7 539 fdisk_label_set_changed(cxt->label, 1);
e2ee9178
DB
540
541 /* Generate an MBR ID for this disk */
67987b47 542 mbr_set_id(cxt->firstsector, id);
e2ee9178
DB
543
544 /* Put MBR signature */
67987b47 545 mbr_set_magic(cxt->firstsector);
ac1a559a
KZ
546
547 fdisk_sinfo(cxt, FDISK_INFO_SUCCESS,
548 ("Created a new DOS disklabel with disk "
549 "identifier 0x%08x."), id);
a71601af 550 return 0;
e2ee9178
DB
551}
552
35b1f0a4 553static int dos_set_disklabel_id(struct fdisk_context *cxt)
e2ee9178 554{
0a49e5d5
KZ
555 char *end = NULL, *str = NULL;
556 unsigned int id, old;
35b1f0a4 557 struct fdisk_dos_label *l;
0a49e5d5 558 int rc;
e2ee9178 559
35b1f0a4
KZ
560 assert(cxt);
561 assert(cxt->label);
562 assert(fdisk_is_disklabel(cxt, DOS));
563
9c825fa0
KZ
564 DBG(LABEL, dbgprint("DOS: setting Id"));
565
35b1f0a4 566 l = self_label(cxt);
0a49e5d5
KZ
567 old = mbr_get_id(cxt->firstsector);
568 rc = fdisk_ask_string(cxt,
a0862e26 569 _("Enter the new disk identifier"), &str);
0a49e5d5
KZ
570 if (rc)
571 return rc;
e2ee9178 572
0a49e5d5
KZ
573 errno = 0;
574 id = strtoul(str, &end, 0);
575 if (errno || str == end || (end && *end)) {
576 fdisk_warnx(cxt, _("Incorrect value."));
577 return -EINVAL;
578 }
e2ee9178 579
e2ee9178 580
0a49e5d5 581 mbr_set_id(cxt->firstsector, id);
8b3d9241 582 l->non_pt_changed = 1;
bddd84e7 583 fdisk_label_set_changed(cxt->label, 1);
ac1a559a
KZ
584
585 fdisk_sinfo(cxt, FDISK_INFO_SUCCESS,
586 _("Disk identifier changed from 0x%08x to 0x%08x."),
587 old, id);
0a49e5d5 588 return 0;
e2ee9178
DB
589}
590
9a5e29e9
KZ
591static void get_partition_table_geometry(struct fdisk_context *cxt,
592 unsigned int *ph, unsigned int *ps)
593{
67987b47 594 unsigned char *bufp = cxt->firstsector;
f540fe15 595 struct dos_partition *p;
9a5e29e9
KZ
596 int i, h, s, hh, ss;
597 int first = 1;
598 int bad = 0;
599
600 hh = ss = 0;
60c1b036 601 for (i = 0; i < 4; i++) {
e8bf93de 602 p = mbr_get_partition(bufp, i);
9a5e29e9 603 if (p->sys_ind != 0) {
da967236
KZ
604 h = p->eh + 1;
605 s = (p->es & 077);
9a5e29e9
KZ
606 if (first) {
607 hh = h;
608 ss = s;
609 first = 0;
610 } else if (hh != h || ss != s)
611 bad = 1;
612 }
613 }
614
615 if (!first && !bad) {
616 *ph = hh;
617 *ps = ss;
618 }
cf3808e4 619
fcea064b 620 DBG(LABEL, dbgprint("DOS PT geometry: heads=%u, sectors=%u", *ph, *ps));
9a5e29e9
KZ
621}
622
9ffeb235 623static int dos_reset_alignment(struct fdisk_context *cxt)
cf3808e4 624{
9ffeb235
KZ
625 assert(cxt);
626 assert(cxt->label);
627 assert(fdisk_is_disklabel(cxt, DOS));
628
cf3808e4 629 /* overwrite necessary stuff by DOS deprecated stuff */
852ce62b 630 if (is_dos_compatible(cxt)) {
9c825fa0 631 DBG(LABEL, dbgprint("DOS: reseting alignemnt for DOS-comaptiblem PT"));
cf3808e4
KZ
632 if (cxt->geom.sectors)
633 cxt->first_lba = cxt->geom.sectors; /* usually 63 */
634
635 cxt->grain = cxt->sector_size; /* usually 512 */
636 }
cf3808e4
KZ
637
638 return 0;
639}
9a5e29e9 640
9ed268d0
KZ
641/* TODO: move to include/pt-dos.h and share with libblkid */
642#define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1"
643#define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1)
644
9ffeb235 645static int dos_probe_label(struct fdisk_context *cxt)
e2ee9178 646{
e3661531 647 size_t i;
9a5e29e9 648 unsigned int h = 0, s = 0;
e2ee9178 649
e3661531
KZ
650 assert(cxt);
651 assert(cxt->label);
9ffeb235 652 assert(fdisk_is_disklabel(cxt, DOS));
e3661531 653
9ed268d0
KZ
654 /* ignore disks with AIX magic number */
655 if (memcmp(cxt->firstsector, AIX_MAGIC_STRING, AIX_MAGIC_STRLEN) == 0)
656 return 0;
657
67987b47 658 if (!mbr_is_valid_magic(cxt->firstsector))
e2ee9178
DB
659 return 0;
660
e53ced85 661 dos_init(cxt);
e2ee9178 662
9a5e29e9
KZ
663 get_partition_table_geometry(cxt, &h, &s);
664 if (h && s) {
665 cxt->geom.heads = h;
666 cxt->geom.sectors = s;
667 }
668
e2ee9178 669 for (i = 0; i < 4; i++) {
af0df606 670 struct pte *pe = self_pte(cxt, i);
e2ee9178 671
6864f168 672 if (!is_cleared_partition(pe->pt_entry))
e3661531
KZ
673 cxt->label->nparts_cur++;
674
6864f168 675 if (IS_EXTENDED (pe->pt_entry->sys_ind)) {
e3661531 676 if (cxt->label->nparts_max != 4)
fcea064b
KZ
677 fdisk_warnx(cxt, _(
678 "Ignoring extra extended partition %zd"),
679 i + 1);
e2ee9178 680 else
7737f698 681 read_extended(cxt, i);
e2ee9178
DB
682 }
683 }
684
e3661531 685 for (i = 3; i < cxt->label->nparts_max; i++) {
af0df606 686 struct pte *pe = self_pte(cxt, i);
e2ee9178 687
67188340 688 if (!mbr_is_valid_magic(pe->sectorbuffer)) {
fcea064b
KZ
689 fdisk_info(cxt, _(
690 "Invalid flag 0x%02x%02x of partition table %zd will "
691 "be corrected by w(rite)"),
6fe71269
KZ
692 pe->sectorbuffer[510],
693 pe->sectorbuffer[511],
694 i + 1);
af0df606 695 partition_set_changed(cxt, 1, 1);
e2ee9178
DB
696 }
697 }
698
699 return 1;
700}
701
702/*
703 * Avoid warning about DOS partitions when no DOS partition was changed.
704 * Here a heuristic "is probably dos partition".
705 * We might also do the opposite and warn in all cases except
706 * for "is probably nondos partition".
707 */
02460b8a 708static int is_dos_partition(int t)
e2ee9178
DB
709{
710 return (t == 1 || t == 4 || t == 6 ||
711 t == 0x0b || t == 0x0c || t == 0x0e ||
712 t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 ||
713 t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 ||
714 t == 0xc1 || t == 0xc4 || t == 0xc6);
715}
9dea2923 716
e53ced85
DB
717static void set_partition(struct fdisk_context *cxt,
718 int i, int doext, sector_t start,
24def09d 719 sector_t stop, int sysid)
9dea2923 720{
af0df606 721 struct pte *pe = self_pte(cxt, i);
f540fe15 722 struct dos_partition *p;
24def09d 723 sector_t offset;
9dea2923 724
80063996
KZ
725 DBG(LABEL, dbgprint("DOS: setting partition %d%s, start=%zu, stop=%zu, sysid=%02x",
726 i, doext ? " [extended]" : "",
727 (size_t) start, (size_t) stop, sysid));
728
9dea2923 729 if (doext) {
fcea064b 730 struct fdisk_dos_label *l = self_label(cxt);
af0df606 731 p = pe->ex_entry;
fcea064b 732 offset = l->ext_offset;
9dea2923 733 } else {
af0df606
KZ
734 p = pe->pt_entry;
735 offset = pe->offset;
9dea2923
DB
736 }
737 p->boot_ind = 0;
738 p->sys_ind = sysid;
b44244cb
KZ
739 dos_partition_set_start(p, start - offset);
740 dos_partition_set_size(p, stop - start + 1);
9dea2923 741
b4bfbadd
KZ
742 if (!doext) {
743 struct fdisk_parttype *t = fdisk_get_parttype_from_code(cxt, sysid);
744 fdisk_info_new_partition(cxt, i + 1, start, stop, t);
745 }
852ce62b 746 if (is_dos_compatible(cxt) && (start/(cxt->geom.sectors*cxt->geom.heads) > 1023))
24cd580b 747 start = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
da967236 748 set_hsc(p->bh, p->bs, p->bc, start);
852ce62b 749 if (is_dos_compatible(cxt) && (stop/(cxt->geom.sectors*cxt->geom.heads) > 1023))
24cd580b 750 stop = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
da967236 751 set_hsc(p->eh, p->es, p->ec, stop);
af0df606 752 partition_set_changed(cxt, i, 1);
9dea2923
DB
753}
754
cf3808e4
KZ
755static sector_t get_unused_start(struct fdisk_context *cxt,
756 int part_n, sector_t start,
24def09d 757 sector_t first[], sector_t last[])
9dea2923 758{
e3661531 759 size_t i;
9dea2923 760
e3661531 761 for (i = 0; i < cxt->label->nparts_max; i++) {
24def09d 762 sector_t lastplusoff;
af0df606 763 struct pte *pe = self_pte(cxt, i);
9dea2923 764
af0df606 765 if (start == pe->offset)
cf3808e4
KZ
766 start += cxt->first_lba;
767 lastplusoff = last[i] + ((part_n < 4) ? 0 : cxt->first_lba);
9dea2923
DB
768 if (start >= first[i] && start <= lastplusoff)
769 start = lastplusoff + 1;
770 }
771
772 return start;
773}
774
e3661531
KZ
775static void fill_bounds(struct fdisk_context *cxt,
776 sector_t *first, sector_t *last)
cc0c4e56 777{
e3661531 778 size_t i;
af0df606 779 struct pte *pe = self_pte(cxt, 0);
f540fe15 780 struct dos_partition *p;
cc0c4e56 781
e3661531 782 for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
6864f168 783 p = pe->pt_entry;
cc0c4e56
KZ
784 if (!p->sys_ind || IS_EXTENDED (p->sys_ind)) {
785 first[i] = 0xffffffff;
786 last[i] = 0;
787 } else {
61eaad50 788 first[i] = get_abs_partition_start(pe);
b44244cb 789 last[i] = first[i] + dos_partition_get_size(p) - 1;
cc0c4e56
KZ
790 }
791 }
792}
793
8254c3a5 794static int add_partition(struct fdisk_context *cxt, int n, struct fdisk_parttype *t)
9dea2923 795{
0e07540d 796 int sys, read = 0, rc;
e3661531 797 size_t i;
fcea064b 798 struct fdisk_dos_label *l = self_label(cxt);
af0df606 799 struct dos_partition *p = self_partition(cxt, n);
fcea064b 800 struct dos_partition *q = self_partition(cxt, l->ext_index);
af0df606 801
24def09d 802 sector_t start, stop = 0, limit, temp,
e3661531
KZ
803 first[cxt->label->nparts_max],
804 last[cxt->label->nparts_max];
9dea2923 805
9c825fa0
KZ
806 DBG(LABEL, dbgprint("DOS: adding partition %d", n));
807
bb58112e 808 sys = t ? t->type : MBR_LINUX_DATA_PARTITION;
ed470672 809
9dea2923 810 if (p && p->sys_ind) {
83217641
BS
811 fdisk_warnx(cxt, _("Partition %zd is already defined. "
812 "Delete it before re-adding it."), n + 1);
8254c3a5 813 return -EINVAL;
9dea2923 814 }
e3661531 815 fill_bounds(cxt, first, last);
9dea2923 816 if (n < 4) {
cf3808e4 817 start = cxt->first_lba;
ec10aa67 818 if (fdisk_context_use_cylinders(cxt) || !cxt->total_sectors)
24cd580b 819 limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
9dea2923 820 else
618882d6 821 limit = cxt->total_sectors - 1;
9dea2923
DB
822
823 if (limit > UINT_MAX)
824 limit = UINT_MAX;
825
fcea064b
KZ
826 if (l->ext_offset) {
827 first[l->ext_index] = l->ext_offset;
828 last[l->ext_index] = dos_partition_get_start(q) +
b44244cb 829 dos_partition_get_size(q) - 1;
9dea2923
DB
830 }
831 } else {
fcea064b
KZ
832 start = l->ext_offset + cxt->first_lba;
833 limit = dos_partition_get_start(q)
834 + dos_partition_get_size(q) - 1;
9dea2923 835 }
ec10aa67 836 if (fdisk_context_use_cylinders(cxt))
80063996 837 for (i = 0; i < cxt->label->nparts_max; i++) {
fcea064b
KZ
838 first[i] = (cround(cxt, first[i]) - 1)
839 * fdisk_context_get_units_per_sector(cxt);
80063996 840 }
9dea2923 841
0e07540d
KZ
842 /*
843 * Ask for first sector
844 */
9dea2923 845 do {
24def09d 846 sector_t dflt, aligned;
9dea2923
DB
847
848 temp = start;
cf3808e4 849 dflt = start = get_unused_start(cxt, n, start, first, last);
9dea2923
DB
850
851 /* the default sector should be aligned and unused */
852 do {
9475cc78 853 aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
cf3808e4 854 dflt = get_unused_start(cxt, n, aligned, first, last);
9dea2923
DB
855 } while (dflt != aligned && dflt > aligned && dflt < limit);
856
857 if (dflt >= limit)
858 dflt = start;
859 if (start > limit)
860 break;
fcea064b
KZ
861 if (start >= temp + fdisk_context_get_units_per_sector(cxt)
862 && read) {
ac1a559a 863 fdisk_info(cxt, _("Sector %llu is already allocated."),
fcea064b 864 temp);
9dea2923
DB
865 temp = start;
866 read = 0;
867 }
0e07540d 868
9dea2923 869 if (!read && start == temp) {
c9f63764 870 sector_t j = start;
0e07540d
KZ
871 struct fdisk_ask *ask = fdisk_new_ask();
872
873 if (fdisk_context_use_cylinders(cxt))
874 fdisk_ask_set_query(ask, _("First cylinder"));
875 else
876 fdisk_ask_set_query(ask, _("First sector"));
877
878 fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
879 fdisk_ask_number_set_low(ask, cround(cxt, j));
880 fdisk_ask_number_set_default(ask, cround(cxt, dflt));
881 fdisk_ask_number_set_high(ask, cround(cxt, limit));
882
883 rc = fdisk_do_ask(cxt, ask);
884 if (!rc)
885 start = fdisk_ask_number_get_result(ask);
886 fdisk_free_ask(ask);
887 if (rc)
888 return rc;
9dea2923 889
ec10aa67 890 if (fdisk_context_use_cylinders(cxt)) {
fcea064b
KZ
891 start = (start - 1)
892 * fdisk_context_get_units_per_sector(cxt);
0e07540d
KZ
893 if (start < j)
894 start = j;
9dea2923
DB
895 }
896 read = 1;
897 }
898 } while (start != temp || !read);
0e07540d 899
9dea2923 900 if (n > 4) { /* NOT for fifth partition */
af0df606 901 struct pte *pe = self_pte(cxt, n);
9dea2923 902
cf3808e4 903 pe->offset = start - cxt->first_lba;
fcea064b 904 if (pe->offset == l->ext_offset) { /* must be corrected */
9dea2923 905 pe->offset++;
cf3808e4 906 if (cxt->first_lba == 1)
9dea2923
DB
907 start++;
908 }
909 }
910
e3661531 911 for (i = 0; i < cxt->label->nparts_max; i++) {
af0df606 912 struct pte *pe = self_pte(cxt, i);
9dea2923
DB
913
914 if (start < pe->offset && limit >= pe->offset)
915 limit = pe->offset - 1;
916 if (start < first[i] && limit >= first[i])
917 limit = first[i] - 1;
918 }
919 if (start > limit) {
ac1a559a 920 fdisk_info(cxt, _("No free sectors available."));
9dea2923 921 if (n > 4)
e3661531 922 cxt->label->nparts_max--;
8254c3a5 923 return -ENOSPC;
9dea2923 924 }
ec10aa67 925 if (cround(cxt, start) == cround(cxt, limit)) {
9dea2923
DB
926 stop = limit;
927 } else {
0e07540d
KZ
928 /*
929 * Ask for last sector
930 */
931 struct fdisk_ask *ask = fdisk_new_ask();
9dea2923 932
0e07540d 933 fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
9dea2923 934
ec10aa67 935 if (fdisk_context_use_cylinders(cxt)) {
0e07540d
KZ
936 fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
937 fdisk_ask_number_set_unit(ask,
938 cxt->sector_size *
939 fdisk_context_get_units_per_sector(cxt));
940 } else {
941 fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
942 fdisk_ask_number_set_unit(ask,cxt->sector_size);
943 }
944
945 fdisk_ask_number_set_low(ask, cround(cxt, start));
946 fdisk_ask_number_set_default(ask, cround(cxt, limit));
947 fdisk_ask_number_set_high(ask, cround(cxt, limit));
948 fdisk_ask_number_set_base(ask, cround(cxt, start)); /* base for relative input */
0e07540d
KZ
949
950 rc = fdisk_do_ask(cxt, ask);
951 if (rc) {
952 fdisk_free_ask(ask);
953 return rc;
9dea2923
DB
954 }
955
0e07540d
KZ
956 stop = fdisk_ask_number_get_result(ask);
957
80063996
KZ
958 if (fdisk_context_use_cylinders(cxt)) {
959 stop = stop * fdisk_context_get_units_per_sector(cxt) - 1;
960 if (stop >limit)
961 stop = limit;
962 }
0e07540d
KZ
963 if (fdisk_ask_number_is_relative(ask)
964 && alignment_required(cxt)) {
9dea2923 965 /* the last sector has not been exactly requested (but
0e07540d
KZ
966 * defined by +size{K,M,G} convention), so be smart and
967 * align the end of the partition. The next partition
968 * will start at phy.block boundary.
9dea2923 969 */
9475cc78 970 stop = fdisk_align_lba_in_range(cxt, stop, start, limit) - 1;
9dea2923
DB
971 if (stop > limit)
972 stop = limit;
973 }
0e07540d 974 fdisk_free_ask(ask);
9dea2923
DB
975 }
976
e53ced85 977 set_partition(cxt, n, 0, start, stop, sys);
af0df606
KZ
978 if (n > 4) {
979 struct pte *pe = self_pte(cxt, n);
bb58112e
KZ
980 set_partition(cxt, n - 1, 1, pe->offset, stop,
981 MBR_DOS_EXTENDED_PARTITION);
af0df606 982 }
9dea2923 983
bb58112e 984 if (IS_EXTENDED(sys)) {
af0df606
KZ
985 struct pte *pe4 = self_pte(cxt, 4);
986 struct pte *pen = self_pte(cxt, n);
9dea2923 987
fcea064b 988 l->ext_index = n;
6864f168 989 pen->ex_entry = p;
fcea064b 990 pe4->offset = l->ext_offset = start;
ddec0e40
KZ
991 pe4->sectorbuffer = calloc(1, cxt->sector_size);
992 if (!pe4->sectorbuffer)
993 return -ENOMEM;
9c825fa0 994 DBG(LABEL, dbgprint("DOS: add partition, sector buffer %p", pe4->sectorbuffer));
ddec0e40 995 pe4->private_sectorbuffer = 1;
e8bf93de 996 pe4->pt_entry = mbr_get_partition(pe4->sectorbuffer, 0);
6864f168 997 pe4->ex_entry = pe4->pt_entry + 1;
af0df606
KZ
998
999 partition_set_changed(cxt, 4, 1);
e3661531 1000 cxt->label->nparts_max = 5;
9dea2923 1001 }
8254c3a5 1002
bddd84e7 1003 fdisk_label_set_changed(cxt->label, 1);
8254c3a5 1004 return 0;
9dea2923
DB
1005}
1006
8254c3a5 1007static int add_logical(struct fdisk_context *cxt)
9dea2923 1008{
af0df606
KZ
1009 struct dos_partition *p4 = self_partition(cxt, 4);
1010
e3661531
KZ
1011 assert(cxt);
1012 assert(cxt->label);
1013
af0df606
KZ
1014 if (cxt->label->nparts_max > 5 || p4->sys_ind) {
1015 struct pte *pe = self_pte(cxt, cxt->label->nparts_max);
1016
1017 pe->sectorbuffer = calloc(1, cxt->sector_size);
1018 if (!pe->sectorbuffer)
1019 return -ENOMEM;
9c825fa0 1020 DBG(LABEL, dbgprint("DOS: add logical, sector buffer %p", pe->sectorbuffer));
ddec0e40 1021 pe->private_sectorbuffer = 1;
e8bf93de 1022 pe->pt_entry = mbr_get_partition(pe->sectorbuffer, 0);
6864f168 1023 pe->ex_entry = pe->pt_entry + 1;
9dea2923 1024 pe->offset = 0;
af0df606
KZ
1025
1026 partition_set_changed(cxt, cxt->label->nparts_max, 1);
e3661531 1027 cxt->label->nparts_max++;
9dea2923 1028 }
fcea064b
KZ
1029 fdisk_info(cxt, _("Adding logical partition %zd"),
1030 cxt->label->nparts_max);
e3661531 1031 return add_partition(cxt, cxt->label->nparts_max - 1, NULL);
9dea2923
DB
1032}
1033
e3661531 1034static void check(struct fdisk_context *cxt, size_t n,
5dfca634
KZ
1035 unsigned int h, unsigned int s, unsigned int c,
1036 unsigned int start)
1037{
1038 unsigned int total, real_s, real_c;
1039
1040 real_s = sector(s) - 1;
1041 real_c = cylinder(s, c);
1042 total = (real_c * cxt->geom.sectors + real_s) * cxt->geom.heads + h;
fcea064b 1043
5dfca634 1044 if (!total)
fcea064b 1045 fdisk_warnx(cxt, _("Partition %zd: contains sector 0"), n);
5dfca634 1046 if (h >= cxt->geom.heads)
fcea064b
KZ
1047 fdisk_warnx(cxt, _("Partition %zd: head %d greater than "
1048 "maximum %d"), n, h + 1, cxt->geom.heads);
5dfca634 1049 if (real_s >= cxt->geom.sectors)
fcea064b
KZ
1050 fdisk_warnx(cxt, _("Partition %zd: sector %d greater than "
1051 "maximum %llu"), n, s, cxt->geom.sectors);
5dfca634 1052 if (real_c >= cxt->geom.cylinders)
fcea064b
KZ
1053 fdisk_warnx(cxt, _("Partition %zd: cylinder %d greater than "
1054 "maximum %llu"),
1055 n, real_c + 1,
1056 cxt->geom.cylinders);
1057
5dfca634 1058 if (cxt->geom.cylinders <= 1024 && start != total)
fcea064b
KZ
1059 fdisk_warnx(cxt, _("Partition %zd: previous sectors %d "
1060 "disagrees with total %d"), n, start, total);
5dfca634
KZ
1061}
1062
1063/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1064 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1065 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1066 * Lubkin Oct. 1991). */
1067
1068static void
1069long2chs(struct fdisk_context *cxt, unsigned long ls,
1070 unsigned int *c, unsigned int *h, unsigned int *s) {
1071 int spc = cxt->geom.heads * cxt->geom.sectors;
1072
1073 *c = ls / spc;
1074 ls = ls % spc;
1075 *h = ls / cxt->geom.sectors;
1076 *s = ls % cxt->geom.sectors + 1; /* sectors count from 1 */
1077}
1078
f540fe15 1079static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p,
e3661531 1080 size_t partition)
5dfca634
KZ
1081{
1082 unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */
1083 unsigned int pec, peh, pes; /* physical ending c, h, s */
1084 unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */
1085 unsigned int lec, leh, les; /* logical ending c, h, s */
1086
1087 if (!is_dos_compatible(cxt))
1088 return;
1089
1090 if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
1091 return; /* do not check extended partitions */
1092
60c1b036 1093 /* physical beginning c, h, s */
da967236
KZ
1094 pbc = (p->bc & 0xff) | ((p->bs << 2) & 0x300);
1095 pbh = p->bh;
1096 pbs = p->bs & 0x3f;
5dfca634 1097
60c1b036 1098 /* physical ending c, h, s */
da967236
KZ
1099 pec = (p->ec & 0xff) | ((p->es << 2) & 0x300);
1100 peh = p->eh;
1101 pes = p->es & 0x3f;
5dfca634 1102
60c1b036 1103 /* compute logical beginning (c, h, s) */
b44244cb 1104 long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs);
5dfca634 1105
60c1b036 1106 /* compute logical ending (c, h, s) */
b44244cb 1107 long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les);
5dfca634 1108
60c1b036 1109 /* Same physical / logical beginning? */
fcea064b
KZ
1110 if (cxt->geom.cylinders <= 1024
1111 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
1112 fdisk_warnx(cxt, _("Partition %zd: different physical/logical "
1113 "beginnings (non-Linux?): "
1114 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1115 partition + 1,
1116 pbc, pbh, pbs,
1117 lbc, lbh, lbs);
5dfca634
KZ
1118 }
1119
60c1b036 1120 /* Same physical / logical ending? */
fcea064b
KZ
1121 if (cxt->geom.cylinders <= 1024
1122 && (pec != lec || peh != leh || pes != les)) {
1123 fdisk_warnx(cxt, _("Partition %zd: different physical/logical "
1124 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1125 partition + 1,
1126 pec, peh, pes,
1127 lec, leh, les);
5dfca634
KZ
1128 }
1129
60c1b036 1130 /* Ending on cylinder boundary? */
5dfca634 1131 if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
fcea064b
KZ
1132 fdisk_warnx(cxt, _("Partition %zd: does not end on "
1133 "cylinder boundary."),
5dfca634
KZ
1134 partition + 1);
1135 }
1136}
1137
9ffeb235 1138static int dos_verify_disklabel(struct fdisk_context *cxt)
2ca61a61 1139{
e3661531 1140 size_t i, j;
2ca61a61 1141 sector_t total = 1, n_sectors = cxt->total_sectors;
e3661531
KZ
1142 unsigned long long first[cxt->label->nparts_max],
1143 last[cxt->label->nparts_max];
f540fe15 1144 struct dos_partition *p;
fcea064b 1145 struct fdisk_dos_label *l = self_label(cxt);
2ca61a61 1146
9ffeb235
KZ
1147 assert(fdisk_is_disklabel(cxt, DOS));
1148
e3661531
KZ
1149 fill_bounds(cxt, first, last);
1150 for (i = 0; i < cxt->label->nparts_max; i++) {
af0df606 1151 struct pte *pe = self_pte(cxt, i);
2ca61a61 1152
af0df606 1153 p = self_partition(cxt, i);
bb58112e 1154 if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) {
2ca61a61 1155 check_consistency(cxt, p, i);
61eaad50
KZ
1156 fdisk_warn_alignment(cxt, get_abs_partition_start(pe), i);
1157 if (get_abs_partition_start(pe) < first[i])
fcea064b
KZ
1158 fdisk_warnx(cxt, _(
1159 "Partition %zd: bad start-of-data."),
1160 i + 1);
1161
1162 check(cxt, i + 1, p->eh, p->es, p->ec, last[i]);
2ca61a61 1163 total += last[i] + 1 - first[i];
fcea064b
KZ
1164
1165 for (j = 0; j < i; j++) {
2ca61a61
DB
1166 if ((first[i] >= first[j] && first[i] <= last[j])
1167 || ((last[i] <= last[j] && last[i] >= first[j]))) {
fcea064b
KZ
1168
1169 fdisk_warnx(cxt, _("Partition %zd: "
1170 "overlaps partition %zd."),
1171 j + 1, i + 1);
1172
2ca61a61
DB
1173 total += first[i] >= first[j] ?
1174 first[i] : first[j];
1175 total -= last[i] <= last[j] ?
1176 last[i] : last[j];
1177 }
fcea064b 1178 }
2ca61a61
DB
1179 }
1180 }
1181
fcea064b 1182 if (l->ext_offset) {
5d324c6b 1183 sector_t e_last;
fcea064b 1184 p = self_partition(cxt, l->ext_index);
5d324c6b 1185 e_last = dos_partition_get_start(p)
af0df606 1186 + dos_partition_get_size(p) - 1;
2ca61a61 1187
e3661531 1188 for (i = 4; i < cxt->label->nparts_max; i++) {
2ca61a61 1189 total++;
af0df606
KZ
1190 p = self_partition(cxt, i);
1191
2ca61a61 1192 if (!p->sys_ind) {
e3661531 1193 if (i != 4 || i + 1 < cxt->label->nparts_max)
fcea064b 1194 fdisk_warnx(cxt,
ac1a559a 1195 _("Partition %zd: empty."),
fcea064b
KZ
1196 i + 1);
1197 } else if (first[i] < l->ext_offset
1198 || last[i] > e_last) {
1199
1200 fdisk_warnx(cxt, _("Logical partition %zd: "
ac1a559a 1201 "not entirely in partition %zd."),
fcea064b 1202 i + 1, l->ext_index + 1);
2ca61a61 1203 }
2ca61a61
DB
1204 }
1205 }
1206
1207 if (total > n_sectors)
fcea064b
KZ
1208 fdisk_warnx(cxt, _("Total allocated sectors %llu greater "
1209 "than the maximum %llu."), total, n_sectors);
2ca61a61 1210 else if (total < n_sectors)
fcea064b 1211 fdisk_warnx(cxt, _("Remaining %lld unallocated %ld-byte "
ac1a559a 1212 "sectors."), n_sectors - total, cxt->sector_size);
2ca61a61
DB
1213
1214 return 0;
1215}
1216
9dea2923
DB
1217/*
1218 * Ask the user for new partition type information (logical, extended).
0f639e54
DB
1219 * This function calls the actual partition adding logic - add_partition.
1220 *
1221 * API callback.
9dea2923 1222 */
8254c3a5 1223static int dos_add_partition(
43b382e6 1224 struct fdisk_context *cxt,
9ffeb235 1225 size_t partnum __attribute__ ((__unused__)),
ed470672 1226 struct fdisk_parttype *t)
9dea2923 1227{
e3661531
KZ
1228 size_t i, free_primary = 0;
1229 int rc = 0;
fcea064b 1230 struct fdisk_dos_label *l = self_label(cxt);
9dea2923 1231
9ffeb235
KZ
1232 assert(cxt);
1233 assert(cxt->label);
1234 assert(fdisk_is_disklabel(cxt, DOS));
1235
af0df606
KZ
1236 for (i = 0; i < 4; i++) {
1237 struct dos_partition *p = self_partition(cxt, i);
1238 free_primary += !p->sys_ind;
1239 }
9dea2923 1240
e3661531 1241 if (!free_primary && cxt->label->nparts_max >= MAXIMUM_PARTS) {
fcea064b
KZ
1242 fdisk_info(cxt, _("The maximum number of partitions has "
1243 "been created."));
8254c3a5 1244 return -EINVAL;
9dea2923 1245 }
1bf3a034 1246 rc = 1;
fcea064b 1247
9dea2923 1248 if (!free_primary) {
fcea064b
KZ
1249 if (l->ext_offset) {
1250 fdisk_info(cxt, _("All primary partitions are in use."));
8254c3a5 1251 rc = add_logical(cxt);
9dea2923 1252 } else
fcea064b 1253 fdisk_info(cxt, _("If you want to create more than "
0d0d12ad 1254 "four partitions, you must replace a "
fcea064b
KZ
1255 "primary partition with an extended "
1256 "partition first."));
1257
e3661531 1258 } else if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
c9f63764 1259 int j;
1bf3a034 1260
fcea064b
KZ
1261 fdisk_info(cxt, _("All logical partitions are in use. "
1262 "Adding a primary partition."));
c9f63764
SK
1263 j = get_partition_unused_primary(cxt);
1264 if (j >= 0)
1265 rc = add_partition(cxt, j, t);
9dea2923 1266 } else {
235adaa6 1267 char *buf;
bb58112e 1268 char c, prompt[BUFSIZ];
e3661531 1269 int dflt;
9dea2923 1270
fcea064b
KZ
1271 dflt = (free_primary == 1 && !l->ext_offset) ? 'e' : 'p';
1272
bd0d850b 1273 snprintf(prompt, sizeof(prompt),
9dea2923 1274 _("Partition type:\n"
e3661531 1275 " p primary (%zd primary, %d extended, %zd free)\n"
9dea2923 1276 "%s\n"
235adaa6 1277 "Select (default %c)"),
fcea064b
KZ
1278 4 - (l->ext_offset ? 1 : 0) - free_primary,
1279 l->ext_offset ? 1 : 0, free_primary,
1280 l->ext_offset ? _(" l logical (numbered from 5)") : _(" e extended"),
9dea2923
DB
1281 dflt);
1282
235adaa6 1283 rc = fdisk_ask_string(cxt, prompt, &buf);
bd0d850b
KZ
1284 if (rc)
1285 return rc;
1286 if (!buf[0]) {
9dea2923 1287 c = dflt;
ac1a559a 1288 fdisk_info(cxt, _("Using default response %c."), c);
bd0d850b
KZ
1289 } else
1290 c = tolower(buf[0]);
235adaa6 1291 free(buf);
bd0d850b 1292
9dea2923 1293 if (c == 'p') {
c9f63764
SK
1294 int j = get_partition_unused_primary(cxt);
1295 if (j >= 0)
1296 rc = add_partition(cxt, j, t);
8254c3a5 1297 goto done;
fcea064b 1298 } else if (c == 'l' && l->ext_offset) {
8254c3a5
DB
1299 rc = add_logical(cxt);
1300 goto done;
fcea064b 1301 } else if (c == 'e' && !l->ext_offset) {
c9f63764
SK
1302 int j = get_partition_unused_primary(cxt);
1303 if (j >= 0) {
bb58112e
KZ
1304 t = fdisk_get_parttype_from_code(cxt,
1305 MBR_DOS_EXTENDED_PARTITION);
c9f63764 1306 rc = add_partition(cxt, j, t);
ed470672 1307 }
8254c3a5 1308 goto done;
9dea2923 1309 } else
ac1a559a 1310 fdisk_warnx(cxt, _("Invalid partition type `%c'."), c);
9dea2923 1311 }
8254c3a5 1312done:
e3661531
KZ
1313 if (rc == 0)
1314 cxt->label->nparts_cur++;
8254c3a5 1315 return rc;
9dea2923 1316}
0dc13a38 1317
fae7b1bc
DB
1318static int write_sector(struct fdisk_context *cxt, sector_t secno,
1319 unsigned char *buf)
0dc13a38 1320{
21770662
KZ
1321 int rc;
1322
1323 rc = seek_sector(cxt, secno);
1324 if (rc != 0) {
98aeae96 1325 fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"),
21770662
KZ
1326 (uintmax_t) secno);
1327 return rc;
1328 }
fae7b1bc
DB
1329 if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
1330 return -errno;
1331 return 0;
1332}
1333
9ffeb235 1334static int dos_write_disklabel(struct fdisk_context *cxt)
fae7b1bc 1335{
8b3d9241 1336 struct fdisk_dos_label *l = self_label(cxt);
e3661531 1337 size_t i;
8b3d9241 1338 int rc = 0, mbr_changed = 0;
0dc13a38 1339
9ffeb235
KZ
1340 assert(cxt);
1341 assert(cxt->label);
1342 assert(fdisk_is_disklabel(cxt, DOS));
1343
8b3d9241
KZ
1344 mbr_changed = l->non_pt_changed;
1345
0dc13a38 1346 /* MBR (primary partitions) */
8b3d9241 1347 if (!mbr_changed) {
af0df606
KZ
1348 for (i = 0; i < 4; i++) {
1349 struct pte *pe = self_pte(cxt, i);
1350 if (pe->changed)
8b3d9241 1351 mbr_changed = 1;
af0df606 1352 }
0dc13a38 1353 }
8b3d9241 1354 if (mbr_changed) {
67987b47 1355 mbr_set_magic(cxt->firstsector);
fae7b1bc
DB
1356 rc = write_sector(cxt, 0, cxt->firstsector);
1357 if (rc)
1358 goto done;
0dc13a38 1359 }
8b3d9241 1360
0dc13a38 1361 /* EBR (logical partitions) */
e3661531 1362 for (i = 4; i < cxt->label->nparts_max; i++) {
af0df606 1363 struct pte *pe = self_pte(cxt, i);
0dc13a38
DB
1364
1365 if (pe->changed) {
87a97832 1366 mbr_set_magic(pe->sectorbuffer);
fae7b1bc
DB
1367 rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
1368 if (rc)
1369 goto done;
0dc13a38
DB
1370 }
1371 }
fae7b1bc
DB
1372
1373done:
1374 return rc;
0dc13a38 1375}
b8855c86 1376
f32a9be4
KZ
1377static int dos_locate_disklabel(struct fdisk_context *cxt, int n,
1378 const char **name, off_t *offset, size_t *size)
1379{
1380 assert(cxt);
1381
1382 *name = NULL;
1383 *offset = 0;
1384 *size = 0;
1385
1386 switch (n) {
1387 case 0:
1388 *name = "MBR";
1389 *offset = 0;
1390 *size = 512;
1391 break;
1392 default:
1393 /* extended partitions */
1394 if (n - 1 + 4 < cxt->label->nparts_max) {
1395 struct pte *pe = self_pte(cxt, n - 1 + 4);
1396
1397 assert(pe->private_sectorbuffer);
1398
1399 *name = "EBR";
1400 *offset = pe->offset * cxt->sector_size;
1401 *size = 512;
1402 } else
1403 return 1;
1404 break;
1405 }
1406
1407 return 0;
1408}
1409
8a95621d
KZ
1410static struct fdisk_parttype *dos_get_parttype(
1411 struct fdisk_context *cxt,
9ffeb235 1412 size_t partnum)
010186f2
KZ
1413{
1414 struct fdisk_parttype *t;
f540fe15 1415 struct dos_partition *p;
010186f2 1416
9ffeb235
KZ
1417 assert(cxt);
1418 assert(cxt->label);
e09435aa 1419 assert(fdisk_is_disklabel(cxt, DOS));
9ffeb235
KZ
1420
1421 if (partnum >= cxt->label->nparts_max)
010186f2
KZ
1422 return NULL;
1423
af0df606 1424 p = self_partition(cxt, partnum);
010186f2
KZ
1425 t = fdisk_get_parttype_from_code(cxt, p->sys_ind);
1426 if (!t)
1427 t = fdisk_new_unknown_parttype(p->sys_ind, NULL);
1428 return t;
1429}
1430
8a95621d 1431static int dos_set_parttype(
e3661531 1432 struct fdisk_context *cxt,
9ffeb235 1433 size_t partnum,
8a95621d 1434 struct fdisk_parttype *t)
02460b8a 1435{
f540fe15 1436 struct dos_partition *p;
02460b8a 1437
9ffeb235
KZ
1438 assert(cxt);
1439 assert(cxt->label);
e09435aa 1440 assert(fdisk_is_disklabel(cxt, DOS));
9ffeb235
KZ
1441
1442 if (partnum >= cxt->label->nparts_max || !t || t->type > UINT8_MAX)
02460b8a
KZ
1443 return -EINVAL;
1444
af0df606 1445 p = self_partition(cxt, partnum);
02460b8a
KZ
1446 if (t->type == p->sys_ind)
1447 return 0;
1448
1449 if (IS_EXTENDED(p->sys_ind) || IS_EXTENDED(t->type)) {
fcea064b
KZ
1450 fdisk_warnx(cxt, _("You cannot change a partition into an "
1451 "extended one or vice versa. Delete it first."));
02460b8a
KZ
1452 return -EINVAL;
1453 }
1454
1455 if (is_dos_partition(t->type) || is_dos_partition(p->sys_ind))
fcea064b 1456 fdisk_info(cxt, _("If you have created or modified any DOS 6.x "
0d0d12ad 1457 "partitions, please see the fdisk documentation for additional "
fcea064b 1458 "information."));
02460b8a
KZ
1459
1460 p->sys_ind = t->type;
af0df606
KZ
1461
1462 partition_set_changed(cxt, partnum, 1);
02460b8a
KZ
1463 return 0;
1464}
1465
dfc96cbf
KZ
1466/*
1467 * Check whether partition entries are ordered by their starting positions.
1468 * Return 0 if OK. Return i if partition i should have been earlier.
1469 * Two separate checks: primary and logical partitions.
1470 */
e3661531 1471static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
dfc96cbf 1472{
e3661531
KZ
1473 size_t last_p_start_pos = 0, p_start_pos;
1474 size_t i, last_i = 0;
dfc96cbf 1475
e3661531 1476 for (i = 0 ; i < cxt->label->nparts_max; i++) {
af0df606
KZ
1477
1478 struct pte *pe = self_pte(cxt, i);
1479 struct dos_partition *p = pe->pt_entry;
1480
dfc96cbf
KZ
1481 if (i == 4) {
1482 last_i = 4;
1483 last_p_start_pos = 0;
1484 }
af0df606 1485 if (p->sys_ind) {
61eaad50 1486 p_start_pos = get_abs_partition_start(pe);
dfc96cbf
KZ
1487
1488 if (last_p_start_pos > p_start_pos) {
1489 if (prev)
1490 *prev = last_i;
1491 return i;
1492 }
1493
1494 last_p_start_pos = p_start_pos;
1495 last_i = i;
1496 }
1497 }
1498 return 0;
1499}
6d864a49 1500
af0df606 1501static int is_garbage_table(struct fdisk_context *cxt)
6d864a49 1502{
e3661531 1503 size_t i;
6d864a49
KZ
1504
1505 for (i = 0; i < 4; i++) {
af0df606 1506 struct dos_partition *p = self_partition(cxt, i);
6d864a49
KZ
1507
1508 if (p->boot_ind != 0 && p->boot_ind != 0x80)
1509 return 1;
1510 }
1511 return 0;
1512}
1513
1cf67f1c
KZ
1514/*
1515 * List all PT fields.
1516 *
1517 * This is useful for PT debugging (or for 70's Hippies
1518 * who are on permanent LSD trip).
1519 */
1520static int dos_fulllist_disklabel(struct fdisk_context *cxt, int ext)
6d864a49 1521{
1cf67f1c
KZ
1522 int rc;
1523 size_t i;
1524 struct tt *tb = NULL;
1525
1526 assert(cxt);
1527 assert(cxt->label);
1528 assert(fdisk_is_disklabel(cxt, DOS));
1529
1530 tb = tt_new_table(TT_FL_FREEDATA);
1531 if (!tb)
1532 return -ENOMEM;
1533
1534 tt_define_column(tb, _("Nr"), 2, TT_FL_RIGHT);
1535 tt_define_column(tb, _("AF"), 2, TT_FL_RIGHT);
1536
1537 tt_define_column(tb, _("Hd"), 4, TT_FL_RIGHT);
1538 tt_define_column(tb, _("Sec"), 4, TT_FL_RIGHT);
1539 tt_define_column(tb, _("Cyl"), 5, TT_FL_RIGHT);
1540
1541 tt_define_column(tb, _("Hd"), 4, TT_FL_RIGHT);
1542 tt_define_column(tb, _("Sec"), 4, TT_FL_RIGHT);
1543 tt_define_column(tb, _("Cyl"), 5, TT_FL_RIGHT);
1544
1545 tt_define_column(tb, _("Start"), 9, TT_FL_RIGHT);
1546 tt_define_column(tb, _("Size"), 9, TT_FL_RIGHT);
1547 tt_define_column(tb, _("Id"), 2, TT_FL_RIGHT);
1548
1549 for (i = 0 ; i < cxt->label->nparts_max; i++) {
1550 struct pte *pe = self_pte(cxt, i);
1551 struct dos_partition *p;
1552 struct tt_line *ln;
1553 char *str;
1554
1555 p = ext ? pe->ex_entry : pe->pt_entry;
1556 if (!p)
1557 continue;
1558 ln = tt_add_line(tb, NULL);
1559 if (!ln)
1560 continue;
1561
1562 if (asprintf(&str, "%zd", i + 1) > 0)
1563 tt_line_set_data(ln, 0, str); /* Nr */
1564 if (asprintf(&str, "%02x", p->boot_ind) > 0)
1565 tt_line_set_data(ln, 1, str); /* AF */
1566
1567 if (asprintf(&str, "%d", p->bh) > 0)
1568 tt_line_set_data(ln, 2, str); /* Hd */
1569 if (asprintf(&str, "%d", sector(p->bs)) > 0)
1570 tt_line_set_data(ln, 3, str); /* Sec */
1571 if (asprintf(&str, "%d", cylinder(p->bs, p->bc)) > 0)
1572 tt_line_set_data(ln, 4, str); /* Cyl */
1573
1574 if (asprintf(&str, "%d", p->eh) > 0)
1575 tt_line_set_data(ln, 5, str); /* Hd */
1576 if (asprintf(&str, "%d", sector(p->es)) > 0)
1577 tt_line_set_data(ln, 6, str); /* Sec */
1578 if (asprintf(&str, "%d", cylinder(p->es, p->ec)) > 0)
1579 tt_line_set_data(ln, 7, str); /* Cyl */
1580
1581 if (asprintf(&str, "%lu",
1582 (unsigned long) dos_partition_get_start(p)) > 0)
1583 tt_line_set_data(ln, 8, str); /* Start */
1584 if (asprintf(&str, "%lu",
1585 (unsigned long) dos_partition_get_size(p)) > 0)
1586 tt_line_set_data(ln, 9, str); /* End */
1587
1588 if (asprintf(&str, "%02x", p->sys_ind) > 0)
1589 tt_line_set_data(ln, 10, str); /* Id */
1590
1591 if (p->sys_ind) {
1592 check_consistency(cxt, p, i);
1593 fdisk_warn_alignment(cxt,
1594 get_abs_partition_start(pe), i);
1595 }
1596 }
1597
1598 rc = fdisk_print_table(cxt, tb);
1599 tt_free_table(tb);
1600
1601 return rc;
1602}
1603
1604int fdisk_dos_list_extended(struct fdisk_context *cxt)
1605{
1606 return dos_fulllist_disklabel(cxt, 1);
1607}
1608
1609static int dos_list_disklabel(struct fdisk_context *cxt)
1610{
1611 int rc = 0, trunc = TT_FL_TRUNC;
1612 size_t i;
1613 struct tt *tb = NULL;
6d864a49
KZ
1614
1615 assert(cxt);
9ffeb235 1616 assert(cxt->label);
6d864a49
KZ
1617 assert(fdisk_is_disklabel(cxt, DOS));
1618
af0df606 1619 if (is_garbage_table(cxt)) {
fcea064b 1620 fdisk_warnx(cxt, _(
0d0d12ad 1621 "This doesn't look like a partition table. "
fcea064b 1622 "Probably you selected the wrong device."));
6d864a49
KZ
1623 }
1624
1cf67f1c
KZ
1625 if (fdisk_context_display_details(cxt))
1626 return dos_fulllist_disklabel(cxt, 0);
1627
1628 tb = tt_new_table(TT_FL_FREEDATA);
1629 if (!tb)
1630 return -ENOMEM;
1631
1632 /* don't trunc anything in expert mode */
1633 if (fdisk_context_display_details(cxt))
1634 trunc = 0;
6d864a49 1635
1cf67f1c
KZ
1636 tt_define_column(tb, _("Device"), 0.1, 0);
1637 tt_define_column(tb, _("Boot"), 1, 0);
1638 tt_define_column(tb, _("Start"), 9, TT_FL_RIGHT);
1639 tt_define_column(tb, _("End"), 9, TT_FL_RIGHT);
1640 /* TRANSLATORS: keep one blank space behind 'Blocks' */
1641 tt_define_column(tb, _("Blocks "), 5, TT_FL_RIGHT);
1642 tt_define_column(tb, _("Id"), 2, TT_FL_RIGHT);
1643 tt_define_column(tb, _("System"), 0.1, trunc);
6d864a49 1644
e3661531 1645 for (i = 0; i < cxt->label->nparts_max; i++) {
af0df606
KZ
1646 struct pte *pe = self_pte(cxt, i);
1647 struct dos_partition *p = pe->pt_entry;
5d324c6b 1648 unsigned int psects, pblocks, podd = 0;
1cf67f1c
KZ
1649 struct fdisk_parttype *type;
1650 struct tt_line *ln;
1651 char *str;
6d864a49 1652
1cf67f1c
KZ
1653 if (!p || is_cleared_partition(p))
1654 continue;
1655 ln = tt_add_line(tb, NULL);
1656 if (!ln)
1657 continue;
6d864a49 1658
1cf67f1c
KZ
1659 pblocks = psects = dos_partition_get_size(p);
1660 type = fdisk_get_parttype_from_code(cxt, p->sys_ind);
1661
1662 if (cxt->sector_size < 1024) {
1663 pblocks /= (1024 / cxt->sector_size);
1664 podd = psects % (1024 / cxt->sector_size);
6d864a49 1665 }
1cf67f1c
KZ
1666 if (cxt->sector_size > 1024)
1667 pblocks *= (cxt->sector_size / 1024);
1668
1669 str = fdisk_partname(cxt->dev_path, i + 1);
1670 if (str)
1671 tt_line_set_data(ln, 0, str); /* device */
1672
1673 str = strdup(p->boot_ind ?
1674 p->boot_ind == ACTIVE_FLAG ? "*" : "?" : " ");
1675 if (str)
1676 tt_line_set_data(ln, 1, str); /* boot flag */
1677
1678 if (asprintf(&str, "%lu", (unsigned long) cround(cxt,
1679 get_abs_partition_start(pe))) > 0)
1680 tt_line_set_data(ln, 2, str); /* start */
1681
1682 if (asprintf(&str, "%lu", (unsigned long) cround(cxt,
1683 get_abs_partition_start(pe)
1684 + psects - (psects ? 1 : 0))) > 0)
1685 tt_line_set_data(ln, 3, str); /* end */
1686
1687 if (asprintf(&str, "%lu%c", (unsigned long) pblocks,
1688 podd ? '+' : ' ') > 0)
1689 tt_line_set_data(ln, 4, str); /* blocks<flag> */
1690
1691 if (asprintf(&str, "%x", p->sys_ind) > 0)
1692 tt_line_set_data(ln, 5, str); /* id */
1693
1694 str = strdup(type ? type->name : _("Unknown"));
1695 if (str)
1696 tt_line_set_data(ln, 6, str);
1697
1698 check_consistency(cxt, p, i);
1699 fdisk_warn_alignment(cxt, get_abs_partition_start(pe), i);
1700 fdisk_free_parttype(type);
6d864a49
KZ
1701 }
1702
1cf67f1c
KZ
1703 rc = fdisk_print_table(cxt, tb);
1704 tt_free_table(tb);
1705
6d864a49
KZ
1706 /* Is partition table in disk order? It need not be, but... */
1707 /* partition table entries are not checked for correct order if this
d23c661c 1708 is a sgi, sun labeled disk... */
e3661531 1709 if (wrong_p_order(cxt, NULL))
fcea064b
KZ
1710 fdisk_info(cxt, _("Partition table entries are not in "
1711 "disk order."));
6d864a49 1712
1cf67f1c 1713 return rc;
6d864a49
KZ
1714}
1715
5dfca634 1716
dfc96cbf
KZ
1717/*
1718 * Fix the chain of logicals.
fcea064b 1719 * ext_offset is unchanged, the set of sectors used is unchanged
dfc96cbf
KZ
1720 * The chain is sorted so that sectors increase, and so that
1721 * starting sectors increase.
1722 *
1723 * After this it may still be that cfdisk doesn't like the table.
1724 * (This is because cfdisk considers expanded parts, from link to
1725 * end of partition, and these may still overlap.)
1726 * Now
1727 * sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
1728 * may help.
1729 */
e3661531 1730static void fix_chain_of_logicals(struct fdisk_context *cxt)
dfc96cbf 1731{
af0df606 1732 struct fdisk_dos_label *l = self_label(cxt);
e3661531 1733 size_t j, oj, ojj, sj, sjj;
f540fe15 1734 struct dos_partition *pj,*pjj,tmp;
dfc96cbf
KZ
1735
1736 /* Stage 1: sort sectors but leave sector of part 4 */
fcea064b 1737 /* (Its sector is the global ext_offset.) */
60c1b036 1738stage1:
e3661531 1739 for (j = 5; j < cxt->label->nparts_max - 1; j++) {
af0df606
KZ
1740 oj = l->ptes[j].offset;
1741 ojj = l->ptes[j + 1].offset;
dfc96cbf 1742 if (oj > ojj) {
af0df606
KZ
1743 l->ptes[j].offset = ojj;
1744 l->ptes[j + 1].offset = oj;
1745 pj = l->ptes[j].pt_entry;
b44244cb 1746 dos_partition_set_start(pj, dos_partition_get_start(pj)+oj-ojj);
af0df606 1747 pjj = l->ptes[j + 1].pt_entry;
b44244cb 1748 dos_partition_set_start(pjj, dos_partition_get_start(pjj)+ojj-oj);
af0df606 1749 dos_partition_set_start(l->ptes[j - 1].ex_entry,
fcea064b 1750 ojj - l->ext_offset);
af0df606 1751 dos_partition_set_start(l->ptes[j].ex_entry,
fcea064b 1752 oj - l->ext_offset);
dfc96cbf
KZ
1753 goto stage1;
1754 }
1755 }
1756
1757 /* Stage 2: sort starting sectors */
60c1b036 1758stage2:
e3661531 1759 for (j = 4; j < cxt->label->nparts_max - 1; j++) {
af0df606
KZ
1760 pj = l->ptes[j].pt_entry;
1761 pjj = l->ptes[j + 1].pt_entry;
b44244cb
KZ
1762 sj = dos_partition_get_start(pj);
1763 sjj = dos_partition_get_start(pjj);
af0df606
KZ
1764 oj = l->ptes[j].offset;
1765 ojj = l->ptes[j+1].offset;
dfc96cbf
KZ
1766 if (oj+sj > ojj+sjj) {
1767 tmp = *pj;
1768 *pj = *pjj;
1769 *pjj = tmp;
b44244cb
KZ
1770 dos_partition_set_start(pj, ojj+sjj-oj);
1771 dos_partition_set_start(pjj, oj+sj-ojj);
dfc96cbf
KZ
1772 goto stage2;
1773 }
1774 }
1775
1776 /* Probably something was changed */
e3661531 1777 for (j = 4; j < cxt->label->nparts_max; j++)
af0df606 1778 l->ptes[j].changed = 1;
dfc96cbf
KZ
1779}
1780
f8ad3899 1781int fdisk_dos_fix_order(struct fdisk_context *cxt)
dfc96cbf
KZ
1782{
1783 struct pte *pei, *pek;
e3661531 1784 size_t i,k;
dfc96cbf 1785
e3661531 1786 if (!wrong_p_order(cxt, NULL)) {
fcea064b 1787 fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
f8ad3899 1788 return 0;
dfc96cbf
KZ
1789 }
1790
e3661531 1791 while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
dfc96cbf
KZ
1792 /* partition i should have come earlier, move it */
1793 /* We have to move data in the MBR */
f540fe15 1794 struct dos_partition *pi, *pk, *pe, pbuf;
af0df606
KZ
1795 pei = self_pte(cxt, i);
1796 pek = self_pte(cxt, k);
dfc96cbf 1797
6864f168
KZ
1798 pe = pei->ex_entry;
1799 pei->ex_entry = pek->ex_entry;
1800 pek->ex_entry = pe;
dfc96cbf 1801
6864f168
KZ
1802 pi = pei->pt_entry;
1803 pk = pek->pt_entry;
dfc96cbf 1804
f540fe15
KZ
1805 memmove(&pbuf, pi, sizeof(struct dos_partition));
1806 memmove(pi, pk, sizeof(struct dos_partition));
1807 memmove(pk, &pbuf, sizeof(struct dos_partition));
dfc96cbf 1808
af0df606
KZ
1809 partition_set_changed(cxt, i, 1);
1810 partition_set_changed(cxt, k, 1);
dfc96cbf
KZ
1811 }
1812
1813 if (i)
e3661531 1814 fix_chain_of_logicals(cxt);
dfc96cbf 1815
f61d2632 1816 fdisk_info(cxt, _("Done."));
f8ad3899 1817 return 0;
dfc96cbf 1818}
6d864a49 1819
f8ad3899 1820int fdisk_dos_move_begin(struct fdisk_context *cxt, int i)
71ee484e 1821{
f8ad3899
KZ
1822 struct pte *pe;
1823 struct dos_partition *p;
71ee484e 1824 unsigned int new, free_start, curr_start, last;
bd0d850b 1825 uintmax_t res = 0;
e3661531 1826 size_t x;
f8ad3899 1827 int rc;
71ee484e
KZ
1828
1829 assert(cxt);
1830 assert(fdisk_is_disklabel(cxt, DOS));
1831
f8ad3899
KZ
1832 pe = self_pte(cxt, i);
1833 p = pe->pt_entry;
1834
b44244cb 1835 if (!p->sys_ind || !dos_partition_get_size(p) || IS_EXTENDED (p->sys_ind)) {
ac1a559a 1836 fdisk_warnx(cxt, _("Partition %d: no data area."), i + 1);
f8ad3899 1837 return 0;
71ee484e
KZ
1838 }
1839
1840 /* the default start is at the second sector of the disk or at the
1841 * second sector of the extended partition
1842 */
1843 free_start = pe->offset ? pe->offset + 1 : 1;
1844
61eaad50 1845 curr_start = get_abs_partition_start(pe);
71ee484e
KZ
1846
1847 /* look for a free space before the current start of the partition */
e3661531 1848 for (x = 0; x < cxt->label->nparts_max; x++) {
71ee484e 1849 unsigned int end;
af0df606 1850 struct pte *prev_pe = self_pte(cxt, x);
f540fe15 1851 struct dos_partition *prev_p = prev_pe->pt_entry;
71ee484e
KZ
1852
1853 if (!prev_p)
1854 continue;
af0df606
KZ
1855 end = get_abs_partition_start(prev_pe)
1856 + dos_partition_get_size(prev_p);
71ee484e
KZ
1857
1858 if (!is_cleared_partition(prev_p) &&
1859 end > free_start && end <= curr_start)
1860 free_start = end;
1861 }
1862
61eaad50 1863 last = get_abs_partition_start(pe) + dos_partition_get_size(p) - 1;
71ee484e 1864
f8ad3899
KZ
1865 rc = fdisk_ask_number(cxt, free_start, curr_start, last,
1866 _("New beginning of data"), &res);
1867 if (rc)
1868 return rc;
1869
bd0d850b 1870 new = res - pe->offset;
71ee484e 1871
b44244cb 1872 if (new != dos_partition_get_size(p)) {
af0df606
KZ
1873 unsigned int sects = dos_partition_get_size(p)
1874 + dos_partition_get_start(p) - new;
1875
b44244cb
KZ
1876 dos_partition_set_size(p, sects);
1877 dos_partition_set_start(p, new);
af0df606
KZ
1878
1879 partition_set_changed(cxt, i, 1);
71ee484e 1880 }
f8ad3899
KZ
1881
1882 return rc;
71ee484e 1883}
6d864a49 1884
47b8e7c0
KZ
1885static int dos_get_partition_status(
1886 struct fdisk_context *cxt,
9ffeb235 1887 size_t i,
47b8e7c0
KZ
1888 int *status)
1889{
f540fe15 1890 struct dos_partition *p;
47b8e7c0
KZ
1891
1892 assert(cxt);
9ffeb235 1893 assert(cxt->label);
47b8e7c0
KZ
1894 assert(fdisk_is_disklabel(cxt, DOS));
1895
9ffeb235 1896 if (!status || i >= cxt->label->nparts_max)
47b8e7c0
KZ
1897 return -EINVAL;
1898
af0df606 1899 p = self_partition(cxt, i);
47b8e7c0
KZ
1900
1901 if (p && !is_cleared_partition(p))
1902 *status = FDISK_PARTSTAT_USED;
af0df606
KZ
1903 else
1904 *status = FDISK_PARTSTAT_NONE;
47b8e7c0
KZ
1905
1906 return 0;
1907}
1908
fb1caca7
KZ
1909static int dos_toggle_partition_flag(
1910 struct fdisk_context *cxt,
1911 size_t i,
1912 unsigned long flag)
1913{
f540fe15 1914 struct dos_partition *p;
fb1caca7
KZ
1915
1916 assert(cxt);
1917 assert(cxt->label);
1918 assert(fdisk_is_disklabel(cxt, DOS));
1919
1920 if (i >= cxt->label->nparts_max)
1921 return -EINVAL;
1922
af0df606 1923 p = self_partition(cxt, i);
fb1caca7
KZ
1924
1925 switch (flag) {
1926 case DOS_FLAG_ACTIVE:
1927 if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
fcea064b
KZ
1928 fdisk_warnx(cxt, _("Partition %d: is an extended "
1929 "partition."), i + 1);
fb1caca7
KZ
1930
1931 p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
af0df606 1932 partition_set_changed(cxt, i, 1);
fb1caca7
KZ
1933 break;
1934 default:
1935 return 1;
1936 }
1937
1938 return 0;
1939}
1940
0c5d095e 1941static const struct fdisk_label_operations dos_operations =
b8855c86 1942{
0c5d095e
KZ
1943 .probe = dos_probe_label,
1944 .write = dos_write_disklabel,
1945 .verify = dos_verify_disklabel,
1946 .create = dos_create_disklabel,
f32a9be4 1947 .locate = dos_locate_disklabel,
1cf67f1c 1948 .list = dos_list_disklabel,
21fe3dde 1949 .get_id = dos_get_disklabel_id,
35b1f0a4 1950 .set_id = dos_set_disklabel_id,
1cf67f1c 1951
0c5d095e
KZ
1952 .part_add = dos_add_partition,
1953 .part_delete = dos_delete_partition,
1954 .part_get_type = dos_get_parttype,
1955 .part_set_type = dos_set_parttype,
47b8e7c0 1956
fb1caca7 1957 .part_toggle_flag = dos_toggle_partition_flag,
47b8e7c0
KZ
1958 .part_get_status = dos_get_partition_status,
1959
cf3808e4 1960 .reset_alignment = dos_reset_alignment,
ddec0e40
KZ
1961
1962 .deinit = dos_deinit,
b8855c86 1963};
0c5d095e
KZ
1964
1965/*
1966 * allocates DOS in-memory stuff
1967 */
1968struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt)
1969{
1970 struct fdisk_label *lb;
1971 struct fdisk_dos_label *dos;
1972
1973 assert(cxt);
1974
1975 dos = calloc(1, sizeof(*dos));
1976 if (!dos)
1977 return NULL;
1978
1979 /* initialize generic part of the driver */
1980 lb = (struct fdisk_label *) dos;
1981 lb->name = "dos";
53b422ab 1982 lb->id = FDISK_DISKLABEL_DOS;
0c5d095e
KZ
1983 lb->op = &dos_operations;
1984 lb->parttypes = dos_parttypes;
1985 lb->nparttypes = ARRAY_SIZE(dos_parttypes);
1986
caad8583 1987 lb->flags |= FDISK_LABEL_FL_ADDPART_NOPARTNO;
baa7cccc 1988
0c5d095e
KZ
1989 return lb;
1990}
852ce62b
KZ
1991
1992/*
1993 * Public label specific functions
1994 */
1995
1996int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
1997{
1998 struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
1999
2000 if (!lb)
2001 return -EINVAL;
2002
2003 dos->compatible = enable;
0b52b94c
KZ
2004 if (enable)
2005 lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
852ce62b
KZ
2006 return 0;
2007}
2008
2009int fdisk_dos_is_compatible(struct fdisk_label *lb)
2010{
2011 return ((struct fdisk_dos_label *) lb)->compatible;
2012}