Coverage Report

Created: 2025-10-13 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libfdisk/src/dos.c
Line
Count
Source
1
/*
2
 *
3
 * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
4
 *                    2012 Davidlohr Bueso <dave@gnu.org>
5
 *                    2021 Pali Rohár <pali.rohar@gmail.com>
6
 *
7
 * This is re-written version for libfdisk, the original was fdiskdoslabel.c
8
 * from util-linux fdisk.
9
 */
10
#include "c.h"
11
#include "randutils.h"
12
#include "pt-mbr.h"
13
#include "strutils.h"
14
#include "sysfs.h"
15
16
#include "fdiskP.h"
17
18
#include <ctype.h>
19
20
0
#define MAXIMUM_PARTS 60
21
0
#define ACTIVE_FLAG     0x80
22
23
/**
24
 * SECTION: dos
25
 * @title: DOS
26
 * @short_description: disk label specific functions
27
 *
28
 */
29
30
31
#define IS_EXTENDED(i) \
32
0
  ((i) == MBR_DOS_EXTENDED_PARTITION \
33
0
   || (i) == MBR_W95_EXTENDED_PARTITION \
34
0
   || (i) == MBR_LINUX_EXTENDED_PARTITION)
35
36
/*
37
 * per partition table entry data
38
 *
39
 * The four primary partitions have the same sectorbuffer
40
 * and have NULL ex_entry.
41
 *
42
 * Each logical partition table entry has two pointers, one for the
43
 * partition and one link to the next one.
44
 */
45
struct pte {
46
  struct dos_partition *pt_entry; /* on-disk MBR entry */
47
  struct dos_partition *ex_entry; /* on-disk EBR entry */
48
  fdisk_sector_t offset;          /* disk sector number */
49
  unsigned char *sectorbuffer;  /* disk sector contents */
50
51
  unsigned int changed : 1,
52
         private_sectorbuffer : 1;
53
};
54
55
/*
56
 * in-memory fdisk GPT stuff
57
 */
58
struct fdisk_dos_label {
59
  struct fdisk_label  head;   /* generic part */
60
61
  struct pte  ptes[MAXIMUM_PARTS];  /* partition */
62
  fdisk_sector_t  ext_offset;   /* start of the ext.partition */
63
  size_t    ext_index;    /* ext.partition index (if ext_offset is set) */
64
  unsigned int  compatible : 1,   /* is DOS compatible? */
65
      non_pt_changed : 1; /* MBR, but no PT changed */
66
};
67
68
/*
69
 * Partition types
70
 */
71
static const struct fdisk_parttype dos_parttypes[] = {
72
  #include "pt-mbr-partnames.h"
73
};
74
75
static const struct fdisk_shortcut dos_parttype_cuts[] =
76
{
77
  { .shortcut = "L", .alias = "linux",    .data = "83" },
78
  { .shortcut = "S", .alias = "swap",     .data = "82" },
79
  { .shortcut = "E", .alias = "extended", .data = "05", .deprecated = 1 }, /* collision with 0x0e type */
80
  { .shortcut = "Ex",.alias = "extended", .data = "05" }, /* MBR extended */
81
  { .shortcut = "U", .alias = "uefi",     .data = "EF" }, /* UEFI system */
82
  { .shortcut = "R", .alias = "raid",     .data = "FD" }, /* Linux RAID */
83
  { .shortcut = "V", .alias = "lvm",      .data = "8E" }, /* LVM */
84
  { .shortcut = "X", .alias = "linuxex",  .data = "85" }  /* Linux extended */
85
};
86
87
88
0
#define sector(s) ((s) & 0x3f)
89
0
#define cylinder(s, c)  ((c) | (((s) & 0xc0) << 2))
90
91
0
#define alignment_required(_x)  ((_x)->grain != (_x)->sector_size)
92
93
#define is_dos_compatible(_x) \
94
0
       (fdisk_is_label(_x, DOS) && \
95
0
                    fdisk_dos_is_compatible(fdisk_get_label(_x, NULL)))
96
97
#define cround(c, n)  fdisk_cround(c, n)
98
99
100
static inline struct fdisk_dos_label *self_label(struct fdisk_context *cxt)
101
0
{
102
0
  assert(cxt);
103
0
  assert(cxt->label);
104
0
  assert(fdisk_is_label(cxt, DOS));
105
106
0
  return (struct fdisk_dos_label *) cxt->label;
107
0
}
108
109
static inline struct pte *self_pte(struct fdisk_context *cxt, size_t i)
110
0
{
111
0
  struct fdisk_dos_label *l = self_label(cxt);
112
113
0
  if (i >= ARRAY_SIZE(l->ptes))
114
0
    return NULL;
115
116
0
  return &l->ptes[i];
117
0
}
118
119
static inline struct dos_partition *self_partition(
120
        struct fdisk_context *cxt,
121
        size_t i)
122
0
{
123
0
  struct pte *pe = self_pte(cxt, i);
124
0
  return pe ? pe->pt_entry : NULL;
125
0
}
126
127
struct dos_partition *fdisk_dos_get_partition(
128
        struct fdisk_context *cxt,
129
        size_t i)
130
0
{
131
0
  assert(cxt);
132
0
  assert(cxt->label);
133
0
  assert(fdisk_is_label(cxt, DOS));
134
135
0
  return self_partition(cxt, i);
136
0
}
137
138
static struct fdisk_parttype *dos_partition_parttype(
139
    struct fdisk_context *cxt,
140
    struct dos_partition *p)
141
0
{
142
0
  struct fdisk_parttype *t
143
0
    = fdisk_label_get_parttype_from_code(cxt->label, p->sys_ind);
144
0
  return t ? : fdisk_new_unknown_parttype(p->sys_ind, NULL);
145
0
}
146
147
/*
148
 * Linux kernel cares about partition size only. Things like
149
 * partition type or so are completely irrelevant -- kzak Nov-2013
150
 */
151
static int is_used_partition(struct dos_partition *p)
152
0
{
153
0
  return p && dos_partition_get_size(p) != 0;
154
0
}
155
156
static void partition_set_changed(
157
        struct fdisk_context *cxt,
158
        size_t i,
159
        int changed)
160
0
{
161
0
  struct pte *pe = self_pte(cxt, i);
162
163
0
  if (!pe)
164
0
    return;
165
166
0
  DBG(LABEL, ul_debug("DOS: setting %zu partition to %s", i,
167
0
        changed ? "changed" : "unchanged"));
168
169
0
  pe->changed = changed ? 1 : 0;
170
0
  if (changed)
171
0
    fdisk_label_set_changed(cxt->label, 1);
172
0
}
173
174
static fdisk_sector_t get_abs_partition_start(struct pte *pe)
175
0
{
176
0
  assert(pe);
177
0
  assert(pe->pt_entry);
178
179
0
  return pe->offset + dos_partition_get_start(pe->pt_entry);
180
0
}
181
182
static fdisk_sector_t get_abs_partition_end(struct pte *pe)
183
0
{
184
0
  fdisk_sector_t size;
185
186
0
  assert(pe);
187
0
  assert(pe->pt_entry);
188
189
0
  size = dos_partition_get_size(pe->pt_entry);
190
0
  return get_abs_partition_start(pe) + size - (size ? 1 : 0);
191
0
}
192
193
static int is_cleared_partition(struct dos_partition *p)
194
0
{
195
0
  return !(!p || p->boot_ind || p->bh || p->bs || p->bc ||
196
0
     p->sys_ind || p->eh || p->es || p->ec ||
197
0
     dos_partition_get_start(p) || dos_partition_get_size(p));
198
0
}
199
200
static int get_partition_unused_primary(struct fdisk_context *cxt,
201
          struct fdisk_partition *pa,
202
          size_t *partno)
203
0
{
204
0
  size_t org, n;
205
0
  int rc;
206
207
0
  assert(cxt);
208
0
  assert(cxt->label);
209
0
  assert(partno);
210
211
0
  org = cxt->label->nparts_max;
212
213
0
  cxt->label->nparts_max = 4;
214
0
  rc = fdisk_partition_next_partno(pa, cxt, &n);
215
0
  cxt->label->nparts_max = org;
216
217
0
  if (rc == 1) {
218
0
    fdisk_info(cxt, _("All primary partitions have been defined already."));
219
0
    rc = -1;
220
0
  } else if (rc == -ERANGE) {
221
0
    fdisk_warnx(cxt, _("Primary partition not available."));
222
0
  } else if (rc == 0)
223
0
    *partno = n;
224
225
0
  return rc;
226
0
}
227
228
static int seek_sector(struct fdisk_context *cxt, fdisk_sector_t secno)
229
0
{
230
0
  off_t offset = (off_t) secno * cxt->sector_size;
231
232
0
  return lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1 ? -errno : 0;
233
0
}
234
235
static int read_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
236
      unsigned char *buf)
