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 */
lba_is_aligned(struct fdisk_context * cxt,uintmax_t lba)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 */
lba_is_phy_aligned(struct fdisk_context * cxt,fdisk_sector_t lba)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 */
fdisk_align_lba(struct fdisk_context * cxt,fdisk_sector_t lba,int direction)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 */
fdisk_align_lba_in_range(struct fdisk_context * cxt,fdisk_sector_t lba,fdisk_sector_t start,fdisk_sector_t stop)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 */
fdisk_lba_is_phy_aligned(struct fdisk_context * cxt,fdisk_sector_t lba)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
get_sector_size(struct fdisk_context * cxt)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
recount_geometry(struct fdisk_context * cxt)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 */
fdisk_override_geometry(struct fdisk_context * cxt,unsigned int cylinders,unsigned int heads,unsigned int sectors)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 */
fdisk_save_user_geometry(struct fdisk_context * cxt,unsigned int cylinders,unsigned int heads,unsigned int sectors)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 */
fdisk_save_user_sector_size(struct fdisk_context * cxt,unsigned int phy,unsigned int log)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 */
fdisk_save_user_grain(struct fdisk_context * cxt,unsigned long grain)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 */
fdisk_has_user_device_properties(struct fdisk_context * cxt)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
fdisk_has_user_device_geometry(struct fdisk_context * cxt)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
fdisk_apply_user_device_properties(struct fdisk_context * cxt)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
fdisk_zeroize_device_properties(struct fdisk_context * cxt)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 */
fdisk_reset_device_properties(struct fdisk_context * cxt)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 */
fdisk_discover_geometry(struct fdisk_context * cxt)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
fdisk_discover_topology(struct fdisk_context * cxt)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 /* ignore optimal I/O if not aligned to phy.sector size */
545 if (cxt->io_size
546 && cxt->phy_sector_size
547 && (cxt->io_size % cxt->phy_sector_size) != 0) {
548 DBG(CXT, ul_debugobj(cxt, "ignore misaligned I/O size"));
549 cxt->io_size = cxt->phy_sector_size;
550 }
551
552 }
553 }
554 blkid_free_probe(pr);
555 #endif
556
557 cxt->sector_size = get_sector_size(cxt);
558 if (!cxt->phy_sector_size) /* could not discover physical size */
559 cxt->phy_sector_size = cxt->sector_size;
560
561 /* no blkid or error, use default values */
562 if (!cxt->min_io_size)
563 cxt->min_io_size = cxt->sector_size;
564 if (!cxt->io_size)
565 cxt->io_size = cxt->sector_size;
566
567 DBG(CXT, ul_debugobj(cxt, "result: log/phy sector size: %ld/%ld",
568 cxt->sector_size, cxt->phy_sector_size));
569 DBG(CXT, ul_debugobj(cxt, "result: fdisk/optimal/minimal io: %ld/%ld/%ld",
570 cxt->io_size, cxt->optimal_io_size, cxt->min_io_size));
571 return 0;
572 }
573
has_topology(struct fdisk_context * cxt)574 static int has_topology(struct fdisk_context *cxt)
575 {
576 /*
577 * Assume that the device provides topology info if
578 * optimal_io_size is set or alignment_offset is set or
579 * minimum_io_size is not power of 2.
580 */
581 if (cxt &&
582 (cxt->optimal_io_size ||
583 cxt->alignment_offset ||
584 !is_power_of_2(cxt->min_io_size)))
585 return 1;
586 return 0;
587 }
588
589 /*
590 * The LBA of the first partition is based on the device geometry and topology.
591 * This offset is generic (and recommended) for all labels.
592 *
593 * Returns: 0 on error or number of logical sectors.
594 */
topology_get_first_lba(struct fdisk_context * cxt)595 static fdisk_sector_t topology_get_first_lba(struct fdisk_context *cxt)
596 {
597 fdisk_sector_t x = 0, res;
598
599 if (!cxt)
600 return 0;
601
602 if (!cxt->io_size)
603 fdisk_discover_topology(cxt);
604
605 /*
606 * Align the begin of partitions to:
607 *
608 * a) topology
609 * a2) alignment offset
610 * a1) or physical sector (minimal_io_size, aka "grain")
611 *
612 * b) or default to 1MiB (2048 sectors, Windows Vista default)
613 *
614 * c) or for very small devices use 1 phy.sector
615 */
616 if (has_topology(cxt)) {
617 if (cxt->alignment_offset)
618 x = cxt->alignment_offset;
619 else if (cxt->io_size > 2048 * 512)
620 x = cxt->io_size;
621 }
622 /* default to 1MiB */
623 if (!x)
624 x = 2048 * 512;
625
626 res = x / cxt->sector_size;
627
628 /* don't use huge offset on small devices */
629 if (cxt->total_sectors <= res * 4)
630 res = cxt->phy_sector_size / cxt->sector_size;
631
632 return res;
633 }
634
topology_get_grain(struct fdisk_context * cxt)635 static unsigned long topology_get_grain(struct fdisk_context *cxt)
636 {
637 unsigned long res;
638
639 if (!cxt)
640 return 0;
641
642 if (!cxt->io_size)
643 fdisk_discover_topology(cxt);
644
645 res = cxt->io_size;
646
647 /* use 1MiB grain always when possible */
648 if (res < 2048 * 512)
649 res = 2048 * 512;
650
651 /* don't use huge grain on small devices */
652 if (cxt->total_sectors <= (res * 4 / cxt->sector_size))
653 res = cxt->phy_sector_size;
654
655 return res;
656 }
657
658 /* apply label alignment setting to the context -- if not sure use
659 * fdisk_reset_alignment()
660 */
fdisk_apply_label_device_properties(struct fdisk_context * cxt)661 int fdisk_apply_label_device_properties(struct fdisk_context *cxt)
662 {
663 int rc = 0;
664
665 if (cxt->label && cxt->label->op->reset_alignment) {
666 DBG(CXT, ul_debugobj(cxt, "applying label device properties..."));
667 rc = cxt->label->op->reset_alignment(cxt);
668 }
669 return rc;
670 }
671
672 /**
673 * fdisk_reset_alignment:
674 * @cxt: fdisk context
675 *
676 * Resets alignment setting to the default and label specific values. This
677 * function does not change device properties (I/O limits, geometry etc.).
678 *
679 * Returns: 0 on success, < 0 in case of error.
680 */
fdisk_reset_alignment(struct fdisk_context * cxt)681 int fdisk_reset_alignment(struct fdisk_context *cxt)
682 {
683 int rc = 0;
684
685 if (!cxt)
686 return -EINVAL;
687
688 DBG(CXT, ul_debugobj(cxt, "resetting alignment..."));
689
690 /* default */
691 cxt->grain = topology_get_grain(cxt);
692 cxt->first_lba = topology_get_first_lba(cxt);
693 cxt->last_lba = cxt->total_sectors - 1;
694
695 /* overwrite default by label stuff */
696 rc = fdisk_apply_label_device_properties(cxt);
697
698 DBG(CXT, ul_debugobj(cxt, "alignment reset to: "
699 "first LBA=%ju, last LBA=%ju, grain=%lu [rc=%d]",
700 (uintmax_t) cxt->first_lba, (uintmax_t) cxt->last_lba,
701 cxt->grain, rc));
702 return rc;
703 }
704
705
fdisk_scround(struct fdisk_context * cxt,fdisk_sector_t num)706 fdisk_sector_t fdisk_scround(struct fdisk_context *cxt, fdisk_sector_t num)
707 {
708 fdisk_sector_t un = fdisk_get_units_per_sector(cxt);
709 return (num + un - 1) / un;
710 }
711
fdisk_cround(struct fdisk_context * cxt,fdisk_sector_t num)712 fdisk_sector_t fdisk_cround(struct fdisk_context *cxt, fdisk_sector_t num)
713 {
714 return fdisk_use_cylinders(cxt) ?
715 (num / fdisk_get_units_per_sector(cxt)) + 1 : num;
716 }
717
718