OSDN Git Service

libparted: don't allow values less than 1 (#665496)
[android-x86/external-parted.git] / libparted / unit.c
1 /*
2     libparted - a library for manipulating disk partitions
3     Copyright (C) 2005, 2007, 2009-2011 Free Software Foundation, Inc.
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 3 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /** \file unit.c */
20
21 /**
22  * \addtogroup PedUnit
23  *
24  * \brief The PedUnit module provides a standard mechanism for describing
25  * and parsing locations within devices in human-friendly plain text.
26  *
27  * Internally, libparted uses PedSector (which is typedef'ed to be long long
28  * in <parted/device.h>) to describe device locations such as the start and
29  * end of partitions.  However, sector numbers are often long and unintuitive.
30  * For example, my extended partition starts at sector 208845.  PedUnit allows
31  * this location to be represented in more intutitive ways, including "106Mb",
32  * "0Gb" and "0%", as well as "208845s".  PedUnit aims to provide facilities
33  * to provide a consistent system for describing device locations all
34  * throughout libparted.
35  *
36  * PedUnit provides two basic services: converting a PedSector into a text
37  * representation, and parsing a text representation into a PedSector.
38  * PedUnit currently supports these units:
39  *
40  *      sectors, bytes, kilobytes, megabytes, gigabytes, terabytes, compact,
41  *      cylinder and percent.
42  *
43  * PedUnit has a global variable that contains the default unit for all
44  * conversions.
45  *
46  * @{
47  */
48
49
50
51
52 #include <config.h>
53 #include <parted/parted.h>
54 #include <parted/debug.h>
55
56 #include <ctype.h>
57 #include <stdio.h>
58 #include <float.h>
59
60 #define N_(String) String
61 #if ENABLE_NLS
62 #  include <libintl.h>
63 #  define _(String) dgettext (PACKAGE, String)
64 #else
65 #  define _(String) (String)
66 #endif /* ENABLE_NLS */
67
68
69 static PedUnit default_unit = PED_UNIT_COMPACT;
70 static const char* unit_names[] = {
71         "s",
72         "B",
73         "kB",
74         "MB",
75         "GB",
76         "TB",
77         "compact",
78         "cyl",
79         "chs",
80         "%",
81         "kiB",
82         "MiB",
83         "GiB",
84         "TiB"
85 };
86
87
88 /**
89  * \brief Set the default \p unit used by subsequent calls to the PedUnit API.
90  *
91  * In particular, this affects how locations inside error messages
92  * (exceptions) are displayed.
93  */
94 void
95 ped_unit_set_default (PedUnit unit)
96 {
97         default_unit = unit;
98 }
99
100
101 /**
102  * \brief Get the current default unit.
103  */
104 PedUnit
105 ped_unit_get_default ()
106 {
107         return default_unit;
108 }
109
110 /**
111  * Get the byte size of a given \p unit.
112  */
113 long long
114 ped_unit_get_size (const PedDevice* dev, PedUnit unit)
115 {
116         PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors;
117
118         switch (unit) {
119                 case PED_UNIT_SECTOR:   return dev->sector_size;
120                 case PED_UNIT_BYTE:     return 1;
121                 case PED_UNIT_KILOBYTE: return PED_KILOBYTE_SIZE;
122                 case PED_UNIT_MEGABYTE: return PED_MEGABYTE_SIZE;
123                 case PED_UNIT_GIGABYTE: return PED_GIGABYTE_SIZE;
124                 case PED_UNIT_TERABYTE: return PED_TERABYTE_SIZE;
125                 case PED_UNIT_KIBIBYTE: return PED_KIBIBYTE_SIZE;
126                 case PED_UNIT_MEBIBYTE: return PED_MEBIBYTE_SIZE;
127                 case PED_UNIT_GIBIBYTE: return PED_GIBIBYTE_SIZE;
128                 case PED_UNIT_TEBIBYTE: return PED_TEBIBYTE_SIZE;
129                 case PED_UNIT_CYLINDER: return cyl_size * dev->sector_size;
130                 case PED_UNIT_CHS:      return dev->sector_size;
131
132                 case PED_UNIT_PERCENT:
133                         return dev->length * dev->sector_size / 100;
134
135                 case PED_UNIT_COMPACT:
136                         ped_exception_throw (
137                                 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
138                                 _("Cannot get unit size for special unit "
139                                   "'COMPACT'."));
140                         return 0;
141         }
142
143         /* never reached */
144         PED_ASSERT(0);
145         return 0;
146 }
147
148 /**
149  * Get a textual (non-internationalized) representation of a \p unit.
150  *
151  * For example, the textual representation of PED_UNIT_SECTOR is "s".
152  */
153 const char*
154 ped_unit_get_name (PedUnit unit)
155 {
156         return unit_names[unit];
157 }
158
159 /**
160  * Get a unit based on its textual representation: \p unit_name.
161  *
162  * For example, ped_unit_get_by_name("Mb") returns PED_UNIT_MEGABYTE.
163  */
164 PedUnit
165 ped_unit_get_by_name (const char* unit_name)
166 {
167         PedUnit unit;
168         for (unit = PED_UNIT_FIRST; unit <= PED_UNIT_LAST; unit++) {
169                 if (!strcasecmp (unit_names[unit], unit_name))
170                         return unit;
171         }
172         return -1;
173 }
174
175 static char*
176 ped_strdup (const char *str)
177 {
178         char *result;
179         result = ped_malloc (strlen (str) + 1);
180         if (!result)
181                 return NULL;
182         strcpy (result, str);
183         return result;
184 }
185
186 /**
187  * \brief Get a string that describes the location of the \p byte on
188  * device \p dev.
189  *
190  * The string is described with the desired \p unit.
191  * The returned string must be freed with free().
192  */
193 char*
194 ped_unit_format_custom_byte (const PedDevice* dev, PedSector byte, PedUnit unit)
195 {
196         char buf[100];
197         PedSector sector = byte / dev->sector_size;
198         double d, w;
199         int p;
200
201         PED_ASSERT (dev != NULL);
202
203         /* CHS has a special comma-separated format. */
204         if (unit == PED_UNIT_CHS) {
205                 const PedCHSGeometry *chs = &dev->bios_geom;
206                 snprintf (buf, 100, "%lld,%lld,%lld",
207                           sector / chs->sectors / chs->heads,
208                           (sector / chs->sectors) % chs->heads,
209                           sector % chs->sectors);
210                 return ped_strdup (buf);
211         }
212
213         /* Cylinders, sectors and bytes should be rounded down... */
214         if (unit == PED_UNIT_CYLINDER
215             || unit == PED_UNIT_SECTOR
216             || unit == PED_UNIT_BYTE) {
217                 snprintf (buf, 100, "%lld%s",
218                           byte / ped_unit_get_size (dev, unit),
219                           ped_unit_get_name (unit));
220                 return ped_strdup (buf);
221         }
222
223         if (unit == PED_UNIT_COMPACT) {
224                 if (byte >= 10LL * PED_TERABYTE_SIZE)
225                         unit = PED_UNIT_TERABYTE;
226                 else if (byte >= 10LL * PED_GIGABYTE_SIZE)
227                         unit = PED_UNIT_GIGABYTE;
228                 else if (byte >= 10LL * PED_MEGABYTE_SIZE)
229                         unit = PED_UNIT_MEGABYTE;
230                 else if (byte >= 10LL * PED_KILOBYTE_SIZE)
231                         unit = PED_UNIT_KILOBYTE;
232                 else
233                         unit = PED_UNIT_BYTE;
234         }
235
236         /* IEEE754 says that 100.5 has to be rounded to 100 (by printf) */
237         /* but 101.5 has to be rounded to 102... so we multiply by 1+E. */
238         /* This just divide by 2 the natural IEEE754 extended precision */
239         /* and won't cause any trouble before 1000 TB */
240         d = ((double)byte / ped_unit_get_size (dev, unit))
241             * (1. + DBL_EPSILON);
242         w = d + ( (d < 10. ) ? 0.005 :
243                   (d < 100.) ? 0.05  :
244                                0.5  );
245         p = (w < 10. ) ? 2 :
246             (w < 100.) ? 1 :
247                          0 ;
248
249 #ifdef __BEOS__
250         snprintf (buf, 100, "%.*f%s", p, d, ped_unit_get_name(unit));
251 #else
252         snprintf (buf, 100, "%1$.*2$f%3$s", d, p, ped_unit_get_name (unit));
253 #endif
254
255         return ped_strdup (buf);
256 }
257
258 /**
259  * \brief Get a string that describes the location of the \p byte on
260  * device \p dev.
261  *
262  * The string is described with the default unit, which is set
263  * by ped_unit_set_default().
264  * The returned string must be freed with free().
265  */
266 char*
267 ped_unit_format_byte (const PedDevice* dev, PedSector byte)
268 {
269         PED_ASSERT (dev != NULL);
270         return ped_unit_format_custom_byte (dev, byte, default_unit);
271 }
272
273 /**
274  * \brief Get a string that describes the location \p sector on device \p dev.
275  *
276  * The string is described with the desired \p unit.
277  * The returned string must be freed with free().
278  */
279 char*
280 ped_unit_format_custom (const PedDevice* dev, PedSector sector, PedUnit unit)
281 {
282         PED_ASSERT (dev != NULL);
283         return ped_unit_format_custom_byte(dev, sector*dev->sector_size, unit);
284 }
285
286 /**
287  * \brief Get a string that describes the location \p sector on device \p dev.
288  *
289  * The string is described with the default unit, which is set
290  * by ped_unit_set_default().
291  * The returned string must be freed with free().
292  */
293 char*
294 ped_unit_format (const PedDevice* dev, PedSector sector)
295 {
296         PED_ASSERT (dev != NULL);
297         return ped_unit_format_custom_byte (dev, sector * dev->sector_size,
298                                             default_unit);
299 }
300
301 /**
302  * If \p str contains a valid description of a location on \p dev,
303  * then \p *sector is modified to describe the location and a geometry
304  * is created in \p *range describing a 2 units large area centered on
305  * \p *sector.  If the \p range as described here would be partially outside
306  * the device \p dev, the geometry returned is the intersection between the
307  * former and the whole device geometry.  If no units are specified, then the
308  * default unit is assumed.
309  *
310  * \return \c 1 if \p str is a valid location description, \c 0 otherwise
311  */
312 int
313 ped_unit_parse (const char* str, const PedDevice* dev, PedSector *sector,
314                 PedGeometry** range)
315 {
316         return ped_unit_parse_custom (str, dev, default_unit, sector, range);
317 }
318
319 /* Inefficiently removes all spaces from a string, in-place. */
320 static void
321 strip_string (char* str)
322 {
323         int i;
324
325         for (i = 0; str[i] != 0; i++) {
326                 if (isspace (str[i])) {
327                         int j;
328                         for (j = i + 1; str[j] != 0; j++)
329                                 str[j - 1] = str[j];
330                 }
331         }
332 }
333
334
335 /* Find non-number suffix.  Eg: find_suffix("32Mb") returns a pointer to
336  * "Mb". */
337 static char*
338 find_suffix (const char* str)
339 {
340         while (str[0] != 0 && (isdigit (str[0]) || strchr(",.-", str[0])))
341                 str++;
342         return (char *) str;
343 }
344
345 static void
346 remove_punct (char* str)
347 {
348         int i = 0;
349
350         for (i = 0; str[i]; i++) {
351                 if (ispunct (str[i]))
352                         str[i] = ' ';
353         }
354 }
355
356 static int
357 is_chs (const char* str)
358 {
359         int punct_count = 0;
360         int i = 0;
361
362         for (i = 0; str[i]; i++)
363                 punct_count += ispunct (str[i]) != 0;
364         return punct_count == 2;
365 }
366
367 static int
368 parse_chs (const char* str, const PedDevice* dev, PedSector* sector,
369                 PedGeometry** range)
370 {
371         PedSector cyl_size = dev->bios_geom.heads * dev->bios_geom.sectors;
372         PedCHSGeometry chs;
373
374         char* copy = ped_strdup (str);
375         if (!copy)
376                 return 0;
377         strip_string (copy);
378         remove_punct (copy);
379
380         if (sscanf (copy, "%d %d %d",
381                     &chs.cylinders, &chs.heads, &chs.sectors) != 3) {
382                 ped_exception_throw (
383                                 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
384                                 _("\"%s\" has invalid syntax for locations."),
385                                 copy);
386                 goto error_free_copy;
387         }
388
389         if (chs.heads >= dev->bios_geom.heads) {
390                 ped_exception_throw (
391                                 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
392                                 _("The maximum head value is %d."),
393                                 dev->bios_geom.heads - 1);
394                 goto error_free_copy;
395         }
396         if (chs.sectors >= dev->bios_geom.sectors) {
397                 ped_exception_throw (
398                                 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
399                                 _("The maximum sector value is %d."),
400                                 dev->bios_geom.sectors - 1);
401                 goto error_free_copy;
402         }
403
404         *sector = 1LL * chs.cylinders * cyl_size
405                 + chs.heads * dev->bios_geom.sectors
406                 + chs.sectors;
407
408         if (*sector >= dev->length) {
409                 ped_exception_throw (
410                                 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
411                                 _("The location %s is outside of the "
412                                   "device %s."),
413                                 str, dev->path);
414                 goto error_free_copy;
415         }
416         if (range)
417                 *range = ped_geometry_new (dev, *sector, 1);
418         free (copy);
419         return !range || *range != NULL;
420
421 error_free_copy:
422         free (copy);
423         *sector = 0;
424         if (range)
425                 *range = NULL;
426         return 0;
427 }
428
429 static PedSector
430 clip (const PedDevice* dev, PedSector sector)
431 {
432         if (sector < 0)
433                 return 0;
434         if (sector > dev->length - 1)
435                 return dev->length - 1;
436         return sector;
437 }
438
439 static PedGeometry*
440 geometry_from_centre_radius (const PedDevice* dev,
441                              PedSector sector, PedSector radius)
442 {
443         PedSector start = clip (dev, sector - radius);
444         PedSector end = clip (dev, sector + radius);
445         if (sector - end > radius || start - sector > radius)
446                 return NULL;
447         return ped_geometry_new (dev, start, end - start + 1);
448 }
449
450 static PedUnit
451 parse_unit_suffix (const char* suffix, PedUnit suggested_unit)
452 {
453         if (strlen (suffix) > 1 && tolower (suffix[1]) == 'i') {
454                 switch (tolower (suffix[0])) {
455                         case 'k': return PED_UNIT_KIBIBYTE;
456                         case 'm': return PED_UNIT_MEBIBYTE;
457                         case 'g': return PED_UNIT_GIBIBYTE;
458                         case 't': return PED_UNIT_TEBIBYTE;
459                 }
460         } else if (strlen (suffix) > 0) {
461                 switch (tolower (suffix[0])) {
462                         case 's': return PED_UNIT_SECTOR;
463                         case 'b': return PED_UNIT_BYTE;
464                         case 'k': return PED_UNIT_KILOBYTE;
465                         case 'm': return PED_UNIT_MEGABYTE;
466                         case 'g': return PED_UNIT_GIGABYTE;
467                         case 't': return PED_UNIT_TERABYTE;
468                         case 'c': return PED_UNIT_CYLINDER;
469                         case '%': return PED_UNIT_PERCENT;
470                 }
471         }
472
473         if (suggested_unit == PED_UNIT_COMPACT) {
474                 if (default_unit == PED_UNIT_COMPACT)
475                         return PED_UNIT_MEGABYTE;
476                 else
477                         return default_unit;
478         }
479
480         return suggested_unit;
481 }
482
483 static bool
484 is_power_of_2 (long long n)
485 {
486   return (n & (n - 1)) == 0;
487 }
488
489 /**
490  * If \p str contains a valid description of a location on \p dev, then
491  * \p *sector is modified to describe the location and a geometry is created
492  * in \p *range describing a 2 units large area centered on \p *sector.  If the
493  * \p range as described here would be partially outside the device \p dev, the
494  * geometry returned is the intersection between the former and the whole
495  * device geometry.  If no units are specified, then the default unit is
496  * assumed.
497  *
498  * \throws PED_EXCEPTION_ERROR if \p str contains invalid description of a
499  * location
500  * \throws PED_EXCEPTION_ERROR if location described by \p str
501  * is outside of the device \p dev->path
502  *
503  * \return \c 1 if \p str is a valid location description, \c 0 otherwise.
504  */
505 int
506 ped_unit_parse_custom (const char* str, const PedDevice* dev, PedUnit unit,
507                        PedSector* sector, PedGeometry** range)
508 {
509         char*     copy;
510         char*     suffix;
511         double    num;
512         long long unit_size;
513         PedSector radius;
514
515         if (is_chs (str))
516                 return parse_chs (str, dev, sector, range);
517
518         copy = ped_strdup (str);
519         if (!copy)
520                 goto error;
521         strip_string (copy);
522
523         suffix = find_suffix (copy);
524         unit = parse_unit_suffix (suffix, unit);
525         suffix[0] = 0;
526
527         if (sscanf (copy, "%lf", &num) != 1) {
528                 ped_exception_throw (
529                                 PED_EXCEPTION_ERROR,
530                                 PED_EXCEPTION_CANCEL,
531                                 _("Invalid number."));
532                 goto error_free_copy;
533         }
534         if (num > 0 && num < 1) {
535             ped_exception_throw (
536                     PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
537                     _("Use a smaller unit instead of a value < 1"));
538             goto error_free_copy;
539         }
540
541         unit_size = ped_unit_get_size (dev, unit);
542         radius = (ped_div_round_up (unit_size, dev->sector_size) / 2) - 1;
543         if (radius < 0)
544                 radius = 0;
545         /* If the user specifies units in a power of 2, e.g., 4MiB, as in
546                parted -s -- $dev mklabel gpt mkpart P-NAME 4MiB -34s
547            do not use 4MiB as the range.  Rather, presume that they
548            are specifying precisely the starting or ending number,
549            and treat "4MiB" just as we would treat "4194304B".  */
550         if (is_power_of_2 (unit_size))
551                 radius = 0;
552
553         *sector = num * unit_size / dev->sector_size;
554         /* negative numbers count from the end */
555         if (copy[0] == '-')
556                 *sector += dev->length;
557         if (range) {
558                 *range = geometry_from_centre_radius (dev, *sector, radius);
559                 if (!*range) {
560                         ped_exception_throw (
561                                 PED_EXCEPTION_ERROR, PED_EXCEPTION_CANCEL,
562                                 _("The location %s is outside of the "
563                                   "device %s."),
564                                 str, dev->path);
565                         goto error_free_copy;
566                 }
567         }
568         *sector = clip (dev, *sector);
569
570         free (copy);
571         return 1;
572
573 error_free_copy:
574         free (copy);
575 error:
576         *sector = 0;
577         if (range)
578                 *range = NULL;
579         return 0;
580 }
581
582
583 /** @} */