237
0
{
238
0
  int rc = seek_sector(cxt, secno);
239
0
  ssize_t r;
240
241
0
  if (rc < 0)
242
0
    return rc;
243
244
0
  r = read(cxt->dev_fd, buf, cxt->sector_size);
245
0
  if (r == (ssize_t) cxt->sector_size)
246
0
    return 0;
247
0
  if (r < 0)
248
0
    return -errno;
249
0
  return -1;
250
0
}
251
252
/* Allocate a buffer and read a partition table sector */
253
static int read_pte(struct fdisk_context *cxt, size_t pno, fdisk_sector_t offset)
254
0
{
255
0
  int rc;
256
0
  unsigned char *buf;
257
0
  struct pte *pe = self_pte(cxt, pno);
258
259
0
  if (!pe)
260
0
    return -EINVAL;
261
262
0
  buf = calloc(1, cxt->sector_size);
263
0
  if (!buf)
264
0
    return -ENOMEM;
265
266
0
  DBG(LABEL, ul_debug("DOS: reading EBR %zu: offset=%ju, buffer=%p",
267
0
        pno, (uintmax_t) offset, buf));
268
269
0
  pe->offset = offset;
270
0
  pe->sectorbuffer = buf;
271
0
  pe->private_sectorbuffer = 1;
272
273
0
  rc = read_sector(cxt, offset, pe->sectorbuffer);
274
0
  if (rc) {
275
0
    fdisk_warn(cxt, _("Failed to read extended partition table "
276
0
        "(offset=%ju)"), (uintmax_t) offset);
277
0
    return rc;
278
0
  }
279
280
0
  pe->changed = 0;
281
0
  pe->pt_entry = pe->ex_entry = NULL;
282
0
  return 0;
283
0
}
284
285
286
static void clear_partition(struct dos_partition *p)
287
0
{
288
0
  if (!p)
289
0
    return;
290
0
  p->boot_ind = 0;
291
0
  p->bh = 0;
292
0
  p->bs = 0;
293
0
  p->bc = 0;
294
0
  p->sys_ind = 0;
295
0
  p->eh = 0;
296
0
  p->es = 0;
297
0
  p->ec = 0;
298
0
  dos_partition_set_start(p,0);
299
0
  dos_partition_set_size(p,0);
300
0
}
301
302
static void dos_init(struct fdisk_context *cxt)
303
0
{
304
0
  struct fdisk_dos_label *l = self_label(cxt);
305
0
  size_t i;
306
307
0
  assert(cxt);
308
0
  assert(cxt->label);
309
0
  assert(fdisk_is_label(cxt, DOS));
310
311
0
  DBG(LABEL, ul_debug("DOS: initialize, first sector buffer %p", cxt->firstsector));
312
313
0
  cxt->label->nparts_max = 4; /* default, unlimited number of logical */
314
315
0
  l->ext_index = 0;
316
0
  l->ext_offset = 0;
317
0
  l->non_pt_changed = 0;
318
319
0
  memset(l->ptes, 0, sizeof(l->ptes));
320
321
0
  for (i = 0; i < 4; i++) {
322
0
    struct pte *pe = self_pte(cxt, i);
323
324
0
    assert(pe);
325
0
    pe->pt_entry = mbr_get_partition(cxt->firstsector, i);
326
0
    pe->ex_entry = NULL;
327
0
    pe->offset = 0;
328
0
    pe->sectorbuffer = cxt->firstsector;
329
0
    pe->private_sectorbuffer = 0;
330
0
    pe->changed = 0;
331
332
0
    DBG(LABEL, ul_debug("DOS: initialize: #%zu start=%u size=%u sysid=%02x",
333
0
          i,
334
0
          dos_partition_get_start(pe->pt_entry),
335
0
          dos_partition_get_size(pe->pt_entry),
336
0
          pe->pt_entry->sys_ind));
337
0
  }
338
339
0
  if (fdisk_is_listonly(cxt))
340
0
    return;
341
  /*
342
   * Various warnings...
343
   */
344
0
  if (fdisk_missing_geometry(cxt))
345
0
    fdisk_warnx(cxt, _("You can set geometry from the extra functions menu."));
346
347
0
  if (is_dos_compatible(cxt)) {
348
0
    fdisk_warnx(cxt, _("DOS-compatible mode is deprecated."));
349
350
0
    if (cxt->sector_size != cxt->phy_sector_size)
351
0
      fdisk_info(cxt, _(
352
0
    "The device presents a logical sector size that is smaller than "
353
0
    "the physical sector size. Aligning to a physical sector (or optimal "
354
0
    "I/O) size boundary is recommended, or performance may be impacted."));
355
0
  }
356
357
0
  if (fdisk_use_cylinders(cxt))
358
0
    fdisk_warnx(cxt, _("Cylinders as display units are deprecated."));
359
360
0
  if (cxt->total_sectors > UINT_MAX) {
361
0
    uint64_t bytes = cxt->total_sectors * cxt->sector_size;
362
0
    char *szstr = size_to_human_string(SIZE_SUFFIX_SPACE
363
0
             | SIZE_SUFFIX_3LETTER, bytes);
364
0
    fdisk_warnx(cxt,
365
0
    _("The size of this disk is %s (%ju bytes). DOS "
366
0
      "partition table format cannot be used on drives for "
367
0
      "volumes larger than %lu bytes for %lu-byte "
368
0
      "sectors. Use GUID partition table format (GPT)."),
369
0
      szstr, bytes,
370
0
      UINT_MAX * cxt->sector_size,
371
0
      cxt->sector_size);
372
0
    free(szstr);
373
0
  }
374
0
}
375
376
/* callback called by libfdisk */
377
static void dos_deinit(struct fdisk_label *lb)
378
3.67k
{
379
3.67k
  size_t i;
380
3.67k
  struct fdisk_dos_label *l = (struct fdisk_dos_label *) lb;
381
382
224k
  for (i = 0; i < ARRAY_SIZE(l->ptes); i++) {
383
220k
    struct pte *pe = &l->ptes[i];
384
385
220k
    if (pe->private_sectorbuffer && pe->sectorbuffer) {
386
0
      DBG(LABEL, ul_debug("DOS: freeing pte %zu sector buffer %p",
387
0
            i, pe->sectorbuffer));
388
0
      free(pe->sectorbuffer);
389
0
    }
390
220k
    pe->sectorbuffer = NULL;
391
220k
    pe->private_sectorbuffer = 0;
392
220k
  }
393
394
3.67k
  memset(l->ptes, 0, sizeof(l->ptes));
395
3.67k
}
396
397
static void reset_pte(struct pte *pe)
398
0
{
399
0
  assert(pe);
400
401
0
  if (pe->private_sectorbuffer) {
402
0
    DBG(LABEL, ul_debug("   --> freeing pte sector buffer %p",
403
0
          pe->sectorbuffer));
404
0
    free(pe->sectorbuffer);
405
0
  }
406
0
  memset(pe, 0, sizeof(struct pte));
407
0
}
408
409
static int delete_partition(struct fdisk_context *cxt, size_t partnum)
410
0
{
411
0
  struct fdisk_dos_label *l;
412
0
  struct pte *pe;
413
0
  struct dos_partition *p;
414
0
  struct dos_partition *q;
415
416
0
  assert(cxt);
417
0
  assert(cxt->label);
418
0
  assert(fdisk_is_label(cxt, DOS));
419
420
0
  pe = self_pte(cxt, partnum);
421
0
  if (!pe)
422
0
    return -EINVAL;
423
424
0
  DBG(LABEL, ul_debug("DOS: delete partition %zu (max=%zu)", partnum,
425
0
        cxt->label->nparts_max));
426
427
0
  l = self_label(cxt);
428
0
  p = pe->pt_entry;
429
0
  q = pe->ex_entry;
430
431
  /* Note that for the fifth partition (partnum == 4) we don't actually
432
     decrement partitions. */
433
0
  if (partnum < 4) {
434
0
    DBG(LABEL, ul_debug("--> delete primary"));
435
0
    if (IS_EXTENDED(p->sys_ind) && partnum == l->ext_index) {
436
0
      size_t i;
437
0
      DBG(LABEL, ul_debug(" --> delete extended"));
438
0
      for (i = 4; i < cxt->label->nparts_max; i++) {
439
0
        DBG(LABEL, ul_debug("  --> delete logical #%zu", i));
440
0
        reset_pte(&l->ptes[i]);
441
442
0
      }
443
0
      cxt->label->nparts_max = 4;
444
0
      l->ptes[l->ext_index].ex_entry = NULL;
445
0
      l->ext_offset = 0;
446
0
      l->ext_index = 0;
447
0
    }
448
0
    partition_set_changed(cxt, partnum, 1);
449
0
    clear_partition(p);
450
0
  } else if (!q->sys_ind && partnum > 4) {
451
0
    DBG(LABEL, ul_debug("--> delete logical [last in the chain]"));
452
0
    reset_pte(&l->ptes[partnum]);
453
0
    --cxt->label->nparts_max;
454
0
    --partnum;
455
    /* clear link to deleted partition */
456
0
    clear_partition(l->ptes[partnum].ex_entry);
457
0
    partition_set_changed(cxt, partnum, 1);
458
0
  } else {
459
0
    DBG(LABEL, ul_debug("--> delete logical [move down]"));
460
0
    if (partnum > 4) {
461
0
      DBG(LABEL, ul_debug(" --> delete %zu logical link", partnum));
462
0
      p = l->ptes[partnum - 1].ex_entry;
463
0
      *p = *q;
464
0
      dos_partition_set_start(p, dos_partition_get_start(q));
465
0
      dos_partition_set_size(p, dos_partition_get_size(q));
466
0
      dos_partition_sync_chs(p, pe->offset, cxt->geom.sectors, cxt->geom.heads);
467
0
      partition_set_changed(cxt, partnum - 1, 1);
468
469
0
    } else if (cxt->label->nparts_max > 5) {
470
0
      DBG(LABEL, ul_debug(" --> delete first logical link"));
471
0
      pe = &l->ptes[5]; /* second logical */
472
473
0
      if (pe->pt_entry) /* prevent SEGFAULT */
474
0
        dos_partition_set_start(pe->pt_entry,
475
0
                 get_abs_partition_start(pe) -
476
0
                 l->ext_offset);
477
0
      pe->offset = l->ext_offset;
478
0
      dos_partition_sync_chs(p, pe->offset, cxt->geom.sectors, cxt->geom.heads);
479
0
      partition_set_changed(cxt, 5, 1);
480
0
    }
481
482
0
    if (cxt->label->nparts_max > 5) {
483
0
      DBG(LABEL, ul_debug(" --> move ptes"));
484
0
      cxt->label->nparts_max--;
485
0
      reset_pte(&l->ptes[partnum]);
486
0
      while (partnum < cxt->label->nparts_max) {
487
0
        DBG(LABEL, ul_debug("  --> moving pte %zu <-- %zu", partnum, partnum + 1));
488
0
        l->ptes[partnum] = l->ptes[partnum + 1];
489
0
        partnum++;
490
0
      }
491
0
      memset(&l->ptes[partnum], 0, sizeof(struct pte));
492
0
    } else {
493
0
      DBG(LABEL, ul_debug(" --> the only logical: clear only"));
494
0
      clear_partition(l->ptes[partnum].pt_entry);
495
0
      cxt->label->nparts_max--;
496
497
0
      if (partnum == 4) {
498
0
        DBG(LABEL, ul_debug("  --> clear last logical"));
499
0
        reset_pte(&l->ptes[partnum]);
500
0
        partition_set_changed(cxt, l->ext_index, 1);
501
0
      }
502
0
    }
503
0
  }
504
505
0
  fdisk_label_set_changed(cxt->label, 1);
506
0
  return 0;
507
0
}
508
509
static int dos_delete_partition(struct fdisk_context *cxt, size_t partnum)
510
0
{
511
0
  struct pte *pe;
512
513
0
  assert(cxt);
514
0
  assert(cxt->label);
515
0
  assert(fdisk_is_label(cxt, DOS));
516
517
0
  pe = self_pte(cxt, partnum);
518
0
  if (!pe || !is_used_partition(pe->pt_entry))
519
0
    return -EINVAL;
520
521
0
  return delete_partition(cxt, partnum);
522
0
}
523
524
static void read_extended(struct fdisk_context *cxt, size_t ext)
525
0
{
526
0
  size_t i;
527
0
  struct pte *pex, *pe;
528
0
  struct dos_partition *p, *q;
529
0
  struct fdisk_dos_label *l = self_label(cxt);
530
531
0
  if (fdisk_is_listonly(cxt) &&
532
0
      !sysfs_devno_is_wholedisk(fdisk_get_devno(cxt))) {
533
0
    DBG(LABEL, ul_debug("DOS: unable to gather logical partition chain "
534
0
          "when running on a non-whole disk device."));
535
0
    return;
536
0
  }
537
538
0
  l->ext_index = ext;
539
0
  pex = self_pte(cxt, ext);
540
0
  if (!pex) {
541
0
    DBG(LABEL, ul_debug("DOS: uninitialized pointer to %zu pex", ext));
542
0
    return;
543
0
  }
544
0
  pex->ex_entry = pex->pt_entry;
545
546
0
  p = pex->pt_entry;
547
0
  if (!dos_partition_get_start(p)) {
548
0
    fdisk_warnx(cxt, _("Bad offset in primary extended partition."));
549
0
    return;
550
0
  }
551
552
0
  DBG(LABEL, ul_debug("DOS: Reading extended %zu", ext));
553
554
0
  while (IS_EXTENDED (p->sys_ind)) {
555
0
    if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
556
      /* This is not a Linux restriction, but
557
         this program uses arrays of size MAXIMUM_PARTS.
558
         Do not try to `improve' this test. */
559
0
      struct pte *pre = self_pte(cxt,
560
0
            cxt->label->nparts_max - 1);
561
0
      fdisk_warnx(cxt,
562
0
      _("Omitting partitions after #%zu. They will be deleted "
563
0
        "if you save this partition table."),
564
0
        cxt->label->nparts_max);
565
566
0
      if (pre) {
567
0
        clear_partition(pre->ex_entry);
568
0
        partition_set_changed(cxt,
569
0
            cxt->label->nparts_max - 1, 1);
570
0
      }
571
0
      return;
572
0
    }
573
574
0
    pe = self_pte(cxt, cxt->label->nparts_max);
575
0
    if (!pe)
576
0
      return;
577
578
0
    if (read_pte(cxt, cxt->label->nparts_max, l->ext_offset +
579
0
            dos_partition_get_start(p)))
580
0
      return;
581
582
0
    if (!l->ext_offset)
583
0
      l->ext_offset = dos_partition_get_start(p);
584
585
0
    assert(pe->sectorbuffer);
586
0
    q = p = mbr_get_partition(pe->sectorbuffer, 0);
587
588
0
    for (i = 0; i < 4; i++, p++) {
589
0
      if (!dos_partition_get_size(p))
590
0
        continue;
591
592
0
      if (IS_EXTENDED (p->sys_ind)) {
593
0
        if (pe->ex_entry)
594
0
          fdisk_warnx(cxt, _(
595
0
          "Extra link pointer in partition "
596
0
          "table %zu."),
597
0
            cxt->label->nparts_max + 1);
598
0
        else
599
0
          pe->ex_entry = p;
600
0
      } else if (p->sys_ind) {
601
0
        if (pe->pt_entry)
602
0
          fdisk_warnx(cxt, _(
603
0
          "Ignoring extra data in partition "
604
0
          "table %zu."),
605
0
            cxt->label->nparts_max + 1);
606
0
        else
607
0
          pe->pt_entry = p;
608
0
      }
609
0
    }
610
611
    /* very strange code here... */
612
0
    if (!pe->pt_entry) {
613
0
      if (q != pe->ex_entry)
614
0
        pe->pt_entry = q;
615
0
      else
616
0
        pe->pt_entry = q + 1;
617
0
    }
618
0
    if (!pe->ex_entry) {
619
0
      if (q != pe->pt_entry)
620
0
        pe->ex_entry = q;
621
0
      else
622
0
        pe->ex_entry = q + 1;
623
0
    }
624
625
0
    p = pe->ex_entry;
626
0
    cxt->label->nparts_cur = ++cxt->label->nparts_max;
627
628
0
    DBG(LABEL, ul_debug("DOS: EBR[offset=%ju]: link: type=%x,  start=%u, size=%u; "
629
0
                                 " data: type=%x, start=%u, size=%u",
630
0
            (uintmax_t) pe->offset,
631
0
            pe->ex_entry->sys_ind,
632
0
            dos_partition_get_start(pe->ex_entry),
633
0
            dos_partition_get_size(pe->ex_entry),
634
0
            pe->pt_entry->sys_ind,
635
0
            dos_partition_get_start(pe->pt_entry),
636
0
            dos_partition_get_size(pe->pt_entry)));
637
638
0
  }
639
640
  /* remove last empty EBR */
641
0
  pe = self_pte(cxt, cxt->label->nparts_max - 1);
642
0
  if (pe &&
643
0
      is_cleared_partition(pe->ex_entry) &&
644
0
      is_cleared_partition(pe->pt_entry)) {
645
0
    DBG(LABEL, ul_debug("DOS: EBR[offset=%ju]: empty, remove", (uintmax_t) pe->offset));
646
0
    reset_pte(pe);
647
0
    cxt->label->nparts_max--;
648
0
    cxt->label->nparts_cur--;
649
0
  }
650
651
  /* remove empty links */
652
0
 remove:
653
0
  q = self_partition(cxt, 4);
654
0
  for (i = 4; i < cxt->label->nparts_max; i++) {
655
0
    p = self_partition(cxt, i);
656
657
0
    if (p && !dos_partition_get_size(p) &&
658
0
        (cxt->label->nparts_max > 5 || (q && q->sys_ind))) {
659
0
      fdisk_info(cxt, _("omitting empty partition (%zu)"), i+1);
660
0
      delete_partition(cxt, i);
661
0
      goto remove;  /* numbering changed */
662
0
    }
663
0
  }
664
665
0
  DBG(LABEL, ul_debug("DOS: nparts_max: %zu", cxt->label->nparts_max));
666
0
}
667
668
static int dos_create_disklabel(struct fdisk_context *cxt)
669
0
{
670
0
  unsigned int id = 0;
671
0
  int rc, has_id = 0;
672
0
  struct fdisk_dos_label *l;
673
674
0
  assert(cxt);
675
0
  assert(cxt->label);
676
0
  assert(fdisk_is_label(cxt, DOS));
677
678
0
  DBG(LABEL, ul_debug("DOS: creating new disklabel"));
679
680
0
  if (cxt->script) {
681
0
    char *end = NULL;
682
0
    const char *s = fdisk_script_get_header(cxt->script, "label-id");
683
684
0
    if (s) {
685
0
      errno = 0;
686
0
      id = strtoul(s, &end, 16);
687
0
      if (!errno && end && s < end) {
688
0
        has_id = 1;
689
0
        DBG(LABEL, ul_debug("DOS: re-use ID from script (0x%08x)", id));
690
0
      } else
691
0
        DBG(LABEL, ul_debug("DOS: failed to parse label=id '%s'", s));
692
0
    }
693
0
  }
694
695
  /* random disk signature */
696
0
  if (!has_id) {
697
0
    DBG(LABEL, ul_debug("DOS: generate new ID"));
698
0
    ul_random_get_bytes(&id, sizeof(id));
699
0
  }
700
701
0
  if (fdisk_has_protected_bootbits(cxt))
702
0
    rc = fdisk_init_firstsector_buffer(cxt, 0, MBR_PT_BOOTBITS_SIZE);
703
0
  else
704
0
    rc = fdisk_init_firstsector_buffer(cxt, 0, 0);
705
0
  if (rc)
706
0
    return rc;
707
0
  dos_init(cxt);
708
709
0
  l = self_label(cxt);
710
711
  /* Generate an MBR ID for this disk */
712
0
  mbr_set_id(cxt->firstsector, id);
713
0
  l->non_pt_changed = 1;
714
0
  fdisk_label_set_changed(cxt->label, 1);
715
716
  /* Put MBR signature */
717
0
  mbr_set_magic(cxt->firstsector);
718
719
0
  fdisk_info(cxt, _("Created a new DOS (MBR) disklabel with disk "
720
0
       "identifier 0x%08x."), id);
721
0
  return 0;
722
0
}
723
724
static int dos_set_disklabel_id(struct fdisk_context *cxt, const char *str)
725
0
{
726
0
  char *buf = NULL;
727
0
  unsigned int id, old;
728
0
  struct fdisk_dos_label *l;
729
0
  int rc = 0;
730
731
0
  assert(cxt);
732
0
  assert(cxt->label);
733
0
  assert(fdisk_is_label(cxt, DOS));
734
735
0
  DBG(LABEL, ul_debug("DOS: setting Id"));
736
737
0
  l = self_label(cxt);
738
0
  old = mbr_get_id(cxt->firstsector);
739
740
0
  if (!str) {
741
0
    rc = fdisk_ask_string(cxt,
742
0
      _("Enter the new disk identifier"), &buf);
743
0
    str = buf;
744
0
  }
745
0
  if (!rc) {
746
0
    char *end = NULL;
747
748
0
    errno = 0;
749
0
    id = strtoul(str, &end, 0);
750
0
    if (errno || str == end || (end && *end)) {
751
0
      fdisk_warnx(cxt, _("Incorrect value."));
752
0
      rc = -EINVAL;
753
0
    }
754
0
  }
755
756
0
  free(buf);
757
0
  if (rc)
758
0
    return -EINVAL;
759
760
0
  mbr_set_id(cxt->firstsector, id);
761
0
  l->non_pt_changed = 1;
762
0
  fdisk_label_set_changed(cxt->label, 1);
763
764
0
  fdisk_info(cxt, _("Disk identifier changed from 0x%08x to 0x%08x."),
765
0
      old, id);
766
0
  return 0;
767
0
}
768
769
static unsigned int chs_div_minus(unsigned int a1, unsigned int a2, unsigned int b1, unsigned int b2)
770
0
{
771
0
  if (a1 > a2 && b1 > b2) {
772
0
    a1 = a1 - a2;
773
0
    b1 = b1 - b2;
774
0
  } else if (a2 > a1 && b2 > b1) {
775
0
    a1 = a2 - a1;
776
0
    b1 = b2 - b1;
777
0
  } else {
778
0
    return 0;
779
0
  }
780
0
  if (a1 % b1)
781
0
    return 0;
782
0
  return a1 / b1;
783
0
}
784
785
static inline int chs_overflowed(unsigned int c, unsigned int h, unsigned int s)
786
0
{
787
  /* 1023/254/63 or 1023/255/63 indicates overflowed/invalid C/H/S values */
788
0
  return (c == 1023 && (h == 254 || h == 255) && s == 63);
789
0
}
790
791
static inline int lba_overflowed(fdisk_sector_t start, fdisk_sector_t sects)
792
0
{
793
  /* Check if the last LBA sector can be represented by unsigned 32bit int */
794
0
  return (start + (sects-1) > UINT32_MAX);
795
0
}
796
797
static void get_partition_table_geometry(struct fdisk_context *cxt,
798
      unsigned int *ph, unsigned int *ps)
