summaryrefslogtreecommitdiffstats
path: root/libfdisk/src/dos.c
diff options
context:
space:
mode:
Diffstat (limited to 'libfdisk/src/dos.c')
-rw-r--r--libfdisk/src/dos.c275
1 files changed, 169 insertions, 106 deletions
diff --git a/libfdisk/src/dos.c b/libfdisk/src/dos.c
index 4f81970db..fa139a51d 100644
--- a/libfdisk/src/dos.c
+++ b/libfdisk/src/dos.c
@@ -907,55 +907,6 @@ static void set_partition(struct fdisk_context *cxt,
partition_set_changed(cxt, i, 1);
}
-static fdisk_sector_t get_unused_start(struct fdisk_context *cxt,
- int part_n, fdisk_sector_t start,
- fdisk_sector_t first[], fdisk_sector_t last[])
-{
- size_t i;
-
- if (part_n >= 4) {
- struct fdisk_dos_label *l = self_label(cxt);
- fdisk_sector_t ex_start = l->ext_offset + cxt->first_lba;
- if (start < ex_start)
- start = ex_start;
- }
-
- for (i = 0; i < cxt->label->nparts_max; i++) {
- fdisk_sector_t lastplusoff;
- struct pte *pe = self_pte(cxt, i);
-
- assert(pe);
- if (start == pe->offset)
- start += cxt->first_lba;
- lastplusoff = last[i] + ((part_n < 4) ? 0 : cxt->first_lba);
- if (start >= first[i] && start <= lastplusoff)
- start = lastplusoff + 1;
- }
-
- DBG(LABEL, ul_debug("DOS: fist unused start for #%d is %ju",
- part_n, (uintmax_t) start));
- return start;
-}
-
-static void fill_bounds(struct fdisk_context *cxt,
- fdisk_sector_t *first, fdisk_sector_t *last)
-{
- size_t i;
- struct pte *pe = self_pte(cxt, 0);
- struct dos_partition *p;
-
- assert(pe);
- for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
- p = pe->pt_entry;
- if (is_cleared_partition(p) || IS_EXTENDED (p->sys_ind)) {
- first[i] = SIZE_MAX;
- last[i] = 0;
- } else {
- first[i] = get_abs_partition_start(pe);
- last[i] = get_abs_partition_end(pe);
- }
- }
-}
static int get_start_from_user( struct fdisk_context *cxt,
fdisk_sector_t *start,
@@ -1011,75 +962,169 @@ static int get_start_from_user( struct fdisk_context *cxt,
return 0;
}
-static fdisk_sector_t get_possible_last(struct fdisk_context *cxt, size_t n)
+static int find_last_free_sector_in_range(
+ struct fdisk_context *cxt,
+ int logical,
+ fdisk_sector_t begin,
+ fdisk_sector_t end,
+ fdisk_sector_t *result)
+{
+ int last_moved;
+ fdisk_sector_t last = end;
+
+ do {
+ size_t i = logical ? 4 : 0;
+
+ last_moved = 0;
+ for ( ; i < cxt->label->nparts_max; i++) {
+ struct pte *pe = self_pte(cxt, i);
+ fdisk_sector_t p_start = get_abs_partition_start(pe);
+ fdisk_sector_t p_end = get_abs_partition_end(pe);
+
+ if (is_cleared_partition(pe->pt_entry))
+ continue;
+
+ /* count EBR and begin of the logical partition as used area */
+ if (pe->offset)
+ p_start -= cxt->first_lba;
+
+ if (last >= p_start && last <= p_end) {
+ last = p_start - 1;
+ last_moved = 1;
+
+ if (last < begin) {
+ DBG(LABEL, ul_debug("DOS: last free out of range <%ju,%ju>: %ju",
+ (uintmax_t) begin, (uintmax_t) end, (uintmax_t) last));
+
+ return -ENOSPC;
+ }
+ }
+ }
+ } while (last_moved == 1);
+
+ DBG(LABEL, ul_debug("DOS: last unused sector in range <%ju,%ju>: %ju",
+ (uintmax_t) begin, (uintmax_t) end, (uintmax_t) last));
+
+ *result = last;
+ return 0;
+}
+
+static int find_first_free_sector_in_range(
+ struct fdisk_context *cxt,
+ int logical,
+ fdisk_sector_t begin,
+ fdisk_sector_t end,
+ fdisk_sector_t *result)
{
- fdisk_sector_t limit;
+ int first_moved = 0;
+ fdisk_sector_t first = begin;
+
+ do {
+ size_t i = logical ? 4 : 0;
+
+ first_moved = 0;
+ for (; i < cxt->label->nparts_max; i++) {
+ struct pte *pe = self_pte(cxt, i);
+ fdisk_sector_t p_start = get_abs_partition_start(pe);
+ fdisk_sector_t p_end = get_abs_partition_end(pe);
- if (n >= 4) {
+ if (is_cleared_partition(pe->pt_entry))
+ continue;
+ /* count EBR and begin of the logical partition as used area */
+ if (pe->offset)
+ p_start -= cxt->first_lba;
+ if (first < p_start)
+ continue;
+ if (first <= p_end) {
+ first = p_end + 1 + (logical ? cxt->first_lba : 0);
+ first_moved = 1;
+
+ if (first > end) {
+ DBG(LABEL, ul_debug("DOS: first free out of range <%ju,%ju>: %ju",
+ (uintmax_t) begin, (uintmax_t) end, (uintmax_t) first));
+ return -ENOSPC;
+ }
+ }
+ }
+ } while (first_moved == 1);
+
+ DBG(LABEL, ul_debug("DOS: first unused sector in range <%ju,%ju>: %ju",
+ (uintmax_t) begin, (uintmax_t) end, (uintmax_t) first));
+ *result = first;
+ return 0;
+}
+
+static int get_disk_ranges(struct fdisk_context *cxt, int logical,
+ fdisk_sector_t *first, fdisk_sector_t *last)
+{
+ if (logical) {
/* logical partitions */
struct fdisk_dos_label *l = self_label(cxt);
struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
if (!ext_pe)
- return 0;
- limit = get_abs_partition_end(ext_pe);
+ return -EINVAL;
+
+ *first = l->ext_offset + cxt->first_lba;
+ *last = get_abs_partition_end(ext_pe);
+
} else {
/* primary partitions */
if (fdisk_use_cylinders(cxt) || !cxt->total_sectors)
- limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
+ *last = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
else
- limit = cxt->total_sectors - 1;
+ *last = cxt->total_sectors - 1;
- if (limit > UINT_MAX)
- limit = UINT_MAX;
+ if (*last > UINT_MAX)
+ *last = UINT_MAX;
+ *first = cxt->first_lba;
}
- DBG(LABEL, ul_debug("DOS: last possible sector for #%zu is %ju",
- n, (uintmax_t) limit));
- return limit;
+ return 0;
}
-/* returns last free sector for area addressed by @start, the first[] and
- * last[] are fill_bounds() results */
-static fdisk_sector_t get_unused_last(struct fdisk_context *cxt, size_t n,
- fdisk_sector_t start,
- fdisk_sector_t first[])
+static int find_last_free_sector(struct fdisk_context *cxt, int logical, fdisk_sector_t *result)
{
- size_t i;
- fdisk_sector_t limit = get_possible_last(cxt, n);
+ fdisk_sector_t first, last;
+ int rc;
- for (i = 0; i < cxt->label->nparts_max; i++) {
- struct pte *pe = self_pte(cxt, i);
+ rc = get_disk_ranges(cxt, logical, &first, &last);
+ if (rc)
+ return rc;
- assert(pe);
- if (start < pe->offset && limit >= pe->offset)
- limit = pe->offset - 1;
- if (start < first[i] && limit >= first[i])
- limit = first[i] - 1;
- }
+ return find_last_free_sector_in_range(cxt, logical, first, last, result);
+}
- DBG(LABEL, ul_debug("DOS: unused sector for #%zu is %ju",
- n, (uintmax_t) limit));
- return limit;
+static int find_first_free_sector(struct fdisk_context *cxt,
+ int logical,
+ fdisk_sector_t start,
+ fdisk_sector_t *result)
+{
+ fdisk_sector_t first, last;
+ int rc;
+
+ rc = get_disk_ranges(cxt, logical, &first, &last);
+ if (rc)
+ return rc;
+
+ return find_first_free_sector_in_range(cxt, logical, start, last, result);
}
static int add_partition(struct fdisk_context *cxt, size_t n,
struct fdisk_partition *pa)
{
- int sys, read = 0, rc, isrel = 0;
- size_t i;
+ int sys, read = 0, rc, isrel = 0, is_logical;
struct fdisk_dos_label *l = self_label(cxt);
struct dos_partition *p = self_partition(cxt, n);
struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
struct fdisk_ask *ask = NULL;
- fdisk_sector_t start, stop = 0, limit, temp,
- first[cxt->label->nparts_max],
- last[cxt->label->nparts_max];
+ fdisk_sector_t start, stop = 0, limit, temp;
DBG(LABEL, ul_debug("DOS: adding partition %zu", n));
sys = pa && pa->type ? pa->type->code : MBR_LINUX_DATA_PARTITION;
+ is_logical = n >= 4;
if (p && is_used_partition(p)) {
fdisk_warnx(cxt, _("Partition %zu is already defined. "
@@ -1087,10 +1132,14 @@ static int add_partition(struct fdisk_context *cxt, size_t n,
n + 1);
return -EINVAL;
}
- fill_bounds(cxt, first, last);
- limit = get_possible_last(cxt, n);
- if (n < 4) {
+ rc = find_last_free_sector(cxt, is_logical, &limit);
+ if (rc == -ENOSPC)
+ fdisk_warnx(cxt, _("No free sectors available."));
+ if (rc)
+ return rc;
+
+ if (!is_logical) {
if (cxt->parent && fdisk_is_label(cxt->parent, GPT))
start = 1; /* Bad boy modifies hybrid MBR */
else {
@@ -1101,12 +1150,6 @@ static int add_partition(struct fdisk_context *cxt, size_t n,
start = cxt->first_lba;
}
-
- if (l->ext_offset) {
- assert(ext_pe);
- first[l->ext_index] = l->ext_offset;
- last[l->ext_index] = get_abs_partition_end(ext_pe);
- }
} else {
assert(ext_pe);
@@ -1118,12 +1161,6 @@ static int add_partition(struct fdisk_context *cxt, size_t n,
start = l->ext_offset + cxt->first_lba;
}
- if (fdisk_use_cylinders(cxt))
- for (i = 0; i < cxt->label->nparts_max; i++) {
- first[i] = (fdisk_cround(cxt, first[i]) - 1)
- * fdisk_get_units_per_sector(cxt);
- }
-
/*
* Ask for first sector
*/
@@ -1131,7 +1168,13 @@ static int add_partition(struct fdisk_context *cxt, size_t n,
fdisk_sector_t dflt, aligned;
temp = start;
- dflt = start = get_unused_start(cxt, n, start, first, last);
+
+ rc = find_first_free_sector(cxt, is_logical, start, &dflt);
+ if (rc == -ENOSPC)
+ fdisk_warnx(cxt, _("No free sectors available."));
+ if (rc)
+ return rc;
+ start = dflt;
if (n >= 4 && pa && fdisk_partition_has_start(pa) && cxt->script
&& cxt->first_lba > 1
@@ -1143,7 +1186,7 @@ static int add_partition(struct fdisk_context *cxt, size_t n,
/* the default sector should be aligned and unused */
do {
aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
- dflt = get_unused_start(cxt, n, aligned, first, last);
+ find_first_free_sector(cxt, is_logical, aligned, &dflt);
} while (dflt != aligned && dflt > aligned && dflt < limit);
if (dflt >= limit)
@@ -1188,12 +1231,12 @@ static int add_partition(struct fdisk_context *cxt, size_t n,
}
}
- limit = get_unused_last(cxt, n, start, first);
-
- if (start > limit) {
+ rc = find_last_free_sector_in_range(cxt, is_logical, start, limit, &stop);
+ if (rc == -ENOSPC)
fdisk_warnx(cxt, _("No free sectors available."));
- return -ENOSPC;
- }
+ if (rc)
+ return rc;
+ limit = stop;
/*
* Ask for last sector
@@ -1472,6 +1515,26 @@ static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p
}
}
+static void fill_bounds(struct fdisk_context *cxt,
+ fdisk_sector_t *first, fdisk_sector_t *last)
+{
+ size_t i;
+ struct pte *pe = self_pte(cxt, 0);
+ struct dos_partition *p;
+
+ assert(pe);
+ for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
+ p = pe->pt_entry;
+ if (is_cleared_partition(p) || IS_EXTENDED (p->sys_ind)) {
+ first[i] = SIZE_MAX;
+ last[i] = 0;
+ } else {
+ first[i] = get_abs_partition_start(pe);
+ last[i] = get_abs_partition_end(pe);
+ }
+ }
+}
+
static int dos_verify_disklabel(struct fdisk_context *cxt)
{
size_t i, j;