1
2 #ifdef HAVE_LIBBLKID
3 #include <blkid.h>
4 #endif
5 #include "blkdev.h"
6
7 #include "fdiskP.h"
8
9 /**
10 * SECTION: alignment
11 * @title: Alignment
12 * @short_description: functions to align partitions and work with disk topology and geometry
13 *
14 * The libfdisk aligns the end of the partitions to make it possible to align
15 * the next partition to the "grain" (see fdisk_get_grain_size()). The grain is
16 * usually 1MiB (or more for devices where optimal I/O is greater than 1MiB).
17 *
18 * It means that the library does not align strictly to physical sector size
19 * (or minimal or optimal I/O), but it uses greater granularity. It makes
20 * partition tables more portable. If you copy disk layout from 512-sector to
21 * 4K-sector device, all partitions are still aligned to physical sectors.
22 *
23 * This unified concept also makes partition tables more user friendly, all
24 * tables look same, LBA of the first partition is 2048 sectors everywhere, etc.
25 *
26 * It's recommended to not change any alignment or device properties. All is
27 * initialized by default by fdisk_assign_device().
28 *
29 * Note that terminology used by libfdisk is:
30 * - device properties: I/O limits (topology), geometry, sector size, ...
31 * - alignment: first, last LBA, grain, ...
32 *
33 * The alignment setting may be modified by disk label driver.
34 */
35
36 /*
37 * Alignment according to logical granularity (usually 1MiB)
38 */
39 static int lba_is_aligned(struct fdisk_context *cxt, uintmax_t lba)
40 {
41 unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
42 uintmax_t offset;
43
44 if (cxt->grain > granularity)
45 granularity = cxt->grain;
46
47 offset = (lba * cxt->sector_size) % granularity;
48
49 return !((granularity + cxt->alignment_offset - offset) % granularity);
50 }
51
52 /*
53 * Alignment according to physical device topology (usually minimal i/o size)
54 */
55 static int lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
56 {
57 unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
58 uintmax_t offset = (lba * cxt->sector_size) % granularity;
59
60 return !((granularity + cxt->alignment_offset - offset) % granularity);
61 }
62
63 /**
64 * fdisk_align_lba:
65 * @cxt: context
66 * @lba: address to align
67 * @direction: FDISK_ALIGN_{UP,DOWN,NEAREST}
68 *
69 * This function aligns @lba to the "grain" (see fdisk_get_grain_size()). If the
70 * device uses alignment offset then the result is moved according the offset
71 * to be on the physical boundary.
72 *
73 * Returns: alignment LBA.
74 */
75 fdisk_sector_t fdisk_align_lba(struct fdisk_context *cxt, fdisk_sector_t lba, int direction)
76 {
77 fdisk_sector_t res;
78
79 if (lba_is_aligned(cxt, lba))
80 res = lba;
81 else {
82 fdisk_sector_t sects_in_phy = cxt->grain / cxt->sector_size;
83
84 if (lba < cxt->first_lba)
85 res = cxt->first_lba;
86
87 else if (direction == FDISK_ALIGN_UP)
88 res = ((lba + sects_in_phy) / sects_in_phy) * sects_in_phy;
89
90 else if (direction == FDISK_ALIGN_DOWN)
91 res = (lba / sects_in_phy) * sects_in_phy;
92
93 else /* FDISK_ALIGN_NEAREST */
94 res = ((lba + sects_in_phy / 2) / sects_in_phy) * sects_in_phy;
95
96 if (cxt->alignment_offset && !lba_is_aligned(cxt, res) &&
97 res > cxt->alignment_offset / cxt->sector_size) {
98 /*
99 * apply alignment_offset
100 *
101 * On disk with alignment compensation physical blocks starts
102 * at LBA < 0 (usually LBA -1). It means we have to move LBA
103 * according the offset to be on the physical boundary.
104 */
105 /* fprintf(stderr, "LBA: %llu apply alignment_offset\n", res); */
106 res -= (max(cxt->phy_sector_size, cxt->min_io_size) -
107 cxt->alignment_offset) / cxt->sector_size;
108
109 if (direction == FDISK_ALIGN_UP && res < lba)
110 res += sects_in_phy;
111 }
112 }
113 /*
114 if (lba != res)
115 DBG(CXT, ul_debugobj(cxt, "LBA %12ju aligned-%s %12ju [grain=%lus]",
116 (uintmax_t) lba,
117 direction == FDISK_ALIGN_UP ? "up " :
118 direction == FDISK_ALIGN_DOWN ? "down" : "near",
119 (uintmax_t) res,
120 cxt->grain / cxt->sector_size));
121 else
122 DBG(CXT, ul_debugobj(cxt, "LBA %12ju already aligned", (uintmax_t)lba));
123 */
124 return res;
125 }
126
127 /**
128 * fdisk_align_lba_in_range:
129 * @cxt: context
130 * @lba: LBA
131 * @start: range start
132 * @stop: range stop
133 *
134 * Align @lba, the result has to be between @start and @stop
135 *
136 * Returns: aligned LBA
137 */
138 fdisk_sector_t fdisk_align_lba_in_range(struct fdisk_context *cxt,
139 fdisk_sector_t lba, fdisk_sector_t start, fdisk_sector_t stop)
140 {
141 fdisk_sector_t res;
142
143 /*DBG(CXT, ul_debugobj(cxt, "LBA: align in range <%ju..%ju>", (uintmax_t) start, (uintmax_t) stop));*/
144
145 if (start + (cxt->grain / cxt->sector_size) <= stop) {
146 start = fdisk_align_lba(cxt, start, FDISK_ALIGN_UP);
147 stop = fdisk_align_lba(cxt, stop, FDISK_ALIGN_DOWN);
148 }
149
150 if (start + (cxt->grain / cxt->sector_size) > stop) {
151 DBG(CXT, ul_debugobj(cxt, "LBA: area smaller than grain, don't align"));
152 res = lba;
153 goto done;
154 }
155
156 lba = fdisk_align_lba(cxt, lba, FDISK_ALIGN_NEAREST);
157
158 if (lba < start)
159 res = start;
160 else if (lba > stop)
161 res = stop;
162 else
163 res = lba;
164 done:
165 DBG(CXT, ul_debugobj(cxt, "%ju in range <%ju..%ju> aligned to %ju",
166 (uintmax_t) lba,
167 (uintmax_t) start,
168 (uintmax_t) stop,
169 (uintmax_t) res));
170 return res;
171 }
172
173 /**
174 * fdisk_lba_is_phy_aligned:
175 * @cxt: context
176 * @lba: LBA to check
177 *
178 * Check if the @lba is aligned to physical sector boundary.
179 *
180 * Returns: 1 if aligned.
181 */
182 int fdisk_lba_is_phy_aligned(struct fdisk_context *cxt, fdisk_sector_t lba)
183 {
184 return lba_is_phy_aligned(cxt, lba);
185 }
186
187 static unsigned long get_sector_size(struct fdisk_context *cxt)
188 {
189 int sect_sz;
190
191 if (!fdisk_is_regfile(cxt) &&
192 !blkdev_get_sector_size(cxt->dev_fd, §_sz))
193 return (unsigned long) sect_sz;
194
195 return DEFAULT_SECTOR_SIZE;
196 }
197
198 static void recount_geometry(struct fdisk_context *cxt)
199 {
200 if (!cxt->geom.heads)
201 cxt->geom.heads = 255;
202 if (!cxt->geom.sectors)
203 cxt->geom.sectors = 63;
204
205 cxt->geom.cylinders = cxt->total_sectors /
206 (cxt->geom.heads * cxt->geom.sectors);
207 }
208
209 /**
210 * fdisk_override_geometry:
211 * @cxt: fdisk context
212 * @cylinders: user specified cylinders
213 * @heads: user specified heads
214 * @sectors: user specified sectors
215 *
216 * Overrides auto-discovery. The function fdisk_reset_device_properties()
217 * restores the original setting.
218 *
219 * The difference between fdisk_override_geometry() and fdisk_save_user_geometry()
220 * is that saved user geometry is persistent setting and it's applied always
221 * when device is assigned to the context or device properties are reset.
222 *
223 * Returns: 0 on success, < 0 on error.
224 */
225 int fdisk_override_geometry(struct fdisk_context *cxt,
226 unsigned int cylinders,
227 unsigned int heads,
228 unsigned int sectors)
229 {
230 if (!cxt)
231 return -EINVAL;
232 if (heads)
233 cxt->geom.heads = heads;
234 if (sectors)
235 cxt->geom.sectors = sectors;
236
237 if (cylinders)
238 cxt->geom.cylinders = cylinders;
239 else
240 recount_geometry(cxt);
241
242 fdisk_reset_alignment(cxt);
243
244 DBG(CXT, ul_debugobj(cxt, "override C/H/S: %u/%u/%u",
245 (unsigned) cxt->geom.cylinders,
246 (unsigned) cxt->geom.heads,
247 (unsigned) cxt->geom.sectors));
248
249 return 0;
250 }
251
252 /**
253 * fdisk_save_user_geometry:
254 * @cxt: context
255 * @cylinders: C
256 * @heads: H
257 * @sectors: S
258 *
259 * Save user defined geometry to use it for partitioning.
260 *
261 * The user properties are applied by fdisk_assign_device() or
262 * fdisk_reset_device_properties().
263
264 * Returns: <0 on error, 0 on success.
265 */
266 int fdisk_save_user_geometry(struct fdisk_context *cxt,
267 unsigned int cylinders,
268 unsigned int heads,
269 unsigned int sectors)
270 {
271 if (!cxt)
272 return -EINVAL;
273
274 if (heads)
275 cxt->user_geom.heads = heads > 256 ? 0 : heads;
276 if (sectors)
277 cxt->user_geom.sectors = sectors >= 64 ? 0 : sectors;
278 if (cylinders)
279 cxt->user_geom.cylinders = cylinders;
280
281 DBG(CXT, ul_debugobj(cxt, "user C/H/S: %u/%u/%u",
282 (unsigned) cxt->user_geom.cylinders,
283 (unsigned) cxt->user_geom.heads,
284 (unsigned) cxt->user_geom.sectors));
285
286 return 0;
287 }
288
289 /**
290 * fdisk_save_user_sector_size:
291 * @cxt: context
292 * @phy: physical sector size
293 * @log: logical sector size
294 *
295 * Save user defined sector sizes to use it for partitioning.
296 *
297 * The user properties are applied by fdisk_assign_device() or
298 * fdisk_reset_device_properties().
299 *
300 * Returns: <0 on error, 0 on success.
301 */
302 int fdisk_save_user_sector_size(struct fdisk_context *cxt,
303 unsigned int phy,
304 unsigned int log)
305 {
306 if (!cxt)
307 return -EINVAL;
308
309 DBG(CXT, ul_debugobj(cxt, "user phy/log sector size: %u/%u", phy, log));
310
311 cxt->user_pyh_sector = phy;
312 cxt->user_log_sector = log;
313
314 return 0;
315 }
316
317 /**
318 * fdisk_save_user_grain:
319 * @cxt: context
320 * @grain: size in bytes (>= 512, multiple of 512)
321 *
322 * Save user define grain size. The size is used to align partitions.
323 *
324 * The default is 1MiB (or optimal I/O size if greater than 1MiB). It's strongly
325 * recommended to use the default.
326 *
327 * The smallest possible granularity for partitioning is physical sector size
328 * (or minimal I/O size; the bigger number win). If the user's @grain size is
329 * too small then the smallest possible granularity is used. It means
330 * fdisk_save_user_grain(cxt, 512) forces libfdisk to use grain as small as
331 * possible.
332 *
333 * The setting is applied by fdisk_assign_device() or
334 * fdisk_reset_device_properties().
335 *
336 * Returns: <0 on error, 0 on success.
337 */
338 int fdisk_save_user_grain(struct fdisk_context *cxt, unsigned long grain)
339 {
340 if (!cxt || grain % 512)
341 return -EINVAL;
342
343 DBG(CXT, ul_debugobj(cxt, "user grain size: %lu", grain));
344 cxt->user_grain = grain;
345 return 0;
346 }
347
348 /**
349 * fdisk_has_user_device_properties:
350 * @cxt: context
351 *
352 * Returns: 1 if user specified any properties
353 */
354 int fdisk_has_user_device_properties(struct fdisk_context *cxt)
355 {
356 return (cxt->user_pyh_sector || cxt->user_log_sector ||
357 cxt->user_grain ||
358 fdisk_has_user_device_geometry(cxt));
359 }
360
361 int fdisk_has_user_device_geometry(struct fdisk_context *cxt)
362 {
363 return (cxt->user_geom.heads || cxt->user_geom.sectors || cxt->user_geom.cylinders);
364 }
365
366 int fdisk_apply_user_device_properties(struct fdisk_context *cxt)
367 {
368 if (!cxt)
369 return -EINVAL;
370
371 DBG(CXT, ul_debugobj(cxt, "applying user device properties"));
372
373 if (cxt->user_pyh_sector)
374 cxt->phy_sector_size = cxt->user_pyh_sector;
375 if (cxt->user_log_sector) {
376 uint64_t old_total = cxt->total_sectors;
377 uint64_t old_secsz = cxt->sector_size;
378
379 cxt->sector_size = cxt->min_io_size =
380 cxt->io_size = cxt->user_log_sector;
381
382 if (cxt->sector_size != old_secsz) {
383 cxt->total_sectors = (old_total * (old_secsz/512)) / (cxt->sector_size >> 9);
384 DBG(CXT, ul_debugobj(cxt, "new total sectors: %ju", (uintmax_t)cxt->total_sectors));
385 }
386 }
387
388 if (cxt->user_geom.heads)
389 cxt->geom.heads = cxt->user_geom.heads;
390 if (cxt->user_geom.sectors)
391 cxt->geom.sectors = cxt->user_geom.sectors;
392
393 if (cxt->user_geom.cylinders)
394 cxt->geom.cylinders = cxt->user_geom.cylinders;
395 else if (cxt->user_geom.heads || cxt->user_geom.sectors)
396 recount_geometry(cxt);
397
398 fdisk_reset_alignment(cxt);
399
400 if (cxt->user_grain) {
401 unsigned long granularity = max(cxt->phy_sector_size, cxt->min_io_size);
402
403 cxt->grain = cxt->user_grain < granularity ? granularity : cxt->user_grain;
404 DBG(CXT, ul_debugobj(cxt, "new grain: %lu", cxt->grain));
405 }
406
407 if (cxt->firstsector_bufsz != cxt->sector_size)
408 fdisk_read_firstsector(cxt);
409
410 DBG(CXT, ul_debugobj(cxt, "new C/H/S: %u/%u/%u",
411 (unsigned) cxt->geom.cylinders,
412 (unsigned) cxt->geom.heads,
413 (unsigned) cxt->geom.sectors));
414 DBG(CXT, ul_debugobj(cxt, "new log/phy sector size: %u/%u",
415 (unsigned) cxt->sector_size,
416 (unsigned) cxt->phy_sector_size));
417
418 return 0;
419 }
420
421 void fdisk_zeroize_device_properties(struct fdisk_context *cxt)
422 {
423 assert(cxt);
424
425 cxt->io_size = 0;
426 cxt->optimal_io_size = 0;
427 cxt->min_io_size = 0;
428 cxt->phy_sector_size = 0;
429 cxt->sector_size = 0;
430 cxt->alignment_offset = 0;
431 cxt->grain = 0;
432 cxt->first_lba = 0;
433 cxt->last_lba = 0;
434 cxt->total_sectors = 0;
435
436 memset(&cxt->geom, 0, sizeof(struct fdisk_geometry));
437 }
438
439 /**
440 * fdisk_reset_device_properties:
441 * @cxt: context
442 *
443 * Resets and discovery topology (I/O limits), geometry, re-read the first
444 * rector on the device if necessary and apply user device setting (geometry
445 * and sector size), then initialize alignment according to label driver (see
446 * fdisk_reset_alignment()).
447 *
448 * You don't have to use this function by default, fdisk_assign_device() is
449 * smart enough to initialize all necessary setting.
450 *
451 * Returns: 0 on success, <0 on error.
452 */
453 int fdisk_reset_device_properties(struct fdisk_context *cxt)
454 {
455 int rc;
456
457 if (!cxt)
458 return -EINVAL;
459
460 DBG(CXT, ul_debugobj(cxt, "*** resetting device properties"));
461
462 fdisk_zeroize_device_properties(cxt);
463 fdisk_discover_topology(cxt);
464 fdisk_discover_geometry(cxt);
465
466 rc = fdisk_read_firstsector(cxt);
467 if (rc)
468 return rc;
469
470 fdisk_apply_user_device_properties(cxt);
471 return 0;
472 }
473
474 /*
475 * Generic (label independent) geometry
476 */
477 int fdisk_discover_geometry(struct fdisk_context *cxt)
478 {
479 fdisk_sector_t nsects = 0;
480 unsigned int h = 0, s = 0;
481
482 assert(cxt);
483 assert(cxt->geom.heads == 0);
484
485 DBG(CXT, ul_debugobj(cxt, "%s: discovering geometry...", cxt->dev_path));
486
487 if (fdisk_is_regfile(cxt))
488 cxt->total_sectors = cxt->dev_st.st_size / cxt->sector_size;
489 else {
490 /* get number of 512-byte sectors, and convert it the real sectors */
491 if (!blkdev_get_sectors(cxt->dev_fd, (unsigned long long *) &nsects))
492 cxt->total_sectors = (nsects / (cxt->sector_size >> 9));
493
494 /* what the kernel/bios thinks the geometry is */
495 blkdev_get_geometry(cxt->dev_fd, &h, &s);
496 }
497
498 DBG(CXT, ul_debugobj(cxt, "total sectors: %ju (ioctl=%ju)",
499 (uintmax_t) cxt->total_sectors,
500 (uintmax_t) nsects));
501
502 cxt->geom.cylinders = 0;
503 cxt->geom.heads = h;
504 cxt->geom.sectors = s;
505
506 /* obtained heads and sectors */
507 recount_geometry(cxt);
508
509 DBG(CXT, ul_debugobj(cxt, "result: C/H/S: %u/%u/%u",
510 (unsigned) cxt->geom.cylinders,
511 (unsigned) cxt->geom.heads,
512 (unsigned) cxt->geom.sectors));
513 return 0;
514 }
515
516 int fdisk_discover_topology(struct fdisk_context *cxt)
517 {
518 #ifdef HAVE_LIBBLKID
519 blkid_probe pr;
520 #endif
521 assert(cxt);
522 assert(cxt->sector_size == 0);
523
524 DBG(CXT, ul_debugobj(cxt, "%s: discovering topology...", cxt->dev_path));
525 #ifdef HAVE_LIBBLKID
526 DBG(CXT, ul_debugobj(cxt, "initialize libblkid prober"));
527
528 pr = blkid_new_probe();
529 if (pr && blkid_probe_set_device(pr, cxt->dev_fd, 0, 0) == 0) {
530 blkid_topology tp = blkid_probe_get_topology(pr);
531
532 if (tp) {
533 cxt->min_io_size = blkid_topology_get_minimum_io_size(tp);
534 cxt->optimal_io_size = blkid_topology_get_optimal_io_size(tp);
535 cxt->phy_sector_size = blkid_topology_get_physical_sector_size(tp);
536 cxt->alignment_offset = blkid_topology_get_alignment_offset(tp);
537
538 /* I/O size used by fdisk */
539 cxt->io_size = cxt->optimal_io_size;
540 if (!cxt->io_size)
541 /* optimal I/O is optional, default to minimum IO */
542 cxt->io_size = cxt->min_io_size;
543
544 if (cxt->io_size && cxt->phy_sector_size) {
545 if (cxt->io_size == 33553920) {
546 /* 33553920 (32 MiB - 512) is always a controller error */
547 DBG(CXT, ul_debugobj(cxt, "ignore bad I/O size 33553920"));
548 cxt->io_size = cxt->phy_sector_size;
549 } else if ((cxt->io_size % cxt->phy_sector_size) != 0) {
550 /* ignore optimal I/O if not aligned to phy.sector size */
551 DBG(CXT, ul_debugobj(cxt, "ignore misaligned I/O size"));
552 cxt->io_size = cxt->phy_sector_size;
553 }
554 }
555
556 }
557 }
558 blkid_free_probe(pr);
559 #endif
560
561 cxt->sector_size = get_sector_size(cxt);
562 if (!cxt->phy_sector_size) /* could not discover physical size */
563 cxt->phy_sector_size = cxt->sector_size;
564
565 /* no blkid or error, use default values */
566 if (!cxt->min_io_size)
567 cxt->min_io_size = cxt->sector_size;
568 if (!cxt->io_size)
569 cxt->io_size = cxt->sector_size;
570
571 DBG(CXT, ul_debugobj(cxt, "result: log/phy sector size: %ld/%ld",
572 cxt->sector_size, cxt->phy_sector_size));
573 DBG(CXT, ul_debugobj(cxt, "result: fdisk/optimal/minimal io: %ld/%ld/%ld",
574 cxt->io_size, cxt->optimal_io_size, cxt->min_io_size));
575 return 0;
576 }
577
578 static int has_topology(struct fdisk_context *cxt)
579 {
580 /*
581 * Assume that the device provides topology info if
582 * optimal_io_size is set or alignment_offset is set or
583 * minimum_io_size is not power of 2.
584 */
585 if (cxt &&
586 (cxt->optimal_io_size ||
587 cxt->alignment_offset ||
588 !is_power_of_2(cxt->min_io_size)))
589 return 1;
590 return 0;
591 }
592
593 /*
594 * The LBA of the first partition is based on the device geometry and topology.
595 * This offset is generic (and recommended) for all labels.
596 *
597 * Returns: 0 on error or number of logical sectors.
598 */
599 static fdisk_sector_t topology_get_first_lba(struct fdisk_context *cxt)
600 {
601 fdisk_sector_t x = 0, res;
602
603 if (!cxt)
604 return 0;
605
606 if (!cxt->io_size)
607 fdisk_discover_topology(cxt);
608
609 /*
610 * Align the begin of partitions to:
611 *
612 * a) topology
613 * a2) alignment offset
614 * a1) or physical sector (minimal_io_size, aka "grain")
615 *
616 * b) or default to 1MiB (2048 sectors, Windows Vista default)
617 *
618 * c) or for very small devices use 1 phy.sector
619 */
620 if (has_topology(cxt)) {
621 if (cxt->alignment_offset)
622 x = cxt->alignment_offset;
623 else if (cxt->io_size > 2048 * 512)
624 x = cxt->io_size;
625 }
626 /* default to 1MiB */
627 if (!x)
628 x = 2048 * 512;
629
630 res = x / cxt->sector_size;
631
632 /* don't use huge offset on small devices */
633 if (cxt->total_sectors <= res * 4)
634 res = cxt->phy_sector_size / cxt->sector_size;
635
636 return res;
637 }
638
639 static unsigned long topology_get_grain(struct fdisk_context *cxt)
640 {
641 unsigned long res;
642
643 if (!cxt)
644 return 0;
645
646 if (!cxt->io_size)
647 fdisk_discover_topology(cxt);
648
649 res = cxt->io_size;
650
651 /* use 1MiB grain always when possible */
652 if (res < 2048 * 512)
653 res = 2048 * 512;
654
655 /* don't use huge grain on small devices */
656 if (cxt->total_sectors <= (res * 4 / cxt->sector_size))
657 res = cxt->phy_sector_size;
658
659 return res;
660 }
661
662 /* apply label alignment setting to the context -- if not sure use
663 * fdisk_reset_alignment()
664 */
665 int fdisk_apply_label_device_properties(struct fdisk_context *cxt)
666 {
667 int rc = 0;
668
669 if (cxt->label && cxt->label->op->reset_alignment) {
670 DBG(CXT, ul_debugobj(cxt, "applying label device properties..."));
671 rc = cxt->label->op->reset_alignment(cxt);
672 }
673 return rc;
674 }
675
676 /**
677 * fdisk_reset_alignment:
678 * @cxt: fdisk context
679 *
680 * Resets alignment setting to the default and label specific values. This
681 * function does not change device properties (I/O limits, geometry etc.).
682 *
683 * Returns: 0 on success, < 0 in case of error.
684 */
685 int fdisk_reset_alignment(struct fdisk_context *cxt)
686 {
687 int rc = 0;
688
689 if (!cxt)
690 return -EINVAL;
691
692 DBG(CXT, ul_debugobj(cxt, "resetting alignment..."));
693
694 /* default */
695 cxt->grain = topology_get_grain(cxt);
696 cxt->first_lba = topology_get_first_lba(cxt);
697 cxt->last_lba = cxt->total_sectors - 1;
698
699 /* overwrite default by label stuff */
700 rc = fdisk_apply_label_device_properties(cxt);
701
702 DBG(CXT, ul_debugobj(cxt, "alignment reset to: "
703 "first LBA=%ju, last LBA=%ju, grain=%lu [rc=%d]",
704 (uintmax_t) cxt->first_lba, (uintmax_t) cxt->last_lba,
705 cxt->grain, rc));
706 return rc;
707 }
708
709
710 fdisk_sector_t fdisk_scround(struct fdisk_context *cxt, fdisk_sector_t num)
711 {
712 fdisk_sector_t un = fdisk_get_units_per_sector(cxt);
713 return (num + un - 1) / un;
714 }
715
716 fdisk_sector_t fdisk_cround(struct fdisk_context *cxt, fdisk_sector_t num)
717 {
718 return fdisk_use_cylinders(cxt) ?
719 (num / fdisk_get_units_per_sector(cxt)) + 1 : num;
720 }
721