799
0
{
800
0
  unsigned char *bufp = cxt->firstsector;
801
0
  struct { unsigned int c, h, o, v; } t[8] = { 0 };
802
0
  unsigned int n1, n2, n3, n4, n5, n6;
803
0
  const struct dos_partition *p;
804
0
  unsigned int c, h, s, l;
805
0
  unsigned int hh, ss;
806
0
  unsigned int sects;
807
0
  int i, j, dif;
808
809
0
#define chs_set_t(c, h, s, l, t, i) do { \
810
0
  t[i].c = c; \
811
0
  t[i].h = h; \
812
0
  t[i].o = l - (s-1); \
813
0
  t[i].v = (!chs_overflowed(c, h, s) && s && s-1 <= l); \
814
0
} while (0)
815
816
  /*
817
   * Conversion from C/H/S to LBA is defined by formula:
818
   *   LBA = (c * N_heads + h) * N_sectors + (s - 1)
819
   * Let o to be:
820
   *   o = LBA - (s - 1)
821
   * Then formula can be expressed as:
822
   *   o = (c * N_heads + h) * N_sectors
823
   * In general from two tuples (LBA1, c1, h1, s1), (LBA2, c2, h2, s2)
824
   * we can derive formulas for N_heads and N_sectors:
825
   *   N_heads = (o1 * h2 - o2 * h1) / (o2 * c1 - o1 * c2)
826
   *   N_sectors = (o2 * c1 - o1 * c2) / (c1 * h2 - c2 * h1)
827
   * MBR table contains for very partition start and end tuple.
828
   * So we have up to 8 tuples which leads to up to 28 equations
829
   * for calculating N_heads and N_sectors. Try to calculate
830
   * N_heads and N_sectors from the first possible partition and
831
   * if it fails then try also mixed tuples (beginning from first
832
   * partition and end from second). Calculation may fail if both
833
   * first and last sectors are on cylinder or head boundary
834
   * (dividend or divisor is zero). It is possible that different
835
   * partitions would have different C/H/S geometry. In this case
836
   * we want geometry from the first partition as in most cases
837
   * this partition is or was used by BIOS for booting.
838
   */
839
840
0
  hh = ss = 0;
841
0
  for (i = 0; i < 4; i++) {
842
0
    p = mbr_get_partition(bufp, i);
843
0
    if (!p->sys_ind)
844
0
      continue;
845
846
0
    c = cylinder(p->bs, p->bc);
847
0
    h = p->bh;
848
0
    s = sector(p->bs);
849
0
    l = dos_partition_get_start(p);
850
0
    chs_set_t(c, h, s, l, t, 2*i);
851
852
0
    sects = dos_partition_get_size(p);
853
0
    if (!sects || lba_overflowed(l, sects))
854
0
      continue;
855
856
0
    c = cylinder(p->es, p->ec);
857
0
    h = p->eh;
858
0
    s = sector(p->es);
859
0
    l += sects-1;
860
0
    chs_set_t(c, h, s, l, t, 2*i+1);
861
0
  }
862
863
0
  for (dif = 1; dif < 8; dif++) {
864
0
    for (i = 0; i + dif < 8; i++) {
865
0
      j = i + dif;
866
0
      if (!t[i].v || !t[j].v)
867
0
        continue;
868
0
      n1 = t[i].o * t[j].h;
869
0
      n2 = t[j].o * t[i].h;
870
0
      n3 = t[j].o * t[i].c;
871
0
      n4 = t[i].o * t[j].c;
872
0
      n5 = t[i].c * t[j].h;
873
0
      n6 = t[j].c * t[i].h;
874
0
      if (!hh && n1 != n2 && n3 != n4) {
875
0
        h = chs_div_minus(n1, n2, n3, n4);
876
0
        if (h > 0 && h <= 256)
877
0
          hh = h;
878
0
      }
879
0
      if (!ss && n3 != n4 && n5 != n6) {
880
0
        s = chs_div_minus(n3, n4, n5, n6);
881
0
        if (s > 0 && s <= 63)
882
0
          ss = s;
883
0
      }
884
0
      if (hh && ss)
885
0
        break;
886
0
    }
887
0
    if (hh && ss)
888
0
      break;
889
0
  }
890
891
0
  if (hh && ss) {
892
0
    *ph = hh;
893
0
    *ps = ss;
894
0
  }
895
896
0
  DBG(LABEL, ul_debug("DOS PT geometry: heads=%u, sectors=%u", *ph, *ps));
897
0
}
898
899
static int dos_reset_alignment(struct fdisk_context *cxt)
900
0
{
901
0
  assert(cxt);
902
0
  assert(cxt->label);
903
0
  assert(fdisk_is_label(cxt, DOS));
904
905
  /* overwrite necessary stuff by DOS deprecated stuff */
906
0
  if (is_dos_compatible(cxt)) {
907
0
    DBG(LABEL, ul_debug("DOS: resetting alignment for DOS-compatible PT"));
908
0
    if (cxt->geom.sectors)
909
0
      cxt->first_lba = cxt->geom.sectors; /* usually 63 */
910
911
0
    cxt->grain = cxt->sector_size;      /* usually 512 */
912
0
  }
913
914
0
  return 0;
915
0
}
916
917
/* TODO: move to include/pt-dos.h and share with libblkid */
918
0
#define AIX_MAGIC_STRING  "\xC9\xC2\xD4\xC1"
919
0
#define AIX_MAGIC_STRLEN  (sizeof(AIX_MAGIC_STRING) - 1)
920
921
static int dos_probe_label(struct fdisk_context *cxt)
922
0
{
923
0
  size_t i;
924
0
  unsigned int h = 0, s = 0;
925
926
0
  assert(cxt);
927
0
  assert(cxt->label);
928
0
  assert(fdisk_is_label(cxt, DOS));
929
930
  /* ignore disks with AIX magic number */
931
0
  if (memcmp(cxt->firstsector, AIX_MAGIC_STRING, AIX_MAGIC_STRLEN) == 0)
932
0
    return 0;
933
934
0
  if (!mbr_is_valid_magic(cxt->firstsector))
935
0
    return 0;
936
937
0
  dos_init(cxt);
938
939
0
  get_partition_table_geometry(cxt, &h, &s);
940
0
  if (h && s) {
941
0
    cxt->geom.heads = h;
942
0
          cxt->geom.sectors = s;
943
0
    cxt->geom.cylinders = cxt->total_sectors /
944
0
          (cxt->geom.heads * cxt->geom.sectors);
945
946
0
    if (fdisk_has_user_device_geometry(cxt))
947
0
      fdisk_apply_user_device_properties(cxt);
948
0
  }
949
950
0
  for (i = 0; i < 4; i++) {
951
0
    struct pte *pe = self_pte(cxt, i);
952
953
0
    assert(pe);
954
0
    if (is_used_partition(pe->pt_entry))
955
0
      cxt->label->nparts_cur++;
956
957
0
    if (IS_EXTENDED (pe->pt_entry->sys_ind)) {
958
0
      if (cxt->label->nparts_max != 4)
959
0
        fdisk_warnx(cxt, _(
960
0
        "Ignoring extra extended partition %zu"),
961
0
          i + 1);
962
0
      else
963
0
        read_extended(cxt, i);
964
0
    }
965
0
  }
966
967
0
  for (i = 3; i < cxt->label->nparts_max; i++) {
968
0
    struct pte *pe = self_pte(cxt, i);
969
0
    struct fdisk_dos_label *l = self_label(cxt);
970
971
0
    assert(pe);
972
0
    if (!mbr_is_valid_magic(pe->sectorbuffer)) {
973
0
      fdisk_info(cxt, _(
974
0
      "Invalid flag 0x%02x%02x of EBR (for partition %zu) will "
975
0
      "be corrected by w(rite)."),
976
0
        pe->sectorbuffer[510],
977
0
        pe->sectorbuffer[511],
978
0
        i + 1);
979
0
      partition_set_changed(cxt, i, 1);
980
981
      /* mark also extended as changed to update the first EBR
982
       * in situation that there is no logical partitions at all */
983
0
      partition_set_changed(cxt, l->ext_index, 1);
984
0
    }
985
0
  }
986
987
0
  return 1;
988
0
}
989
990
static void set_partition(struct fdisk_context *cxt,
991
        int i, int doext, fdisk_sector_t start,
992
        fdisk_sector_t stop, int sysid, int boot)
993
0
{
994
0
  struct pte *pe = self_pte(cxt, i);
995
0
  struct dos_partition *p;
996
0
  fdisk_sector_t offset;
997
998
0
  assert(!FDISK_IS_UNDEF(start));
999
0
  assert(!FDISK_IS_UNDEF(stop));
1000
0
  assert(pe);
1001
1002
0
  if (doext) {
1003
0
    struct fdisk_dos_label *l = self_label(cxt);
1004
0
    p = pe->ex_entry;
1005
0
    offset = l->ext_offset;
1006
0
  } else {
1007
0
    p = pe->pt_entry;
1008
0
    offset = pe->offset;
1009
0
  }
1010
1011
0
  DBG(LABEL, ul_debug("DOS: setting partition %d%s, offset=%zu, start=%zu, size=%zu, sysid=%02x",
1012
0
        i, doext ? " [extended]" : "",
1013
0
        (size_t) offset,
1014
0
        (size_t) (start -  offset),
1015
0
        (size_t) (stop - start + 1),
1016
0
        sysid));
1017
1018
0
  p->boot_ind = boot ? ACTIVE_FLAG : 0;
1019
0
  p->sys_ind = sysid;
1020
0
  dos_partition_set_start(p, start - offset);
1021
0
  dos_partition_set_size(p, stop - start + 1);
1022
0
  dos_partition_sync_chs(p, offset, cxt->geom.sectors, cxt->geom.heads);
1023
0
  partition_set_changed(cxt, i, 1);
1024
0
}
1025
1026
1027
static int get_start_from_user( struct fdisk_context *cxt,
1028
        fdisk_sector_t *start,
1029
        fdisk_sector_t low,
1030
        fdisk_sector_t dflt,
1031
        fdisk_sector_t limit,
1032
        struct fdisk_partition *pa)
1033
0
{
1034
0
  assert(start);
1035
1036
  /* try to use template from 'pa' */
1037
0
  if (pa && pa->start_follow_default)
1038
0
    *start = dflt;
1039
1040
0
  else if (pa && fdisk_partition_has_start(pa)) {
1041
0
    DBG(LABEL, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
1042
0
        (uintmax_t) pa->start, (uintmax_t) low, (uintmax_t) limit));
1043
0
    *start = pa->start;
1044
0
    if (*start < low || *start > limit) {
1045
0
      fdisk_warnx(cxt, _("Start sector %ju out of range."),
1046
0
          (uintmax_t) *start);
1047
0
      return -ERANGE;
1048
0
    }
1049
0
  } else {
1050
    /* ask user by dialog */
1051
0
    struct fdisk_ask *ask = fdisk_new_ask();
1052
0
    int rc;
1053
1054
0
    if (!ask)
1055
0
      return -ENOMEM;
1056
0
    fdisk_ask_set_query(ask,
1057
0
      fdisk_use_cylinders(cxt) ?
1058
0
        _("First cylinder") : _("First sector"));
1059
0
    fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
1060
0
    fdisk_ask_number_set_low(ask, fdisk_cround(cxt, low));
1061
0
    fdisk_ask_number_set_default(ask, fdisk_cround(cxt, dflt));
1062
0
    fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
1063
1064
0
    rc = fdisk_do_ask(cxt, ask);
1065
0
    *start = fdisk_ask_number_get_result(ask);
1066
0
    fdisk_unref_ask(ask);
1067
0
    if (rc)
1068
0
      return rc;
1069
0
    if (fdisk_use_cylinders(cxt)) {
1070
0
            *start = (*start - 1)
1071
0
        * fdisk_get_units_per_sector(cxt);
1072
0
      if (*start < low)
1073
0
        *start = low;
1074
0
    }
1075
0
  }
1076
1077
0
  DBG(LABEL, ul_debug("DOS: start is %ju", (uintmax_t) *start));
1078
0
  return 0;
1079
0
}
1080
1081
/* Returns last available sector in the free space pointed to by start. */
1082
static int find_last_free(
1083
      struct fdisk_context *cxt,
1084
      int logical,
1085
      fdisk_sector_t begin,
1086
      fdisk_sector_t stop,
1087
      fdisk_sector_t *result)
1088
0
{
1089
0
  fdisk_sector_t last = stop;
1090
1091
0
  size_t i = logical ? 4 : 0;
1092
1093
0
  for ( ; i < cxt->label->nparts_max; i++) {
1094
0
    struct pte *pe = self_pte(cxt, i);
1095
1096
0
    assert(pe);
1097
0
    fdisk_sector_t p_start = get_abs_partition_start(pe);
1098
0
    fdisk_sector_t p_end = get_abs_partition_end(pe);
1099
1100
0
    if (is_cleared_partition(pe->pt_entry))
1101
0
      continue;
1102
    /* count EBR and begin of the logical partition as used area */
1103
0
    if (pe->offset)
1104
0
      p_start -= cxt->first_lba;
1105
1106
0
    if ((p_start >= begin && p_start <= last) ||
1107
0
        (p_end >= begin && p_end <= last)) {
1108
0
      last = p_start - 1;
1109
0
    }
1110
0
    if (last < begin) {
1111
0
      DBG(LABEL, ul_debug("no free space <%ju,%ju>",
1112
0
          (uintmax_t) begin, (uintmax_t) stop));
1113
0
      return -ENOSPC;
1114
0
    }
1115
0
  }
1116
1117
0
  if (last == begin)
1118
0
    last = stop;
1119
1120
0
  DBG(LABEL, ul_debug("DOS: last free sector  <%ju,%ju>: %ju",
1121
0
      (uintmax_t) begin, (uintmax_t) stop, (uintmax_t) last));
1122
1123
0
  *result = last;
1124
0
  return 0;
1125
0
}
1126
1127
static int find_last_free_sector_in_range(
1128
      struct fdisk_context *cxt,
1129
      int logical,
1130
      fdisk_sector_t begin,
1131
      fdisk_sector_t end,
1132
      fdisk_sector_t *result)
