1
2 #include <ctype.h>
3
4 #include "fdiskP.h"
5 #include "strutils.h"
6
7 /**
8 * SECTION: parttype
9 * @title: Partition types
10 * @short_description: abstraction to partition types
11 *
12 * There are two basic types of parttypes, string based (e.g. GPT)
13 * and code/hex based (e.g. MBR).
14 */
15
16 /**
17 * fdisk_new_parttype:
18 *
19 * It's recommended to use fdisk_label_get_parttype_from_code() or
20 * fdisk_label_get_parttype_from_string() for well known types rather
21 * than allocate a new instance.
22 *
23 * Returns: new instance.
24 */
25 struct fdisk_parttype *fdisk_new_parttype(void)
26 {
27 struct fdisk_parttype *t = calloc(1, sizeof(*t));
28
29 if (!t)
30 return NULL;
31
32 t->refcount = 1;
33 t->flags = FDISK_PARTTYPE_ALLOCATED;
34 DBG(PARTTYPE, ul_debugobj(t, "alloc"));
35 return t;
36 }
37
38 /**
39 * fdisk_ref_parttype:
40 * @t: partition type
41 *
42 * Increments reference counter for allocated types
43 */
44 void fdisk_ref_parttype(struct fdisk_parttype *t)
45 {
46 if (fdisk_parttype_is_allocated(t))
47 t->refcount++;
48 }
49
50 /**
51 * fdisk_unref_parttype
52 * @t: partition pointer
53 *
54 * Decrements reference counter, on zero the @t is automatically
55 * deallocated.
56 */
57 void fdisk_unref_parttype(struct fdisk_parttype *t)
58 {
59 if (!fdisk_parttype_is_allocated(t))
60 return;
61
62 t->refcount--;
63 if (t->refcount <= 0) {
64 DBG(PARTTYPE, ul_debugobj(t, "free"));
65 free(t->typestr);
66 free(t->name);
67 free(t);
68 }
69 }
70
71 /**
72 * fdisk_parttype_set_name:
73 * @t: partition type
74 * @str: type name
75 *
76 * Sets type name to allocated partition type, for static types
77 * it returns -EINVAL.
78 *
79 * Return: 0 on success, <0 on error
80 */
81 int fdisk_parttype_set_name(struct fdisk_parttype *t, const char *str)
82 {
83 if (!t || !fdisk_parttype_is_allocated(t))
84 return -EINVAL;
85 return strdup_to_struct_member(t, name, str);
86 }
87
88 /**
89 * fdisk_parttype_set_typestr:
90 * @t: partition type
91 * @str: type identifier (e.g. GUID for GPT)
92 *
93 * Sets type string to allocated partition type, for static types
94 * it returns -EINVAL. Don't use this function for MBR, see
95 * fdisk_parttype_set_code().
96 *
97 * Return: 0 on success, <0 on error
98 */
99 int fdisk_parttype_set_typestr(struct fdisk_parttype *t, const char *str)
100 {
101 if (!t || !fdisk_parttype_is_allocated(t))
102 return -EINVAL;
103 return strdup_to_struct_member(t, typestr, str);
104 }
105
106 /**
107 * fdisk_parttype_set_code:
108 * @t: partition type
109 * @code: type identifier (e.g. MBR type codes)
110 *
111 * Sets type code to allocated partition type, for static types it returns
112 * -EINVAL. Don't use this function for GPT, see fdisk_parttype_set_typestr().
113 *
114 * Return: 0 on success, <0 on error
115 */
116 int fdisk_parttype_set_code(struct fdisk_parttype *t, int code)
117 {
118 if (!t || !fdisk_parttype_is_allocated(t))
119 return -EINVAL;
120 t->code = code;
121 return 0;
122 }
123
124 /**
125 * fdisk_label_get_nparttypes:
126 * @lb: label
127 *
128 * Returns: number of types supported by label.
129 */
130 size_t fdisk_label_get_nparttypes(const struct fdisk_label *lb)
131 {
132 if (!lb)
133 return 0;
134 return lb->nparttypes;
135 }
136
137 /**
138 * fdisk_label_get_parttype:
139 * @lb: label
140 * @n: number
141 *
142 * Returns: return parttype
143 */
144 struct fdisk_parttype *fdisk_label_get_parttype(const struct fdisk_label *lb, size_t n)
145 {
146 if (!lb || n >= lb->nparttypes)
147 return NULL;
148 return &lb->parttypes[n];
149 }
150
151 /**
152 * fdisk_label_get_parttype_shortcut:
153 * @lb: label
154 * @n: number
155 * @typestr: returns type as string
156 * @shortcut: returns type shortcut string
157 * @alias: returns type alias string
158 *
159 * Returns: return 0 on success, <0 on error, 2 for deprecated alias, 1 for @n out of range
160 *
161 * Since: 2.36
162 */
163 int fdisk_label_get_parttype_shortcut(const struct fdisk_label *lb, size_t n,
164 const char **typestr, const char **shortcut, const char **alias)
165 {
166 const struct fdisk_shortcut *sc;
167
168 if (!lb)
169 return -EINVAL;
170 if (n >= lb->nparttype_cuts)
171 return 1;
172
173 sc = &lb->parttype_cuts[n];
174 if (typestr)
175 *typestr = sc->data;
176 if (shortcut)
177 *shortcut = sc->shortcut;
178 if (alias)
179 *alias = sc->alias;
180
181 return sc->deprecated == 1 ? 2 : 0;
182
183 }
184
185
186 /**
187 * fdisk_label_has_code_parttypes:
188 * @lb: label
189 *
190 * Returns: 1 if the label uses code as partition type
191 * identifiers (e.g. MBR) or 0.
192 */
193 int fdisk_label_has_code_parttypes(const struct fdisk_label *lb)
194 {
195 assert(lb);
196
197 if (lb->parttypes && lb->parttypes[0].typestr)
198 return 0;
199 return 1;
200 }
201
202 /**
203 * fdisk_label_has_parttypes_shortcuts
204 * @lb: label
205 *
206 * Returns: 1 if the label support shortuts/aliases for partition types or 0.
207 *
208 * Since: 2.36
209 */
210 int fdisk_label_has_parttypes_shortcuts(const struct fdisk_label *lb)
211 {
212 assert(lb);
213 return lb->nparttype_cuts ? 1 : 0;
214 }
215
216
217 /**
218 * fdisk_label_get_parttype_from_code:
219 * @lb: label
220 * @code: code to search for
221 *
222 * Search for partition type in label-specific table. The result
223 * is pointer to static array of label types.
224 *
225 * Returns: partition type or NULL upon failure or invalid @code.
226 */
227 struct fdisk_parttype *fdisk_label_get_parttype_from_code(
228 const struct fdisk_label *lb,
229 unsigned int code)
230 {
231 size_t i;
232
233 assert(lb);
234
235 if (!lb->nparttypes)
236 return NULL;
237
238 for (i = 0; i < lb->nparttypes; i++)
239 if (lb->parttypes[i].code == code)
240 return &lb->parttypes[i];
241 return NULL;
242 }
243
244 /**
245 * fdisk_label_get_parttype_from_string:
246 * @lb: label
247 * @str: string to search for
248 *
249 * Search for partition type in label-specific table. The result
250 * is pointer to static array of label types.
251 *
252 * Returns: partition type or NULL upon failure or invalid @str.
253 */
254 struct fdisk_parttype *fdisk_label_get_parttype_from_string(
255 const struct fdisk_label *lb,
256 const char *str)
257 {
258 size_t i;
259
260 assert(lb);
261
262 if (!lb->nparttypes)
263 return NULL;
264
265 for (i = 0; i < lb->nparttypes; i++)
266 if (lb->parttypes[i].typestr
267 && strcasecmp(lb->parttypes[i].typestr, str) == 0)
268 return &lb->parttypes[i];
269
270 return NULL;
271 }
272
273 /**
274 * fdisk_new_unknown_parttype:
275 * @code: type as number
276 * @typestr: type as string
277
278 * Allocates new 'unknown' partition type. Use fdisk_unref_parttype() to
279 * deallocate.
280 *
281 * Returns: newly allocated partition type, or NULL upon failure.
282 */
283 struct fdisk_parttype *fdisk_new_unknown_parttype(unsigned int code,
284 const char *typestr)
285 {
286 struct fdisk_parttype *t = fdisk_new_parttype();
287
288 if (!t)
289 return NULL;
290
291 fdisk_parttype_set_name(t, _("unknown"));
292 fdisk_parttype_set_code(t, code);
293 fdisk_parttype_set_typestr(t, typestr);
294 t->flags |= FDISK_PARTTYPE_UNKNOWN;
295
296 return t;
297 }
298
299 /**
300 * fdisk_copy_parttype:
301 * @type: type to copy
302 *
303 * Use fdisk_unref_parttype() to deallocate.
304 *
305 * Returns: newly allocated partition type, or NULL upon failure.
306 */
307 struct fdisk_parttype *fdisk_copy_parttype(const struct fdisk_parttype *type)
308 {
309 struct fdisk_parttype *t = fdisk_new_parttype();
310
311 if (!t)
312 return NULL;
313
314 fdisk_parttype_set_name(t, type->name);
315 fdisk_parttype_set_code(t, type->code);
316 fdisk_parttype_set_typestr(t, type->typestr);
317
318 return t;
319 }
320
321 static struct fdisk_parttype *parttype_from_data(
322 const struct fdisk_label *lb,
323 const char *str,
324 unsigned int *xcode,
325 int use_seqnum)
326 {
327 struct fdisk_parttype *types, *ret = NULL;
328 char *end = NULL;
329
330 assert(lb);
331 assert(str);
332
333 if (xcode)
334 *xcode = 0;
335 if (!lb->nparttypes)
336 return NULL;
337
338 DBG(LABEL, ul_debugobj(lb, " parsing '%s' data", str));
339 types = lb->parttypes;
340
341 if (types[0].typestr == NULL) {
342 unsigned int code;
343
344 DBG(LABEL, ul_debugobj(lb, " +hex"));
345
346 errno = 0;
347 code = strtol(str, &end, 16);
348
349 if (errno || *end != '\0') {
350 DBG(LABEL, ul_debugobj(lb, " failed: %m"));
351 return NULL;
352 }
353 if (xcode)
354 *xcode = code;
355 ret = fdisk_label_get_parttype_from_code(lb, code);
356 } else {
357 DBG(LABEL, ul_debugobj(lb, " +string"));
358
359 /* maybe specified by type string (e.g. UUID) */
360 ret = fdisk_label_get_parttype_from_string(lb, str);
361
362 if (!ret) {
363 /* maybe specified by order number */
364 int i;
365
366 errno = 0;
367 i = strtol(str, &end, 0);
368
369 if (use_seqnum && errno == 0
370 && *end == '\0' && i > 0
371 && i - 1 < (int) lb->nparttypes)
372 ret = &types[i - 1];
373 }
374 }
375
376 if (ret)
377 DBG(PARTTYPE, ul_debugobj(ret, " result '%s'", ret->name));
378 return ret;
379 }
380
381 static struct fdisk_parttype *parttype_from_shortcut(
382 const struct fdisk_label *lb,
383 const char *str, int deprecated)
384 {
385 size_t i;
386
387 DBG(LABEL, ul_debugobj(lb, " parsing '%s' shortcut", str));
388
389 for (i = 0; i < lb->nparttype_cuts; i++) {
390 const struct fdisk_shortcut *sc = &lb->parttype_cuts[i];
391
392 if (sc->deprecated && !deprecated)
393 continue;
394 if (sc->shortcut && strcmp(sc->shortcut, str) == 0)
395 return parttype_from_data(lb, sc->data, NULL, 0);
396 }
397 return NULL;
398 }
399
400 static struct fdisk_parttype *parttype_from_alias(
401 const struct fdisk_label *lb,
402 const char *str, int deprecated)
403 {
404 size_t i;
405
406 DBG(LABEL, ul_debugobj(lb, " parsing '%s' alias", str));
407
408 for (i = 0; i < lb->nparttype_cuts; i++) {
409 const struct fdisk_shortcut *sc = &lb->parttype_cuts[i];
410
411 if (sc->deprecated && !deprecated)
412 continue;
413 if (sc->alias && strcmp(sc->alias, str) == 0)
414 return parttype_from_data(lb, sc->data, NULL, 0);
415 }
416 return NULL;
417 }
418
419 static struct fdisk_parttype *parttype_from_name(
420 const struct fdisk_label *lb,
421 const char *str)
422 {
423 size_t i;
424
425 DBG(LABEL, ul_debugobj(lb, " parsing '%s' name", str));
426
427 for (i = 0; i < lb->nparttypes; i++) {
428 const char *name = lb->parttypes[i].name;
429
430 if (name && *name && ul_stralnumcmp(name, str) == 0)
431 return &lb->parttypes[i];
432 }
433
434 return NULL;
435 }
436
437 /**
438 * fdisk_label_advparse_parttype:
439 * @lb: label
440 * @str: string to parse from
441 * @flags: FDISK_PARTTYPE_PARSE_*
442 *
443 * This function is advanced partition types parser. It parses partition type
444 * from @str according to the label. The function returns a pointer to static
445 * table of the partition types, or newly allocated partition type for unknown
446 * types (see fdisk_parttype_is_unknown(). It's safe to call fdisk_unref_parttype()
447 * for all results.
448 *
449 * The @str may be type data (hex code or UUID), alias or shortcut. For GPT
450 * also sequence number of the type in the list of the supported types.
451 *
452 * Returns: pointer to type or NULL on error.
453 */
454 struct fdisk_parttype *fdisk_label_advparse_parttype(
455 const struct fdisk_label *lb,
456 const char *str,
457 int flags)
458 {
459 struct fdisk_parttype *res = NULL;
460 unsigned int code = 0;
461
462 if (!lb || !lb->nparttypes)
463 return NULL;
464
465 DBG(LABEL, ul_debugobj(lb, "parsing '%s' (%s) type", str, lb->name));
466
467 if ((flags & FDISK_PARTTYPE_PARSE_DATA)
468 && !(flags & FDISK_PARTTYPE_PARSE_DATALAST))
469 res = parttype_from_data(lb, str, &code,
470 flags & FDISK_PARTTYPE_PARSE_SEQNUM);
471
472 if (!res && (flags & FDISK_PARTTYPE_PARSE_ALIAS))
473 res = parttype_from_alias(lb, str,
474 flags & FDISK_PARTTYPE_PARSE_DEPRECATED);
475
476 if (!res && (flags & FDISK_PARTTYPE_PARSE_SHORTCUT))
477 res = parttype_from_shortcut(lb, str,
478 flags & FDISK_PARTTYPE_PARSE_DEPRECATED);
479
480 if (!res && (flags & FDISK_PARTTYPE_PARSE_NAME))
481 res = parttype_from_name(lb, str);
482
483 if (!res && (flags & FDISK_PARTTYPE_PARSE_DATA)
484 && (flags & FDISK_PARTTYPE_PARSE_DATALAST))
485 res = parttype_from_data(lb, str, &code,
486 flags & FDISK_PARTTYPE_PARSE_SEQNUM);
487
488 if (!res && !(flags & FDISK_PARTTYPE_PARSE_NOUNKNOWN)) {
489 if (lb->parttypes[0].typestr)
490 res = fdisk_new_unknown_parttype(0, str);
491 else
492 res = fdisk_new_unknown_parttype(code, NULL);
493 }
494
495 if (res)
496 DBG(PARTTYPE, ul_debugobj(res, "returns parsed '%s' [%s] partition type",
497 res->name, res->typestr ? : ""));
498 return res;
499 }
500
501 /**
502 * fdisk_label_parse_parttype:
503 * @lb: label
504 * @str: string to parse from (type name, UUID, etc.)
505 *
506 * Parses partition type from @str according to the label. The function returns
507 * a pointer to static table of the partition types, or newly allocated
508 * partition type for unknown types (see fdisk_parttype_is_unknown(). It's
509 * safe to call fdisk_unref_parttype() for all results.
510 *
511 * Note that for GPT it accepts sequence number of UUID.
512 *
513 * Returns: pointer to type or NULL on error.
514 */
515 struct fdisk_parttype *fdisk_label_parse_parttype(
516 const struct fdisk_label *lb,
517 const char *str)
518 {
519 return fdisk_label_advparse_parttype(lb, str, FDISK_PARTTYPE_PARSE_DATA);
520 }
521
522 /**
523 * fdisk_parttype_get_string:
524 * @t: type
525 *
526 * Returns: partition type string (e.g. GUID for GPT)
527 */
528 const char *fdisk_parttype_get_string(const struct fdisk_parttype *t)
529 {
530 assert(t);
531 return t->typestr && *t->typestr ? t->typestr : NULL;
532 }
533
534 /**
535 * fdisk_parttype_get_code:
536 * @t: type
537 *
538 * Returns: partition type code (e.g. for MBR)
539 */
540 unsigned int fdisk_parttype_get_code(const struct fdisk_parttype *t)
541 {
542 assert(t);
543 return t->code;
544 }
545
546 /**
547 * fdisk_parttype_get_name:
548 * @t: type
549 *
550 * Returns: partition type human readable name
551 */
552 const char *fdisk_parttype_get_name(const struct fdisk_parttype *t)
553 {
554 assert(t);
555 return t->name;
556 }
557
558 /**
559 * fdisk_parttype_is_unknown:
560 * @t: type
561 *
562 * Checks for example result from fdisk_label_parse_parttype().
563 *
564 * Returns: 1 is type is "unknown" or 0.
565 */
566 int fdisk_parttype_is_unknown(const struct fdisk_parttype *t)
567 {
568 return t && (t->flags & FDISK_PARTTYPE_UNKNOWN) ? 1 : 0;
569 }