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, &sect_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