1133
0
{
1134
0
  int last_moved;
1135
0
  fdisk_sector_t last = end;
1136
1137
0
  do {
1138
0
    size_t i = logical ? 4 : 0;
1139
1140
0
    last_moved = 0;
1141
0
    for ( ; i < cxt->label->nparts_max; i++) {
1142
0
      struct pte *pe = self_pte(cxt, i);
1143
1144
0
      assert(pe);
1145
0
      fdisk_sector_t p_start = get_abs_partition_start(pe);
1146
0
      fdisk_sector_t p_end = get_abs_partition_end(pe);
1147
1148
0
      if (is_cleared_partition(pe->pt_entry))
1149
0
        continue;
1150
1151
      /* count EBR and begin of the logical partition as used area */
1152
0
      if (pe->offset)
1153
0
        p_start -= cxt->first_lba;
1154
1155
0
      if (last >= p_start && last <= p_end) {
1156
0
        last = p_start - 1;
1157
0
        last_moved = 1;
1158
1159
0
        if (last < begin) {
1160
0
          DBG(LABEL, ul_debug("DOS: last free out of range <%ju,%ju>: %ju",
1161
0
            (uintmax_t) begin, (uintmax_t) end, (uintmax_t) last));
1162
1163
0
          return -ENOSPC;
1164
0
        }
1165
0
      }
1166
0
    }
1167
0
  } while (last_moved == 1);
1168
1169
0
  DBG(LABEL, ul_debug("DOS: last unused sector in range <%ju,%ju>: %ju",
1170
0
      (uintmax_t) begin, (uintmax_t) end, (uintmax_t) last));
1171
1172
0
  *result = last;
1173
0
  return 0;
1174
0
}
1175
1176
static int find_first_free_sector_in_range(
1177
      struct fdisk_context *cxt,
1178
      int logical,
1179
      fdisk_sector_t begin,
1180
      fdisk_sector_t end,
1181
      fdisk_sector_t *result)
1182
0
{
1183
0
  int first_moved = 0;
1184
0
  fdisk_sector_t first = begin;
1185
1186
0
  do {
1187
0
    size_t i = logical ? 4 : 0;
1188
1189
0
    first_moved = 0;
1190
0
    for (; i < cxt->label->nparts_max; i++) {
1191
0
      struct pte *pe = self_pte(cxt, i);
1192
1193
0
      assert(pe);
1194
0
      fdisk_sector_t p_start = get_abs_partition_start(pe);
1195
0
      fdisk_sector_t p_end = get_abs_partition_end(pe);
1196
1197
0
      if (is_cleared_partition(pe->pt_entry))
1198
0
        continue;
1199
      /* count EBR and begin of the logical partition as used area */
1200
0
      if (pe->offset)
1201
0
        p_start -= cxt->first_lba;
1202
0
      if (first < p_start)
1203
0
        continue;
1204
0
      if (first <= p_end) {
1205
0
        first = p_end + 1 + (logical ? cxt->first_lba : 0);
1206
0
        first_moved = 1;
1207
1208
0
        if (first > end) {
1209
0
          DBG(LABEL, ul_debug("DOS: first free out of range <%ju,%ju>: %ju",
1210
0
            (uintmax_t) begin, (uintmax_t) end, (uintmax_t) first));
1211
0
          return -ENOSPC;
1212
0
        }
1213
0
      }
1214
0
    }
1215
0
  } while (first_moved == 1);
1216
1217
0
  DBG(LABEL, ul_debug("DOS: first unused sector in range <%ju,%ju>: %ju",
1218
0
      (uintmax_t) begin, (uintmax_t) end, (uintmax_t) first));
1219
0
  *result = first;
1220
0
  return 0;
1221
0
}
1222
1223
static int get_disk_ranges(struct fdisk_context *cxt, int logical,
1224
         fdisk_sector_t *first, fdisk_sector_t *last)
1225
0
{
1226
0
  if (logical) {
1227
    /* logical partitions */
1228
0
    struct fdisk_dos_label *l = self_label(cxt);
1229
0
    struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
1230
1231
0
    if (!ext_pe)
1232
0
      return -EINVAL;
1233
1234
0
    *first = l->ext_offset + cxt->first_lba;
1235
0
    *last = get_abs_partition_end(ext_pe);
1236
1237
0
  } else {
1238
    /* primary partitions */
1239
0
    if (fdisk_use_cylinders(cxt) || !cxt->total_sectors)
1240
0
      *last = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
1241
0
    else
1242
0
      *last = cxt->total_sectors - 1;
1243
1244
0
    if (*last > UINT_MAX)
1245
0
      *last = UINT_MAX;
1246
0
    *first = cxt->first_lba;
1247
0
  }
1248
1249
0
  return 0;
1250
0
}
1251
1252
/* first free sector on disk */
1253
static int find_first_free_sector(struct fdisk_context *cxt,
1254
        int logical,
1255
        fdisk_sector_t start,
1256
        fdisk_sector_t *result)
1257
0
{
1258
0
  fdisk_sector_t first, last;
1259
0
  int rc;
1260
1261
0
  rc = get_disk_ranges(cxt, logical, &first, &last);
1262
0
  if (rc)
1263
0
    return rc;
1264
1265
0
  return find_first_free_sector_in_range(cxt, logical, start, last, result);
1266
0
}
1267
1268
static int add_partition(struct fdisk_context *cxt, size_t n,
1269
       struct fdisk_partition *pa)
1270
0
{
1271
0
  int sys, read = 0, rc, isrel = 0, is_logical;
1272
0
  struct fdisk_dos_label *l = self_label(cxt);
1273
0
  struct dos_partition *p = self_partition(cxt, n);
1274
0
  struct fdisk_ask *ask = NULL;
1275
1276
0
  fdisk_sector_t start, stop, limit, temp;
1277
1278
0
  DBG(LABEL, ul_debug("DOS: adding partition %zu", n));
1279
1280
0
  sys = pa && pa->type ? pa->type->code : MBR_LINUX_DATA_PARTITION;
1281
0
  is_logical = n >= 4;
1282
1283
0
  if (p && is_used_partition(p)) {
1284
0
    fdisk_warnx(cxt, _("Partition %zu is already defined.  "
1285
0
                 "Delete it before re-adding it."),
1286
0
        n + 1);
1287
0
    return -EINVAL;
1288
0
  }
1289
1290
0
  rc = get_disk_ranges(cxt, is_logical, &start, &stop);
1291
0
  if (rc)
1292
0
    return rc;
1293
1294
0
  if (!is_logical && cxt->parent && fdisk_is_label(cxt->parent, GPT))
1295
0
    start = 1;   /* Bad boy modifies hybrid MBR */
1296
1297
0
  rc = find_last_free_sector_in_range(cxt, is_logical, start, stop, &limit);
1298
0
  if (rc == -ENOSPC)
1299
0
    fdisk_warnx(cxt, _("No free sectors available."));
1300
0
  if (rc)
1301
0
    return rc;
1302
1303
0
  if ((is_logical || !cxt->parent || !fdisk_is_label(cxt->parent, GPT))
1304
0
      && cxt->script && pa && fdisk_partition_has_start(pa)
1305
0
      && pa->start >= (is_logical ? l->ext_offset : 1)
1306
0
      && pa->start < start) {
1307
0
    fdisk_set_first_lba(cxt, 1);
1308
1309
0
    rc = get_disk_ranges(cxt, is_logical, &start, &stop);
1310
0
    if (rc) /* won't happen, but checking to be proper */
1311
0
      return rc;
1312
0
  }
1313
1314
  /*
1315
   * Ask for first sector
1316
   */
1317
0
  do {
1318
0
    fdisk_sector_t dflt, aligned;
1319
1320
0
    temp = start;
1321
1322
0
    DBG(LABEL, ul_debug("DOS: >>> search for first free from %ju", start));
1323
0
    rc = find_first_free_sector(cxt, is_logical, start, &dflt);
1324
0
    if (rc == -ENOSPC)
1325
0
      fdisk_warnx(cxt, _("No free sectors available."));
1326
0
    if (rc)
1327
0
      return rc;
1328
0
    start = dflt;
1329
1330
0
    if (n >= 4 && pa && fdisk_partition_has_start(pa) && cxt->script
1331
0
        && cxt->first_lba > 1
1332
0
        && temp == start - cxt->first_lba) {
1333
0
      fdisk_set_first_lba(cxt, 1);
1334
0
      start = pa->start;
1335
0
    }
1336
1337
    /* the default sector should be aligned and unused */
1338
0
    do {
1339
0
      aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
1340
0
      find_first_free_sector(cxt, is_logical, aligned, &dflt);
1341
0
    } while (dflt != aligned && dflt > aligned && dflt < limit);
1342
1343
0
    if (dflt >= limit)
1344
0
      dflt = start;
1345
0
    if (start > limit)
1346
0
      break;
1347
0
    if (start >= temp + fdisk_get_units_per_sector(cxt)
1348
0
        && read) {
1349
0
      if (!pa || !pa->start_follow_default)
1350
0
        fdisk_info(cxt, _("Sector %ju is already allocated."),
1351
0
            (uintmax_t) temp);
1352
0
      temp = start;
1353
0
      read = 0;
1354
0
      if (pa && fdisk_partition_has_start(pa))
1355
0
        break;
1356
0
    }
1357
1358
0
    if (!read && start == temp) {
1359
0
      rc = get_start_from_user(cxt, &start, temp, dflt, limit, pa);
1360
0
      if (rc)
1361
0
        return rc;
1362
0
      read = 1;
1363
0
    }
1364
0
    if (pa && fdisk_partition_has_size(pa)) {
1365
0
      fdisk_sector_t last;
1366
1367
0
      rc = find_last_free(cxt, is_logical, start, limit, &last);
1368
0
      if (rc == 0 && last - start + 1 < fdisk_partition_get_size(pa)) {
1369
0
        DBG(LABEL, ul_debug("DOS: area <%ju,%ju> too small [wanted=%ju aval=%ju]",
1370
0
              (uintmax_t) start, (uintmax_t) last,
1371
0
              fdisk_partition_get_size(pa),
1372
0
              last - start));
1373
1374
0
        if (fdisk_partition_has_start(pa)
1375
0
            && fdisk_partition_get_start(pa) <= last)
1376
0
          rc = -ENOSPC;
1377
0
        else {
1378
0
          start = last + 1;
1379
0
          continue;
1380
0
        }
1381
0
      }
1382
0
      if (rc == -ENOSPC) {
1383
0
        fdisk_warnx(cxt, _("No free sectors available."));
1384
0
        return rc;
1385
0
      }
1386
0
    }
1387
1388
0
  } while (start != temp || !read);
1389
1390
0
  if (n == 4) {
1391
    /* The first EBR is stored at begin of the extended partition */
1392
0
    struct pte *pe = self_pte(cxt, n);
1393
1394
0
    assert(pe);
1395
0
    pe->offset = l->ext_offset;
1396
0
  } else if (n > 4) {
1397
    /* The second (and another) EBR */
1398
0
    struct pte *pe = self_pte(cxt, n);
1399
1400
0
    assert(pe);
1401
0
    assert(start >= cxt->first_lba);
1402
1403
0
    pe->offset = start - cxt->first_lba;
1404
0
    DBG(LABEL, ul_debug("DOS: setting EBR offset to %ju [start=%ju]", pe->offset, start));
1405
1406
0
    if (pe->offset == l->ext_offset) { /* must be corrected */
1407
0
      pe->offset++;
1408
0
      if (cxt->first_lba == 1)
1409
0
        start++;
1410
0
    }
1411
0
  }
1412
1413
0
  rc = find_last_free(cxt, is_logical, start, limit, &stop);
1414
0
  if (rc == -ENOSPC)
1415
0
    fdisk_warnx(cxt, _("No free sectors available."));
1416
0
  if (rc)
1417
0
    return rc;
1418
0
  limit = stop;
1419
1420
  /*
1421
   * Ask for last sector
1422
   */
1423
0
  if (fdisk_cround(cxt, start) == fdisk_cround(cxt, limit))
1424
0
    stop = limit;
1425
0
  else if (pa && pa->end_follow_default)
1426
0
    stop = limit;
1427
0
  else if (pa && fdisk_partition_has_size(pa)) {
1428
0
    stop = start + pa->size;
1429
0
    isrel = pa->size_explicit ? 0 : 1;
1430
0
    if ((!isrel || !alignment_required(cxt)) && stop > start)
1431
0
      stop -= 1;
1432
0
  } else {
1433
    /* ask user by dialog */
1434
0
    for (;;) {
1435
0
      if (!ask)
1436
0
        ask = fdisk_new_ask();
1437
0
      else
1438
0
        fdisk_reset_ask(ask);
1439
0
      if (!ask)
1440
0
        return -ENOMEM;
1441
0
      fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
1442
1443
0
      if (fdisk_use_cylinders(cxt)) {
1444
0
        fdisk_ask_set_query(ask, _("Last cylinder, +/-cylinders or +/-size{K,M,G,T,P}"));
1445
0
        fdisk_ask_number_set_unit(ask,
1446
0
               cxt->sector_size *
1447
0
               fdisk_get_units_per_sector(cxt));
1448
0
      } else {
1449
0
        fdisk_ask_set_query(ask, _("Last sector, +/-sectors or +/-size{K,M,G,T,P}"));
1450
0
        fdisk_ask_number_set_unit(ask,cxt->sector_size);
1451
0
      }
1452
1453
0
      fdisk_ask_number_set_low(ask, fdisk_cround(cxt, start));
1454
0
      fdisk_ask_number_set_default(ask, fdisk_cround(cxt, limit));
1455
0
      fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
1456
0
      fdisk_ask_number_set_base(ask, fdisk_cround(cxt, start)); /* base for relative input */
1457
0
      fdisk_ask_number_set_wrap_negative(ask, 1); /* wrap negative around high */
1458
1459
0
      rc = fdisk_do_ask(cxt, ask);
1460
0
      if (rc)
1461
0
        goto done;
1462
1463
0
      stop = fdisk_ask_number_get_result(ask);
1464
0
      isrel = fdisk_ask_number_is_relative(ask);
1465
0
      if (fdisk_use_cylinders(cxt)) {
1466
0
        stop = stop * fdisk_get_units_per_sector(cxt) - 1;
1467
0
        if (stop >limit)
1468
0
          stop = limit;
1469
0
      }
1470
1471
0
      if (stop >= start && stop <= limit)
1472
0
        break;
1473
0
      fdisk_warnx(cxt, _("Value out of range."));
1474
0
    }
1475
0
  }
1476
1477
0
  DBG(LABEL, ul_debug("DOS: raw stop: %ju [limit %ju]", (uintmax_t) stop, (uintmax_t) limit));
1478
1479
0
  if (stop > limit)
1480
0
    stop = limit;
1481
1482
0
  if (isrel && stop - start < (cxt->grain / fdisk_get_sector_size(cxt))) {
1483
    /* Don't try to be smart on very small partitions and don't align so small sizes */
1484
0
    isrel = 0;
1485
0
    DBG(LABEL, ul_debug("DOS: don't align end of tiny partition [start=%ju, stop=%ju, grain=%lu]",
1486
0
          (uintmax_t)start,  (uintmax_t)stop, cxt->grain));
1487
0
  }
1488
1489
0
  if (stop < limit && isrel && alignment_required(cxt)) {
1490
    /* the last sector has not been exactly requested (but
1491
     * defined by +size{K,M,G} convention), so be smart and
1492
     * align the end of the partition. The next partition
1493
     * will start at phy.block boundary.
1494
     */
1495
0
    stop = fdisk_align_lba_in_range(cxt, stop, start, limit);
1496
0
    if (stop > start)
1497
0
      stop -= 1; /* end one sector before aligned offset */
1498
0
    if (stop > limit)
1499
0
      stop = limit;
1500
0
    DBG(LABEL, ul_debug("DOS: aligned stop: %ju", (uintmax_t) stop));
1501
0
  }
1502
1503
0
  set_partition(cxt, n, 0, start, stop, sys, fdisk_partition_is_bootable(pa));
1504
0
  if (n > 4) {
1505
0
    struct pte *pe = self_pte(cxt, n);
1506
0
    assert(pe);
1507
0
    set_partition(cxt, n - 1, 1, pe->offset, stop,
1508
0
          MBR_DOS_EXTENDED_PARTITION, 0);
1509
0
  }
1510
1511
  /* report */
1512
0
  {
1513
0
    struct fdisk_parttype *t =
1514
0
      fdisk_label_get_parttype_from_code(cxt->label, sys);
1515
0
    fdisk_info_new_partition(cxt, n + 1, start, stop, t);
1516
0
    fdisk_unref_parttype(t);
1517
0
  }
1518
1519
1520
0
  if (IS_EXTENDED(sys)) {
1521
0
    struct pte *pen = self_pte(cxt, n);
1522
1523
0
    assert(pen);
1524
0
    l->ext_index = n;
1525
0
    l->ext_offset = start;
1526
0
    pen->ex_entry = p;
1527
0
  }
1528
1529
0
  fdisk_label_set_changed(cxt->label, 1);
1530
0
  rc = 0;
1531
0
done:
1532
0
  fdisk_unref_ask(ask);
1533
0
  return rc;
1534
0
}
1535
1536
static int add_logical(struct fdisk_context *cxt,
1537
           struct fdisk_partition *pa,
1538
           size_t *partno)
1539
0
{
1540
0
  struct pte *pe;
1541
0
  int rc;
1542
1543
0
  assert(cxt);
1544
0
  assert(partno);
1545
0
  assert(cxt->label);
1546
0
  assert(self_label(cxt)->ext_offset);
1547
1548
0
  DBG(LABEL, ul_debug("DOS: nparts max: %zu", cxt->label->nparts_max));
1549
0
  pe = self_pte(cxt, cxt->label->nparts_max);
1550
0
  assert(pe);
1551
1552
0
  if (!pe->sectorbuffer) {
1553
0
    pe->sectorbuffer = calloc(1, cxt->sector_size);
1554
0
    if (!pe->sectorbuffer)
1555
0
      return -ENOMEM;
1556
0
    DBG(LABEL, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
1557
0
          cxt->label->nparts_max, pe->sectorbuffer));
1558
0
    pe->private_sectorbuffer = 1;
1559
0
  }
1560
0
  pe->pt_entry = mbr_get_partition(pe->sectorbuffer, 0);
1561
0
  pe->ex_entry = pe->pt_entry + 1;
1562
0
  pe->offset = 0;
1563
0
  partition_set_changed(cxt, cxt->label->nparts_max, 1);
1564
1565
0
  cxt->label->nparts_max++;
1566
1567
  /* this message makes sense only when we use extended/primary/logical
1568
   * dialog. The dialog is disable for scripts, see dos_add_partition() */
1569
0
  if (!cxt->script)
1570
0
    fdisk_info(cxt, _("Adding logical partition %zu"),
1571
0
        cxt->label->nparts_max);
1572
0
  *partno = cxt->label->nparts_max - 1;
1573
0
  rc = add_partition(cxt, *partno, pa);
1574
1575
0
  if (rc) {
1576
    /* reset on error */
1577
0
    cxt->label->nparts_max--;
1578
0
    pe->pt_entry = NULL;
1579
0
    pe->ex_entry = NULL;
1580
0
    pe->offset = 0;
1581
0
    pe->changed = 0;
1582
0
  }
1583
1584
0
  return rc;
1585
0
}
1586
1587
static int check(struct fdisk_context *cxt, size_t n,
1588
     unsigned int h, unsigned int s, unsigned int c,
1589
     unsigned int lba_sector)
1590
0
{
1591
0
  unsigned int chs_sector, real_s, real_c;
1592
0
  int nerrors = 0;
1593
1594
0
  if (!is_dos_compatible(cxt))
1595
0
    return 0;
1596
1597
0
  real_s = sector(s) - 1;
1598
0
  real_c = cylinder(s, c);
1599
0
  chs_sector = (real_c * cxt->geom.heads + h) * cxt->geom.sectors + real_s;
1600
1601
0
  if (!chs_sector) {
1602
0
    fdisk_warnx(cxt, _("Partition %zu: contains sector 0"), n);
1603
0
    nerrors++;
1604
0
  }
1605
0
  if (h >= cxt->geom.heads) {
1606
0
    fdisk_warnx(cxt, _("Partition %zu: head %d greater than "
1607
0
           "maximum %d"), n, h + 1, cxt->geom.heads);
1608
0
    nerrors++;
1609
0
  }
1610
0
  if (real_s >= cxt->geom.sectors) {
1611
0
    fdisk_warnx(cxt, _("Partition %zu: sector %d greater than "
1612
0
           "maximum %ju"), n, real_s + 1,
1613
0
        (uintmax_t) cxt->geom.sectors);
1614
0
    nerrors++;
1615
0
  }
1616
0
  if (real_c >= cxt->geom.cylinders) {
1617
0
    fdisk_warnx(cxt, _("Partition %zu: cylinder %d greater than "
1618
0
           "maximum %ju"),
1619
0
        n, real_c + 1,
1620
0
        (uintmax_t) cxt->geom.cylinders);
1621
0
    nerrors++;
1622
0
  }
1623
0
  if (lba_sector / (cxt->geom.heads * cxt->geom.sectors) < 1024 && lba_sector != chs_sector) {
1624
0
    fdisk_warnx(cxt, _("Partition %zu: LBA sector %u "
1625
0
           "disagrees with C/H/S calculated sector %u"),
1626
0
        n, lba_sector, chs_sector);
1627
0
    nerrors++;
1628
0
  }
1629
1630
0
  return nerrors;
1631
0
}
1632
1633
/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1634
 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1635
 * Jan.  1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1636
 * Lubkin Oct.  1991). */
1637
1638
static void
1639
long2chs(struct fdisk_context *cxt, unsigned long ls,
1640
0
   unsigned int *c, unsigned int *h, unsigned int *s) {
1641
0
  int spc = cxt->geom.heads * cxt->geom.sectors;
1642
1643
0
  *c = ls / spc;
1644
0
  ls = ls % spc;
1645
0
  *h = ls / cxt->geom.sectors;
1646
0
  *s = ls % cxt->geom.sectors + 1;  /* sectors count from 1 */
1647
0
}
1648
1649
static int check_consistency(struct fdisk_context *cxt, struct dos_partition *p,
1650
            size_t partition)
1651
0
{
1652
0
  unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */
1653
0
  unsigned int pec, peh, pes; /* physical ending c, h, s */
1654
0
  unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */
1655
0
  unsigned int lec, leh, les; /* logical ending c, h, s */
1656
0
  int nerrors = 0;
1657
1658
0
  if (!is_dos_compatible(cxt))
1659
0
    return 0;
1660
1661
0
  if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
1662
0
    return 0;   /* do not check extended partitions */
1663
1664
  /* physical beginning c, h, s */
1665
0
  pbc = cylinder(p->bs, p->bc);
1666
0
  pbh = p->bh;
1667
0
  pbs = sector(p->bs);
1668
1669
  /* physical ending c, h, s */
1670
0
  pec = cylinder(p->es, p->ec);
1671
0
  peh = p->eh;
1672
0
  pes = sector(p->es);
1673
1674
  /* compute logical beginning (c, h, s) */
1675
0
  long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs);
1676
1677
  /* compute logical ending (c, h, s) */
1678
0
  long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les);
1679
1680
  /* Same physical / logical beginning? */
1681
0
  if (lbc < 1024
1682
0
      && (pbc != lbc || pbh != lbh || pbs != lbs)) {
1683
0
    fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
1684
0
      "beginnings (non-Linux?): "
1685
0
      "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1686
0
      partition + 1,
1687
0
      pbc, pbh, pbs,
1688
0
      lbc, lbh, lbs);
1689
0
    nerrors++;
1690
0
  }
1691
1692
  /* Same physical / logical ending? */
1693
0
  if (lec < 1024
1694
0
      && (pec != lec || peh != leh || pes != les)) {
1695
0
    fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
1696
0
      "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1697
0
      partition + 1,
1698
0
      pec, peh, pes,
1699
0
      lec, leh, les);
1700
0
    nerrors++;
1701
0
  }
1702
1703
  /* Ending on cylinder boundary? */
1704
0
  if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
1705
0
    fdisk_warnx(cxt, _("Partition %zu: does not end on "
1706
0
           "cylinder boundary."),
1707
0
      partition + 1);
1708
0
    nerrors++;
1709
0
  }
1710
1711
0
  return nerrors;
1712
0
}
1713
1714
static void fill_bounds(struct fdisk_context *cxt,
1715
      fdisk_sector_t *first, fdisk_sector_t *last)
1716
0
{
1717
0
  size_t i;
1718
0
  struct pte *pe = self_pte(cxt, 0);
1719
0
  struct dos_partition *p;
1720
1721
0
  assert(pe);
1722
0
  for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
1723
0
    p = pe->pt_entry;
1724
0
    if (is_cleared_partition(p) || IS_EXTENDED (p->sys_ind)) {
1725
0
      first[i] = SIZE_MAX;
1726
0
      last[i] = 0;
1727
0
    } else {
1728
0
      first[i] = get_abs_partition_start(pe);
1729
0
      last[i]  = get_abs_partition_end(pe);
1730
0
    }
1731
0
  }
1732
0
}
1733
1734
static int dos_verify_disklabel(struct fdisk_context *cxt)
1735
0
{
1736
0
  size_t i, j;
1737
0
  fdisk_sector_t total = 1, n_sectors = cxt->total_sectors;
1738
0
  fdisk_sector_t *first, *last;
1739
0
  struct dos_partition *p;
1740
0
  struct fdisk_dos_label *l = self_label(cxt);
1741
0
  int nerrors = 0;
1742
1743
0
  assert(fdisk_is_label(cxt, DOS));
1744
1745
0
  first = calloc(cxt->label->nparts_max, sizeof(*first));
1746
0
  last = calloc(cxt->label->nparts_max, sizeof(*first));
1747
1748
0
  if (!first || !last) {
1749
0
    free(first);
1750
0
    free(last);
1751
0
    return -ENOMEM;
1752
0
  }
1753
1754
0
  fill_bounds(cxt, first, last);
1755
0
  for (i = 0; i < cxt->label->nparts_max; i++) {
1756
0
    struct pte *pe = self_pte(cxt, i);
1757
1758
0
    p = self_partition(cxt, i);
1759
0
    if (p && is_used_partition(p) && !IS_EXTENDED(p->sys_ind)) {
1760
0
      nerrors += check_consistency(cxt, p, i);
1761
0
      assert(pe);
1762
0
      if (get_abs_partition_start(pe) < first[i]) {
1763
0
        fdisk_warnx(cxt, _(
1764
0
          "Partition %zu: bad start-of-data."),
1765
0
           i + 1);
1766
0
        nerrors++;
1767
0
      }
1768
1769
0
      nerrors += check(cxt, i + 1, p->bh, p->bs, p->bc, first[i]);
1770
0
      nerrors += check(cxt, i + 1, p->eh, p->es, p->ec, last[i]);
1771
0
      total += last[i] + 1 - first[i];
1772
1773
0
      if (i == 0)
1774
0
        total += get_abs_partition_start(pe) - 1;
1775
1776
0
      for (j = 0; j < i; j++) {
1777
0
        if ((first[i] >= first[j] && first[i] <= last[j])
1778
0
            || ((last[i] <= last[j] && last[i] >= first[j]))) {
1779
1780
0
          fdisk_warnx(cxt, _("Partition %zu: "
1781
0
            "overlaps partition %zu."),
1782
0
            j + 1, i + 1);
1783
0
          nerrors++;
1784
1785
0
          total += first[i] >= first[j] ?
1786
0
            first[i] : first[j];
1787
0
          total -= last[i] <= last[j] ?
1788
0
            last[i] : last[j];
1789
0
        }
1790
0
      }
1791
0
    }
1792
0
  }
1793
1794
0
  if (l->ext_offset) {
1795
0
    fdisk_sector_t e_last;
1796
0
    struct pte *ext_pe = self_pte(cxt, l->ext_index);
1797
1798
0
    assert(ext_pe);
1799
0
    e_last = get_abs_partition_end(ext_pe);
1800
1801
0
    for (i = 4; i < cxt->label->nparts_max; i++) {
1802
0
      total++;
1803
0
      p = self_partition(cxt, i);
1804
0
      assert(p);
1805
1806
0
      if (!p->sys_ind) {
1807
0
        if (i != 4 || i + 1 < cxt->label->nparts_max) {
1808
0
          fdisk_warnx(cxt,
1809
0
            _("Partition %zu: empty."),
1810
0
            i + 1);
1811
0
          nerrors++;
1812
0
        }
1813
0
      } else if (first[i] < l->ext_offset
1814
0
           || last[i] > e_last) {
1815
1816
0
        fdisk_warnx(cxt, _("Logical partition %zu: "
1817
0
          "not entirely in partition %zu."),
1818
0
          i + 1, l->ext_index + 1);
1819
0
        nerrors++;
1820
0
      }
1821
0
    }
1822
0
  }
1823
1824
0
  if (!nerrors) {
1825
0
    fdisk_info(cxt, _("No errors detected."));
1826
0
    if (total > n_sectors)
1827
0
      fdisk_info(cxt, _("Total allocated sectors %ju greater "
1828
0
        "than the maximum %ju."), (uintmax_t) total, (uintmax_t) n_sectors);
1829
0
    else if (total < n_sectors)
1830
0
      fdisk_info(cxt, _("Remaining %ju unallocated %ld-byte "
1831
0
        "sectors."), (uintmax_t) n_sectors - total, cxt->sector_size);
1832
0
  } else
1833
0
    fdisk_warnx(cxt,
1834
0
      P_("%d error detected.", "%d errors detected.", nerrors),
1835
0
      nerrors);
1836
1837
0
  free(first);
1838
0
  free(last);
1839
0
  return nerrors;
1840
0
}
1841
1842
/*
1843
 * Ask the user for new partition type information (logical, extended).
1844
 * This function calls the actual partition adding logic - add_partition.
1845
 *
1846
 * API callback.
1847
 */
1848
static int dos_add_partition(struct fdisk_context *cxt,
1849
           struct fdisk_partition *pa,
1850
           size_t *partno)
1851
0
{
1852
0
  size_t i;
1853
0
  uint8_t free_primary = 0, free_sectors = 0;
1854
0
  fdisk_sector_t first = 0, grain;
1855
0
  int rc = 0;
1856
0
  struct fdisk_dos_label *l;
1857
0
  struct pte *ext_pe;
1858
0
  size_t res = 0;   /* partno */
1859
1860
0
  assert(cxt);
1861
0
  assert(cxt->label);
1862
0
  assert(fdisk_is_label(cxt, DOS));
1863
1864
0
  DBG(LABEL, ul_debug("DOS: new partition wanted"));
1865
1866
0
  l = self_label(cxt);
1867
1868
0
  if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
1869
0
    fdisk_warnx(cxt, _("The maximum number of partitions has "
1870
0
          "been created."));
1871
0
    return -EINVAL;
1872
0
  }
1873
1874
0
  ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
1875
1876
  /*
1877
   * partition template (@pa) based partitioning
1878
   */
1879
1880
  /* A) template specifies start within extended partition; add logical */
1881
0
  if (pa && fdisk_partition_has_start(pa) && ext_pe
1882
0
      && pa->start >= l->ext_offset
1883
0
      && pa->start <= get_abs_partition_end(ext_pe)) {
1884
0
    DBG(LABEL, ul_debug("DOS: pa template %p: add logical (by offset)", pa));
1885
1886
0
    if (fdisk_partition_has_partno(pa) && fdisk_partition_get_partno(pa) < 4) {
1887
0
      DBG(LABEL, ul_debug("DOS: pa template specifies partno<4 for logical partition"));
1888
0
      return -EINVAL;
1889
0
    }
1890
0
    rc = add_logical(cxt, pa, &res);
1891
0
    goto done;
1892
1893
  /* B) template specifies start out of extended partition; add primary */
1894
0
  } else if (pa && fdisk_partition_has_start(pa) && ext_pe) {
1895
0
    DBG(LABEL, ul_debug("DOS: pa template %p: add primary (by offset)", pa));
1896
1897
0
    if (fdisk_partition_has_partno(pa) && fdisk_partition_get_partno(pa) >= 4) {
1898
0
      DBG(LABEL, ul_debug("DOS: pa template specifies partno>=4 for primary partition"));
1899
0
      return -EINVAL;
1900
0
    }
1901
0
    if (ext_pe && pa->type && IS_EXTENDED(pa->type->code)) {
1902
0
      fdisk_warnx(cxt, _("Extended partition already exists."));
1903
0
      return -EINVAL;
1904
0
    }
1905
0
    rc = get_partition_unused_primary(cxt, pa, &res);
1906
0
    if (rc == 0)
1907
0
      rc = add_partition(cxt, res, pa);
1908
0
    goto done;
1909
1910
  /* C) template specifies start (or default), partno < 4; add primary */
1911
0
  } else if (pa && (fdisk_partition_start_is_default(pa) || fdisk_partition_has_start(pa))
1912
0
       && fdisk_partition_has_partno(pa)
1913
0
       && pa->partno < 4) {
1914
0
    DBG(LABEL, ul_debug("DOS: pa template %p: add primary (by partno)", pa));
1915
1916
0
    if (ext_pe && pa->type && IS_EXTENDED(pa->type->code)) {
1917
0
      fdisk_warnx(cxt, _("Extended partition already exists."));
1918
0
      return -EINVAL;
1919
0
    }
1920
0
    rc = get_partition_unused_primary(cxt, pa, &res);
1921
0
    if (rc == 0)
1922
0
      rc = add_partition(cxt, res, pa);
1923
0
    goto done;
1924
1925
  /* D) template specifies start (or default), partno >= 4; add logical */
1926
0
  } else if (pa && (fdisk_partition_start_is_default(pa) || fdisk_partition_has_start(pa))
1927
0
       && fdisk_partition_has_partno(pa)
1928
0
       && pa->partno >= 4) {
1929
0
    DBG(LABEL, ul_debug("DOS: pa template %p: add logical (by partno)", pa));
1930
1931
0
    if (!ext_pe) {
1932
0
      fdisk_warnx(cxt, _("Extended partition does not exists. Failed to add logical partition."));
1933
0
      return -EINVAL;
1934
0
    }
1935
1936
0
    if (fdisk_partition_has_start(pa)
1937
0
         && pa->start < l->ext_offset
1938
0
         && pa->start > get_abs_partition_end(ext_pe)) {
1939
0
      DBG(LABEL, ul_debug("DOS: pa template specifies partno>=4, but start out of extended"));
1940
0
      return -EINVAL;
1941
0
    }
1942
1943
0
    rc = add_logical(cxt, pa, &res);
1944
0
    goto done;
1945
0
  }
1946
1947
0
  DBG(LABEL, ul_debug("DOS: dialog driven partitioning"));
1948
  /* Note @pa may be still used for things like partition type, etc */
1949
1950
  /* check if there is space for primary partition */
1951
0
  grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1;
1952
0
  first = cxt->first_lba;
1953
1954
0
  if (cxt->parent && fdisk_is_label(cxt->parent, GPT)) {
1955
    /* modifying a hybrid MBR, which throws out the rules */
1956
0
    grain = 1;
1957
0
    first = 1;
1958
0
  }
1959
1960
  /* set @first after the last used partition, set @free_sectors if there
1961
   * is gap in front if the first partition or between used parrtitions. */
1962
0
  for (i = 0; i < 4; i++) {
1963
0
    struct dos_partition *p = self_partition(cxt, i);
1964
1965
0
    if (p && is_used_partition(p)) {
1966
0
      fdisk_sector_t start = dos_partition_get_start(p);
1967
0
      if (first + grain <= start)
1968
0
        free_sectors = 1;
1969
0
      first = start + dos_partition_get_size(p);
1970
0
    } else
1971
0
      free_primary++;
1972
0
  }
1973
1974
  /* set @free_sectors if there is a space after the first usable sector */
1975
0
  if (first + grain - 1 <= cxt->total_sectors - 1)
1976
0
    free_sectors = 1;
1977
1978
0
  DBG(LABEL, ul_debug("DOS: primary: first free: %ju, last on disk: %ju, "
1979
0
          "free_sectors=%d, free_primary=%d",
1980
0
        (uintmax_t) first,
1981
0
        (uintmax_t) cxt->total_sectors - 1,
1982
0
        free_sectors, free_primary));
1983
1984
0
  if (!free_primary || !free_sectors) {
1985
0
    DBG(LABEL, ul_debug("DOS: primary impossible"));
1986
0
    if (l->ext_offset) {
1987
0
      if (!pa || fdisk_partition_has_start(pa)) {
1988
        /* See above case A); here we have start, but
1989
         * out of extended partition */
1990
0
        const char *msg;
1991
0
        if (!free_primary)
1992
0
          msg = _("All primary partitions are in use.");
1993
0
        else
1994
0
          msg =  _("All space for primary partitions is in use.");
1995
1996
0
        if (pa && fdisk_partition_has_start(pa)) {
1997
0
          fdisk_warnx(cxt, "%s", msg);
1998
0
          return -EINVAL;
1999
0
        }
2000
0
        fdisk_info(cxt, "%s", msg);
2001
0
      }
2002
0
      DBG(LABEL, ul_debug("DOS: trying logical"));
2003
0
      rc = add_logical(cxt, pa, &res);
2004
0
    } else {
2005
0
      if (free_primary)
2006
0
        fdisk_info(cxt, _("All space for primary partitions is in use."));
2007
0
      else
2008
      /* TRANSLATORS: Try to keep this within 80 characters. */
2009
0
        fdisk_info(cxt, _("To create more partitions, first replace "
2010
0
            "a primary with an extended partition."));
2011
0
      return -EINVAL;
2012
0
    }
2013
0
  } else {
2014
0
    char hint[BUFSIZ];
2015
0
    struct fdisk_ask *ask;
2016
0
    int c = 0;
2017
2018
    /* the default layout for scripts is to create primary partitions */
2019
0
    if (cxt->script || !fdisk_has_dialogs(cxt)) {
2020
0
      rc = get_partition_unused_primary(cxt, pa, &res);
2021
0
      if (rc == 0)
2022
0
        rc = add_partition(cxt, res, pa);
2023
0
      goto done;
2024
0
    }
2025
2026
0
    ask = fdisk_new_ask();
2027
0
    if (!ask)
2028
0
      return -ENOMEM;
2029
0
    fdisk_ask_set_type(ask, FDISK_ASKTYPE_MENU);
2030
0
    fdisk_ask_set_query(ask, _("Partition type"));
2031
0
    fdisk_ask_menu_set_default(ask, free_primary == 1
2032
0
            && !l->ext_offset ? 'e' : 'p');
2033
0
    snprintf(hint, sizeof(hint),
2034
0
        _("%u primary, %d extended, %u free"),
2035
0
        4 - (l->ext_offset ? 1 : 0) - free_primary,
2036
0
        l->ext_offset ? 1 : 0,
2037
0
        free_primary);
2038
2039
0
    fdisk_ask_menu_add_item(ask, 'p', _("primary"), hint);
2040
0
    if (!l->ext_offset)
2041
0
      fdisk_ask_menu_add_item(ask, 'e', _("extended"), _("container for logical partitions"));
2042
0
    else
2043
0
      fdisk_ask_menu_add_item(ask, 'l', _("logical"), _("numbered from 5"));
2044
2045
0
    rc = fdisk_do_ask(cxt, ask);
2046
0
    if (!rc)
2047
0
      fdisk_ask_menu_get_result(ask, &c);
2048
0
    fdisk_unref_ask(ask);
2049
0
    if (rc)
2050
0
      return rc;
2051
2052
0
    if (c == 'p') {
2053
0
      rc = get_partition_unused_primary(cxt, pa, &res);
2054
0
      if (rc == 0)
2055
0
        rc = add_partition(cxt, res, pa);
2056
0
      goto done;
2057
0
    } else if (c == 'l' && l->ext_offset) {
2058
0
      rc = add_logical(cxt, pa, &res);
2059
0
      goto done;
2060
0
    } else if (c == 'e' && !l->ext_offset) {
2061
0
      rc = get_partition_unused_primary(cxt, pa, &res);
2062
0
      if (rc == 0) {
2063
0
        struct fdisk_partition *xpa = NULL;
2064
0
        struct fdisk_parttype *t;
2065
2066
0
        t = fdisk_label_get_parttype_from_code(cxt->label,
2067
0
            MBR_DOS_EXTENDED_PARTITION);
2068
0
        if (!pa) {
2069
0
          pa = xpa = fdisk_new_partition();
2070
0
          if (!xpa)
2071
0
            return -ENOMEM;
2072
0
        }
2073
0
        fdisk_partition_set_type(pa, t);
2074
0
        rc = add_partition(cxt, res, pa);
2075
0
        if (xpa) {
2076
0
          fdisk_unref_partition(xpa);
2077
0
          pa = NULL;
2078
0
        }
2079
0
      }
2080
0
      goto done;
2081
0
    } else
2082
0
      fdisk_warnx(cxt, _("Invalid partition type `%c'."), c);
2083
0
  }
2084
0
done:
2085
0
  if (rc == 0) {
2086
0
    cxt->label->nparts_cur++;
2087
0
    if (partno)
2088
0
      *partno = res;
2089
0
  }
2090
0
  return rc;
2091
0
}
2092
2093
static int write_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
2094
             unsigned char *buf)
2095
0
{
2096
0
  int rc;
2097
2098
0
  rc = seek_sector(cxt, secno);
2099
0
  if (rc != 0) {
2100
0
    fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"),
2101
0
        (uintmax_t) secno);
2102
0
    return rc;
2103
0
  }
2104
2105
0
  DBG(LABEL, ul_debug("DOS: writing to sector %ju", (uintmax_t) secno));
2106
2107
0
  if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
2108
0
    return -errno;
2109
0
  return 0;
2110
0
}
2111
2112
static int dos_write_disklabel(struct fdisk_context *cxt)
2113
0
{
2114
0
  struct fdisk_dos_label *l = self_label(cxt);
2115
0
  size_t i;
2116
0
  int rc = 0, mbr_changed = 0;
2117
2118
0
  assert(cxt);
2119
0
  assert(cxt->label);
2120
0
  assert(fdisk_is_label(cxt, DOS));
2121
2122
0
  DBG(LABEL, ul_debug("DOS: write PT requested [label-changed: %d, non-pt-changed: %d]",
2123
0
        cxt->label->changed, l->non_pt_changed));
2124
2125
0
  mbr_changed = l->non_pt_changed;
2126
2127
  /* MBR (primary partitions) */
2128
0
  if (!mbr_changed) {
2129
0
    for (i = 0; i < 4; i++) {
2130
0
      struct pte *pe = self_pte(cxt, i);
2131
2132
0
      assert(pe);
2133
0
      if (pe->changed)
2134
0
        mbr_changed = 1;
2135
0
    }
2136
0
  }
2137
0
  if (mbr_changed) {
2138
0
    DBG(LABEL, ul_debug("DOS: MBR changed, writing"));
2139
0
    mbr_set_magic(cxt->firstsector);
2140
0
    rc = write_sector(cxt, 0, cxt->firstsector);
2141
0
    if (rc)
2142
0
      goto done;
2143
0
  }
2144
2145
0
  if (cxt->label->nparts_max <= 4 && l->ext_offset) {
2146
    /* we have empty extended partition, check if the partition has
2147
     * been modified and then cleanup possible remaining EBR  */
2148
0
    struct pte *pe = self_pte(cxt, l->ext_index);
2149
0
    unsigned char empty[512] = { 0 };
2150
0
    fdisk_sector_t off = pe ? get_abs_partition_start(pe) : 0;
2151
2152
0
    if (off && pe->changed) {
2153
0
      mbr_set_magic(empty);
2154
0
      write_sector(cxt, off, empty);
2155
0
    }
2156
0
  }
2157
2158
  /* EBR (logical partitions) */
2159
0
  for (i = 4; i < cxt->label->nparts_max; i++) {
2160
0
    struct pte *pe = self_pte(cxt, i);
2161
2162
0
    assert(pe);
2163
0
    if (!pe->changed || !pe->offset || !pe->sectorbuffer)
2164
0
      continue;
2165
2166
0
    mbr_set_magic(pe->sectorbuffer);
2167
0
    rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
2168
0
    if (rc)
2169
0
      goto done;
2170
0
  }
2171
2172
0
done:
2173
0
  return rc;
2174
0
}
2175
2176
static int dos_locate_disklabel(struct fdisk_context *cxt, int n,
2177
    const char **name, uint64_t *offset, size_t *size)
2178
0
{
2179
0
  assert(cxt);
2180
2181
0
  *name = NULL;
2182
0
  *offset = 0;
2183
0
  *size = 0;
2184
2185
0
  switch (n) {
2186
0
  case 0:
2187
0
    *name = "MBR";
2188
0
    *offset = 0;
2189
0
    *size = 512;
2190
0
    break;
2191
0
  default:
2192
    /* extended partitions */
2193
0
    if ((size_t)n - 1 + 4 < cxt->label->nparts_max) {
2194
0
      struct pte *pe = self_pte(cxt, n - 1 + 4);
2195
2196
0
      assert(pe);
2197
0
      assert(pe->private_sectorbuffer);
2198
2199
0
      *name = "EBR";
2200
0
      *offset = (uint64_t) pe->offset * cxt->sector_size;
2201
0
      *size = 512;
2202
0
    } else
2203
0
      return 1;
2204
0
    break;
2205
0
  }
2206
2207
0
  return 0;
2208
0
}
2209
2210
/*
2211
 * Check whether partition entries are ordered by their starting positions.
2212
 * Return 0 if OK. Return i if partition i should have been earlier.
2213
 * Two separate checks: primary and logical partitions.
2214
 */
2215
static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
2216
0
{
2217
0
  size_t last_p_start_pos = 0, p_start_pos;
2218
0
  size_t i, last_i = 0;
2219
2220
0
  for (i = 0 ; i < cxt->label->nparts_max; i++) {
2221
2222
0
    struct pte *pe = self_pte(cxt, i);
2223
0
    struct dos_partition *p;
2224
2225
0
    assert(pe);
2226
0
    p = pe->pt_entry;
2227
2228
0
    if (i == 4) {
2229
0
      last_i = 4;
2230
0
      last_p_start_pos = 0;
2231
0
    }
2232
0
    if (is_used_partition(p)) {
2233
0
      p_start_pos = get_abs_partition_start(pe);
2234
2235
0
      if (last_p_start_pos > p_start_pos) {
2236
0
        if (prev)
2237
0
          *prev = last_i;
2238
0
        return i;
2239
0
      }
2240
2241
0
      last_p_start_pos = p_start_pos;
2242
0
      last_i = i;
2243
0
    }
2244
0
  }
2245
0
  return 0;
2246
0
}
2247
2248
static int dos_get_disklabel_item(struct fdisk_context *cxt, struct fdisk_labelitem *item)
2249
0
{
2250
0
  int rc = 0;
2251
2252
0
  assert(cxt);
2253
0
  assert(cxt->label);
2254
0
  assert(fdisk_is_label(cxt, DOS));
2255
2256
0
  switch (item->id) {
2257
0
  case FDISK_LABELITEM_ID:
2258
0
  {
2259
0
    unsigned int num = mbr_get_id(cxt->firstsector);
2260
0
    item->name = _("Disk identifier");
2261
0
    item->type = 's';
2262
0
    if (asprintf(&item->data.str, "0x%08x", num) < 0)
2263
0
      rc = -ENOMEM;
2264
0
    break;
2265
0
  }
2266
0
  default:
2267
0
    if (item->id < __FDISK_NLABELITEMS)
2268
0
      rc = 1; /* unsupported generic item */
2269
0
    else
2270
0
      rc = 2; /* out of range */
2271
0
    break;
2272
0
  }
2273
2274
0
  return rc;
2275
2276
0
}
2277
2278
static int dos_get_partition(struct fdisk_context *cxt, size_t n,
2279
           struct fdisk_partition *pa)
2280
0
{
2281
0
  struct dos_partition *p;
2282
0
  struct pte *pe;
2283
0
  struct fdisk_dos_label *lb;
2284
2285
0
  assert(cxt);
2286
0
  assert(pa);
2287
0
  assert(cxt->label);
2288
0
  assert(fdisk_is_label(cxt, DOS));
2289
2290
0
  lb = self_label(cxt);
2291
2292
0
  pe = self_pte(cxt, n);
2293
0
  assert(pe);
2294
2295
0
  p = pe->pt_entry;
2296
0
  pa->used = !is_cleared_partition(p);
2297
0
  if (!pa->used)
2298
0
    return 0;
2299
2300
0
  pa->type = dos_partition_parttype(cxt, p);
2301
0
  pa->boot = p->boot_ind == ACTIVE_FLAG ? 1 : 0;
2302
0
  pa->start = get_abs_partition_start(pe);
2303
0
  pa->size = dos_partition_get_size(p);
2304
0
  pa->container = lb->ext_offset && n == lb->ext_index;
2305
2306
0
  if (n >= 4)
2307
0
    pa->parent_partno = lb->ext_index;
2308
2309
0
  if (p->boot_ind && asprintf(&pa->attrs, "%02x", p->boot_ind) < 0)
2310
0
    return -ENOMEM;
2311
2312
  /* start C/H/S */
2313
0
  if (asprintf(&pa->start_chs, "%d/%d/%d",
2314
0
        cylinder(p->bs, p->bc),
2315
0
        p->bh,
2316
0
        sector(p->bs)) < 0)
2317
0
    return -ENOMEM;
2318
2319
  /* end C/H/S */
2320
0
  if (asprintf(&pa->end_chs, "%d/%d/%d",
2321
0
        cylinder(p->es, p->ec),
2322
0
        p->eh,
2323
0
        sector(p->es)) < 0)
2324
0
    return -ENOMEM;
2325
2326
0
  return 0;
2327
0
}
2328
2329
static int has_logical(struct fdisk_context *cxt)
2330
0
{
2331
0
  size_t i;
2332
0
  struct fdisk_dos_label *l = self_label(cxt);
2333
2334
0
  for (i = 4; i < cxt->label->nparts_max; i++) {
2335
0
    if (l->ptes[i].pt_entry)
2336
0
      return 1;
2337
0
  }
2338
0
  return 0;
2339
0
}
2340
2341
static int dos_set_partition(struct fdisk_context *cxt, size_t n,
2342
           struct fdisk_partition *pa)
2343
0
{
2344
0
  struct fdisk_dos_label *l;
2345
0
  struct dos_partition *p;
2346
0
  struct pte *pe;
2347
0
  int orgtype;
2348
0
  fdisk_sector_t start, size;
2349
2350
0
  assert(cxt);
2351
0
  assert(pa);
2352
0
  assert(cxt->label);
2353
0
  assert(fdisk_is_label(cxt, DOS));
2354
2355
0
  if (n >= cxt->label->nparts_max)
2356
0
    return -EINVAL;
2357
2358
0
  l = self_label(cxt);
2359
0
  p = self_partition(cxt, n);
2360
0
  assert(p);
2361
2362
0
  pe = self_pte(cxt, n);
2363
0
  if (!pe)
2364
0
    return -EINVAL;
2365
2366
0
  orgtype = p->sys_ind;
2367
2368
0
  if (pa->type) {
2369
0
    if (IS_EXTENDED(pa->type->code) && l->ext_offset && l->ext_index != n) {
2370
0
      fdisk_warnx(cxt, _("Extended partition already exists."));
2371
0
      return -EINVAL;
2372
0
    }
2373
2374
0
    if (!pa->type->code)
2375
0
      fdisk_warnx(cxt, _("Type 0 means free space to many systems. "
2376
0
           "Having partitions of type 0 is probably unwise."));
2377
2378
0
    if (IS_EXTENDED(p->sys_ind) && !IS_EXTENDED(pa->type->code) && has_logical(cxt)) {
2379
0
      fdisk_warnx(cxt, _(
2380
0
        "Cannot change type of the extended partition which is "
2381
0
        "already used by logical partitions. Delete logical "
2382
0
        "partitions first."));
2383
0
      return -EINVAL;
2384
0
    }
2385
0
  }
2386
2387
0
  FDISK_INIT_UNDEF(start);
2388
0
  FDISK_INIT_UNDEF(size);
2389
2390
0
  if (fdisk_partition_has_start(pa))
2391
0
    start = pa->start;
2392
0
  if (fdisk_partition_has_size(pa))
2393
0
    size = pa->size;
2394
2395
0
  if (!FDISK_IS_UNDEF(start) || !FDISK_IS_UNDEF(size)) {
2396
0
    DBG(LABEL, ul_debug("DOS: resize partition"));
2397
2398
0
    if (FDISK_IS_UNDEF(start))
2399
0
      start = get_abs_partition_start(pe);
2400
0
    if (FDISK_IS_UNDEF(size))
2401
0
      size = dos_partition_get_size(p);
2402
2403
0
    set_partition(cxt, n, 0, start, start + size - 1,
2404
0
        pa->type  ? pa->type->code : p->sys_ind,
2405
0
        FDISK_IS_UNDEF(pa->boot) ?
2406
0
          p->boot_ind == ACTIVE_FLAG :
2407
0
          fdisk_partition_is_bootable(pa));
2408
0
  } else {
2409
0
    DBG(LABEL, ul_debug("DOS: keep size, modify properties"));
2410
0
    if (pa->type)
2411
0
      p->sys_ind = pa->type->code;
2412
0
    if (!FDISK_IS_UNDEF(pa->boot))
2413
0
      p->boot_ind = fdisk_partition_is_bootable(pa) ? ACTIVE_FLAG : 0;
2414
0
  }
2415
2416
0
  if (pa->type) {
2417
0
    if (IS_EXTENDED(pa->type->code) && !IS_EXTENDED(orgtype)) {
2418
      /* new extended partition - create a reference  */
2419
0
      l->ext_index = n;
2420
0
      l->ext_offset = dos_partition_get_start(p);
2421
0
      pe->ex_entry = p;
2422
0
     } else if (IS_EXTENDED(orgtype)) {
2423
      /* remove extended partition */
2424
0
      cxt->label->nparts_max = 4;
2425
0
      l->ptes[l->ext_index].ex_entry = NULL;
2426
0
      l->ext_offset = 0;
2427
0
      l->ext_index = 0;
2428
0
     }
2429
0
  }
2430
2431
0
  partition_set_changed(cxt, n, 1);
2432
0
  return 0;
2433
0
}
2434
2435
static void print_chain_of_logicals(struct fdisk_context *cxt)
2436
0
{
2437
0
  size_t i;
2438
0
  struct fdisk_dos_label *l = self_label(cxt);
2439
2440
0
  fputc('\n', stdout);
2441
2442
0
  for (i = 4; i < cxt->label->nparts_max; i++) {
2443
0
    struct pte *pe = self_pte(cxt, i);
2444
2445
0
    assert(pe);
2446
0
    fprintf(stderr, "#%02zu EBR [%10ju], "
2447
0
      "data[start=%10ju (%10ju), size=%10ju], "
2448
0
      "link[start=%10ju (%10ju), size=%10ju]\n",
2449
0
      i, (uintmax_t) pe->offset,
2450
      /* data */
2451
0
      (uintmax_t) dos_partition_get_start(pe->pt_entry),
2452
0
      (uintmax_t) get_abs_partition_start(pe),
2453
0
      (uintmax_t) dos_partition_get_size(pe->pt_entry),
2454
      /* link */
2455
0
      (uintmax_t) dos_partition_get_start(pe->ex_entry),
2456
0
      (uintmax_t) l->ext_offset + dos_partition_get_start(pe->ex_entry),
2457
0
      (uintmax_t) dos_partition_get_size(pe->ex_entry));
2458
0
  }
2459
0
}
2460
2461
static int cmp_ebr_offsets(const void *a, const void *b)
2462
0
{
2463
0
  const struct pte *ae = (const struct pte *) a,
2464
0
       *be = (const struct pte *) b;
2465
2466
0
  if (ae->offset == 0 && be->offset == 0)
2467
0
    return 0;
2468
0
  if (ae->offset == 0)
2469
0
    return 1;
2470
0
  if (be->offset == 0)
2471
0
    return -1;
2472
2473
0
  return cmp_numbers(ae->offset, be->offset);
2474
0
}
2475
2476
/*
2477
 * Fix the chain of logicals.
2478
 *
2479
 * The function does not modify data partitions within EBR tables
2480
 * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
2481
 * (pte->ex_entry) between EBR tables.
2482
 *
2483
 */
2484
static void fix_chain_of_logicals(struct fdisk_context *cxt)
2485
0
{
2486
0
  struct fdisk_dos_label *l = self_label(cxt);
2487
0
  struct pte *last;
2488
0
  size_t i;
2489
2490
0
  DBG(LABEL, print_chain_of_logicals(cxt));
2491
2492
  /* Sort chain by EBR offsets */
2493
0
  qsort(&l->ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte),
2494
0
      cmp_ebr_offsets);
2495
2496
0
again:
2497
  /* Sort data partitions by start */
2498
0
  for (i = 4; i < cxt->label->nparts_max - 1; i++) {
2499
0
    struct pte *cur = self_pte(cxt, i),
2500
0
         *nxt = self_pte(cxt, i + 1);
2501
2502
0
    assert(cur);
2503
0
    assert(nxt);
2504
2505
0
    if (get_abs_partition_start(cur) >
2506
0
        get_abs_partition_start(nxt)) {
2507
2508
0
      struct dos_partition tmp = *cur->pt_entry;
2509
0
      fdisk_sector_t cur_start = get_abs_partition_start(cur),
2510
0
         nxt_start = get_abs_partition_start(nxt);
2511
2512
      /* swap data partitions */
2513
0
      *cur->pt_entry = *nxt->pt_entry;
2514
0
      *nxt->pt_entry = tmp;
2515
2516
      /* Recount starts according to EBR offsets, the absolute
2517
       * address still has to be the same! */
2518
0
      dos_partition_set_start(cur->pt_entry, nxt_start - cur->offset);
2519
0
      dos_partition_sync_chs(cur->pt_entry, cur->offset, cxt->geom.sectors, cxt->geom.heads);
2520
0
      dos_partition_set_start(nxt->pt_entry, cur_start - nxt->offset);
2521
0
      dos_partition_sync_chs(nxt->pt_entry, nxt->offset, cxt->geom.sectors, cxt->geom.heads);
2522
2523
0
      partition_set_changed(cxt, i, 1);
2524
0
      partition_set_changed(cxt, i + 1, 1);
2525
0
      goto again;
2526
0
    }
2527
0
  }
2528
2529
  /* Update EBR links */
2530
0
  for (i = 4; i < cxt->label->nparts_max - 1; i++) {
2531
0
    struct pte *cur = self_pte(cxt, i),
2532
0
         *nxt = self_pte(cxt, i + 1);
2533
2534
0
    assert(cur);
2535
0
    assert(nxt);
2536
2537
0
    fdisk_sector_t noff = nxt->offset - l->ext_offset,
2538
0
     ooff = dos_partition_get_start(cur->ex_entry);
2539
2540
0
    if (noff == ooff)
2541
0
      continue;
2542
2543
0
    DBG(LABEL, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
2544
0
      (uintmax_t) cur->offset,
2545
0
      (uintmax_t) ooff, (uintmax_t) noff));
2546
2547
0
    set_partition(cxt, i, 1, nxt->offset,
2548
0
        get_abs_partition_end(nxt),
2549
0
        MBR_DOS_EXTENDED_PARTITION, 0);
2550
0
  }
2551
2552
  /* always terminate the chain ! */
2553
0
  last = self_pte(cxt, cxt->label->nparts_max - 1);
2554
0
  if (last) {
2555
0
    clear_partition(last->ex_entry);
2556
0
    partition_set_changed(cxt, cxt->label->nparts_max - 1, 1);
2557
0
  }
2558
2559
0
  DBG(LABEL, print_chain_of_logicals(cxt));
2560
0
}
2561
2562
static int dos_reorder(struct fdisk_context *cxt)
2563
0
{
2564
0
  struct pte *pei, *pek;
2565
0
  size_t i,k;
2566
2567
0
  if (!wrong_p_order(cxt, NULL))
2568
0
    return 1;
2569
2570
0
  while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
2571
    /* partition i should have come earlier, move it */
2572
    /* We have to move data in the MBR */
2573
0
    struct dos_partition *pi, *pk, *pe, pbuf;
2574
0
    pei = self_pte(cxt, i);
2575
0
    pek = self_pte(cxt, k);
2576
2577
0
    assert(pei);
2578
0
    assert(pek);
2579
2580
0
    pe = pei->ex_entry;
2581
0
    pei->ex_entry = pek->ex_entry;
2582
0
    pek->ex_entry = pe;
2583
2584
0
    pi = pei->pt_entry;
2585
0
    pk = pek->pt_entry;
2586
2587
0
    memmove(&pbuf, pi, sizeof(struct dos_partition));
2588
0
    memmove(pi, pk, sizeof(struct dos_partition));
2589
0
    memmove(pk, &pbuf, sizeof(struct dos_partition));
2590
2591
0
    partition_set_changed(cxt, i, 1);
2592
0
    partition_set_changed(cxt, k, 1);
2593
0
  }
2594
2595
0
  if (i)
2596
0
    fix_chain_of_logicals(cxt);
2597
2598
0
  return 0;
2599
0
}
2600
2601
/**
2602
 * fdisk_dos_fix_chs:
2603
 * @cxt: fdisk context
2604
 *
2605
 * Fix beginning and ending C/H/S values for every partition
2606
 * according to LBA relative offset, relative beginning and
2607
 * size and fdisk idea of disk geometry (sectors per track
2608
 * and number of heads).
2609
 *
2610
 * Returns: number of fixed (changed) partitions.
2611
 */
2612
int fdisk_dos_fix_chs(struct fdisk_context *cxt)
2613
0
{
2614
0
  unsigned int obc, obh, obs; /* old beginning c, h, s */
2615
0
  unsigned int oec, oeh, oes; /* old ending c, h, s */
2616
0
  unsigned int nbc, nbh, nbs; /* new beginning c, h, s */
2617
0
  unsigned int nec, neh, nes; /* new ending c, h, s */
2618
0
  fdisk_sector_t l, sects;  /* lba beginning and size */
2619
0
  struct dos_partition *p;
2620
0
  struct pte *pe;
2621
0
  int changed = 0;
2622
0
  size_t i;
2623
2624
0
  assert(fdisk_is_label(cxt, DOS));
2625
2626
0
  for (i = 0; i < cxt->label->nparts_max; i++) {
2627
0
    p = self_partition(cxt, i);
2628
0
    if (!p || !is_used_partition(p))
2629
0
      continue;
2630
2631
0
    pe = self_pte(cxt, i);
2632
2633
    /* old beginning c, h, s */
2634
0
    obc = cylinder(p->bs, p->bc);
2635
0
    obh = p->bh;
2636
0
    obs = sector(p->bs);
2637
2638
    /* old ending c, h, s */
2639
0
    oec = cylinder(p->es, p->ec);
2640
0
    oeh = p->eh;
2641
0
    oes = sector(p->es);
2642
2643
    /* new beginning c, h, s */
2644
0
    l = get_abs_partition_start(pe);
2645
0
    long2chs(cxt, l, &nbc, &nbh, &nbs);
2646
0
    if (l > UINT32_MAX || nbc >= 1024) {
2647
0
      nbc = 1023;
2648
0
      nbh = cxt->geom.heads-1;
2649
0
      nbs = cxt->geom.sectors;
2650
0
    }
2651
2652
    /* new ending c, h, s */
2653
0
    sects = dos_partition_get_size(p);
2654
0
    long2chs(cxt, l + sects - 1, &nec, &neh, &nes);
2655
0
    if (lba_overflowed(l, sects) || nec >= 1024) {
2656
0
      nec = 1023;
2657
0
      neh = cxt->geom.heads-1;
2658
0
      nes = cxt->geom.sectors;
2659
0
    }
2660
2661
0
    if (obc != nbc || obh != nbh || obs != nbs ||
2662
0
        oec != nec || oeh != neh || oes != nes) {
2663
0
      DBG(LABEL, ul_debug("DOS: changing %zu partition CHS "
2664
0
        "from (%d, %d, %d)-(%d, %d, %d) "
2665
0
        "to (%d, %d, %d)-(%d, %d, %d)",
2666
0
        i, obc, obh, obs, oec, oeh, oes,
2667
0
        nbc, nbh, nbs, nec, neh, nes));
2668
0
      p->bc = nbc & 0xff;
2669
0
      p->bh = nbh;
2670
0
      p->bs = nbs | ((nbc >> 2) & 0xc0);
2671
0
      p->ec = nec & 0xff;
2672
0
      p->eh = neh;
2673
0
      p->es = nes | ((nec >> 2) & 0xc0);
2674
0
      partition_set_changed(cxt, i, 1);
2675
0
      changed++;
2676
0
    }
2677
0
  }
2678
2679
0
  return changed;
2680
0
}
2681
2682
/* TODO: use fdisk_set_partition() API */
2683
int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i)
2684
0
{
2685
0
  struct pte *pe;
2686
0
  struct dos_partition *p;
2687
0
  unsigned int new, free_start, curr_start, last;
2688
0
  uintmax_t res = 0;
2689
0
  size_t x;
2690
0
  int rc;
2691
2692
0
  assert(cxt);
2693
0
  assert(fdisk_is_label(cxt, DOS));
2694
2695
0
  pe = self_pte(cxt, i);
2696
0
  if (!pe)
2697
0
    return -EINVAL;
2698
2699
0
  p = pe->pt_entry;
2700
2701
0
  if (!is_used_partition(p) || IS_EXTENDED (p->sys_ind)) {
2702
0
    fdisk_warnx(cxt, _("Partition %zu: no data area."), i + 1);
2703
0
    return 0;
2704
0
  }
2705
2706
  /* The safe start is at the second sector, but some use-cases require
2707
   * to have MBR within the first partition , so default to the first
2708
   * sector of the disk or at the second sector of the extended partition
2709
   */
2710
0
  free_start = pe->offset ? pe->offset + 1 : 0;
2711
2712
0
  curr_start = get_abs_partition_start(pe);
2713
2714
  /* look for a free space before the current start of the partition */
2715
0
  for (x = 0; x < cxt->label->nparts_max; x++) {
2716
0
    unsigned int end;
2717
0
    struct pte *prev_pe = self_pte(cxt, x);
2718
0
    struct dos_partition *prev_p;
2719
2720
0
    assert(prev_pe);
2721
2722
0
    prev_p = prev_pe->pt_entry;
2723
0
    if (!prev_p)
2724
0
      continue;
2725
0
    end = get_abs_partition_start(prev_pe)
2726
0
          + dos_partition_get_size(prev_p);
2727
2728
0
    if (is_used_partition(prev_p) &&
2729
0
        end > free_start && end <= curr_start)
2730
0
      free_start = end;
2731
0
  }
2732
2733
0
  last = get_abs_partition_end(pe);
2734
2735
0
  rc = fdisk_ask_number(cxt, free_start, curr_start, last,
2736
0
      _("New beginning of data"), &res);
2737
0
  if (rc)
2738
0
    return rc;
2739
2740
0
  new = res - pe->offset;
2741
2742
0
  if (new != dos_partition_get_size(p)) {
2743
0
    unsigned int sects = dos_partition_get_size(p)
2744
0
        + dos_partition_get_start(p) - new;
2745
2746
0
    dos_partition_set_size(p, sects);
2747
0
    dos_partition_set_start(p, new);
2748
0
    dos_partition_sync_chs(p, pe->offset, cxt->geom.sectors, cxt->geom.heads);
2749
2750
0
    partition_set_changed(cxt, i, 1);
2751
2752
0
    if (new == 0)
2753
0
      fdisk_info(cxt, _("The new beginning of the partition overlaps the disk "
2754
0
            "label area. Be very careful when using the partition. "
2755
0
            "You can lose all your partitions on the disk."));
2756
0
  }
2757
2758
0
  return rc;
2759
0
}
2760
2761
static int dos_partition_is_used(
2762
    struct fdisk_context *cxt,
2763
    size_t i)
2764
0
{
2765
0
  struct dos_partition *p;
2766
2767
0
  assert(cxt);
2768
0
  assert(cxt->label);
2769
0
  assert(fdisk_is_label(cxt, DOS));
2770
2771
0
  if (i >= cxt->label->nparts_max)
2772
0
    return 0;
2773
2774
0
  p = self_partition(cxt, i);
2775
2776
0
  return p && !is_cleared_partition(p);
2777
0
}
2778
2779
static int dos_toggle_partition_flag(
2780
    struct fdisk_context *cxt,
2781
    size_t i,
2782
    unsigned long flag)
2783
0
{
2784
0
  struct dos_partition *p;
2785
2786
0
  assert(cxt);
2787
0
  assert(cxt->label);
2788
0
  assert(fdisk_is_label(cxt, DOS));
2789
2790
0
  if (i >= cxt->label->nparts_max)
2791
0
    return -EINVAL;
2792
2793
0
  p = self_partition(cxt, i);
2794
0
  assert(p);
2795
2796
0
  switch (flag) {
2797
0
  case DOS_FLAG_ACTIVE:
2798
0
    if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
2799
0
      fdisk_warnx(cxt, _("Partition %zu: is an extended "
2800
0
          "partition."), i + 1);
2801
2802
0
    p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
2803
0
    partition_set_changed(cxt, i, 1);
2804
0
    fdisk_info(cxt, p->boot_ind ?
2805
0
      _("The bootable flag on partition %zu is enabled now.") :
2806
0
      _("The bootable flag on partition %zu is disabled now."),
2807
0
      i + 1);
2808
0
    break;
2809
0
  default:
2810
0
    return 1;
2811
0
  }
2812
2813
0
  return 0;
2814
0
}
2815
2816
static const struct fdisk_field dos_fields[] =
2817
{
2818
  /* basic */
2819
  { FDISK_FIELD_DEVICE, N_("Device"),  10,  0 },
2820
  { FDISK_FIELD_BOOT, N_("Boot"),   1,  0 },
2821
  { FDISK_FIELD_START,  N_("Start"),    5,  FDISK_FIELDFL_NUMBER },
2822
  { FDISK_FIELD_END,  N_("End"),    5,  FDISK_FIELDFL_NUMBER },
2823
  { FDISK_FIELD_SECTORS,  N_("Sectors"),    5,  FDISK_FIELDFL_NUMBER },
2824
  { FDISK_FIELD_CYLINDERS,N_("Cylinders"),  5,  FDISK_FIELDFL_NUMBER },
2825
  { FDISK_FIELD_SIZE, N_("Size"),   5,  FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
2826
  { FDISK_FIELD_TYPEID, N_("Id"),   2,  FDISK_FIELDFL_NUMBER },
2827
  { FDISK_FIELD_TYPE, N_("Type"), 0.1,  0 },
2828
2829
  /* expert mode */
2830
  { FDISK_FIELD_SADDR,  N_("Start-C/H/S"), 1,   FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2831
  { FDISK_FIELD_EADDR,  N_("End-C/H/S"),   1,   FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2832
  { FDISK_FIELD_ATTR, N_("Attrs"),     2,   FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL }
2833
2834
};
2835
2836
static const struct fdisk_label_operations dos_operations =
2837
{
2838
  .probe    = dos_probe_label,
2839
  .write    = dos_write_disklabel,
2840
  .verify   = dos_verify_disklabel,
2841
  .create   = dos_create_disklabel,
2842
  .locate   = dos_locate_disklabel,
2843
  .get_item = dos_get_disklabel_item,
2844
  .set_id   = dos_set_disklabel_id,
2845
2846
  .get_part = dos_get_partition,
2847
  .set_part = dos_set_partition,
2848
  .add_part = dos_add_partition,
2849
  .del_part = dos_delete_partition,
2850
  .reorder  = dos_reorder,
2851
2852
  .part_toggle_flag = dos_toggle_partition_flag,
2853
  .part_is_used = dos_partition_is_used,
2854
2855
  .reset_alignment = dos_reset_alignment,
2856
2857
  .deinit   = dos_deinit,
2858
};
2859
2860
/*
2861
 * allocates DOS in-memory stuff
2862
 */
2863
struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt __attribute__ ((__unused__)))
2864
3.67k
{
2865
3.67k
  struct fdisk_label *lb;
2866
3.67k
  struct fdisk_dos_label *dos;
2867
2868
3.67k
  dos = calloc(1, sizeof(*dos));
2869
3.67k
  if (!dos)
2870
0
    return NULL;
2871
2872
  /* initialize generic part of the driver */
2873
3.67k
  lb = (struct fdisk_label *) dos;
2874
3.67k
  lb->name = "dos";
2875
3.67k
  lb->id = FDISK_DISKLABEL_DOS;
2876
3.67k
  lb->op = &dos_operations;
2877
2878
3.67k
  lb->parttypes = dos_parttypes;
2879
3.67k
  lb->nparttypes = ARRAY_SIZE(dos_parttypes) - 1;
2880
3.67k
  lb->parttype_cuts = dos_parttype_cuts;
2881
3.67k
  lb->nparttype_cuts = ARRAY_SIZE(dos_parttype_cuts);
2882
2883
3.67k
  lb->fields = dos_fields;
2884
3.67k
  lb->nfields = ARRAY_SIZE(dos_fields);
2885
2886
3.67k
  lb->geom_min.sectors = 1;
2887
3.67k
  lb->geom_min.heads = 1;
2888
3.67k
  lb->geom_min.cylinders = 1;
2889
2890
3.67k
  lb->geom_max.sectors = 63;
2891
3.67k
  lb->geom_max.heads = 255;
2892
3.67k
  lb->geom_max.cylinders = 1048576;
2893
2894
  /* return calloc() result to keep static anaylizers happy */
2895
3.67k
  return (struct fdisk_label *) dos;
2896
3.67k
}
2897
2898
/**
2899
 * fdisk_dos_enable_compatible:
2900
 * @lb: DOS label (see fdisk_get_label())
2901
 * @enable: 0 or 1
2902
 *
2903
 * Enables deprecated DOS compatible mode, in this mode library checks for
2904
 * cylinders boundary, cases about CHS addressing and another obscure things.
2905
 *
2906
 * Returns: 0 on success, <0 on error.
2907
 */
2908
int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
2909
0
{
2910
0
  struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
2911
2912
0
  if (!lb)
2913
0
    return -EINVAL;
2914
2915
0
  dos->compatible = enable;
2916
0
  if (enable)
2917
0
    lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
2918
0
  return 0;
2919
0
}
2920
2921
/**
2922
 * fdisk_dos_is_compatible:
2923
 * @lb: DOS label
2924
 *
2925
 * Returns: 0 if DOS compatibility disabled, 1 if enabled
2926
 */
2927
int fdisk_dos_is_compatible(struct fdisk_label *lb)
2928
0
{
2929
0
  return ((struct fdisk_dos_label *) lb)->compatible;
2930
0
}