1 /*
2 * partitions - partition tables parsing
3 *
4 * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
5 *
6 * This file may be redistributed under the terms of the
7 * GNU Lesser General Public License.
8 *
9 */
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <ctype.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <errno.h>
19 #include <stdint.h>
20 #include <inttypes.h>
21 #include <stdarg.h>
22
23 #include "partitions.h"
24 #include "sysfs.h"
25 #include "strutils.h"
26
27 /**
28 * SECTION: partitions
29 * @title: Partitions probing
30 * @short_description: partitions tables detection and parsing
31 *
32 * This chain supports binary and NAME=value interfaces, but complete PT
33 * description is provided by binary interface only. The libblkid prober is
34 * compatible with kernel partition tables parser. The parser does not return
35 * empty (size=0) partitions or special hidden partitions.
36 *
37 * NAME=value interface, supported tags:
38 *
39 * @PTTYPE: partition table type (dos, gpt, etc.).
40 *
41 * @PTUUID: partition table id (uuid for gpt, hex for dos).
42
43 * @PART_ENTRY_SCHEME: partition table type
44 *
45 * @PART_ENTRY_NAME: partition name (gpt and mac only)
46 *
47 * @PART_ENTRY_UUID: partition UUID (gpt, or pseudo IDs for MBR)
48 *
49 * @PART_ENTRY_TYPE: partition type, 0xNN (e.g. 0x82) or type UUID (gpt only) or type string (mac)
50 *
51 * @PART_ENTRY_FLAGS: partition flags (e.g. boot_ind) or attributes (e.g. gpt attributes)
52 *
53 * @PART_ENTRY_NUMBER: partition number
54 *
55 * @PART_ENTRY_OFFSET: the begin of the partition
56 *
57 * @PART_ENTRY_SIZE: size of the partition
58 *
59 * @PART_ENTRY_DISK: whole-disk maj:min
60 *
61 * Example:
62 *
63 * <informalexample>
64 * <programlisting>
65 * blkid_probe pr;
66 * const char *ptname;
67 *
68 * pr = blkid_new_probe_from_filename(devname);
69 * if (!pr)
70 * err("%s: failed to open device", devname);
71 *
72 * blkid_probe_enable_partitions(pr, TRUE);
73 * blkid_do_fullprobe(pr);
74 *
75 * blkid_probe_lookup_value(pr, "PTTYPE", &ptname, NULL);
76 * printf("%s partition type detected\n", pttype);
77 *
78 * blkid_free_probe(pr);
79 *
80 * // don't forget to check return codes in your code!
81 * </programlisting>
82 * </informalexample>
83 *
84 * Binary interface:
85 *
86 * <informalexample>
87 * <programlisting>
88 * blkid_probe pr;
89 * blkid_partlist ls;
90 * int nparts, i;
91 *
92 * pr = blkid_new_probe_from_filename(devname);
93 * if (!pr)
94 * err("%s: failed to open device", devname);
95 *
96 * ls = blkid_probe_get_partitions(pr);
97 * nparts = blkid_partlist_numof_partitions(ls);
98 *
99 * for (i = 0; i < nparts; i++) {
100 * blkid_partition par = blkid_partlist_get_partition(ls, i);
101 * printf("#%d: %llu %llu 0x%x",
102 * blkid_partition_get_partno(par),
103 * blkid_partition_get_start(par),
104 * blkid_partition_get_size(par),
105 * blkid_partition_get_type(par));
106 * }
107 *
108 * blkid_free_probe(pr);
109 *
110 * // don't forget to check return codes in your code!
111 * </programlisting>
112 * </informalexample>
113 */
114
115 /*
116 * Chain driver function
117 */
118 static int partitions_probe(blkid_probe pr, struct blkid_chain *chn);
119 static void partitions_free_data(blkid_probe pr, void *data);
120
121 /*
122 * Partitions chain probing functions
123 */
124 static const struct blkid_idinfo *idinfos[] =
125 {
126 &aix_pt_idinfo,
127 &sgi_pt_idinfo,
128 &sun_pt_idinfo,
129 &dos_pt_idinfo,
130 &gpt_pt_idinfo,
131 &pmbr_pt_idinfo, /* always after GPT */
132 &mac_pt_idinfo,
133 &ultrix_pt_idinfo,
134 &bsd_pt_idinfo,
135 &unixware_pt_idinfo,
136 &solaris_x86_pt_idinfo,
137 &minix_pt_idinfo,
138 &atari_pt_idinfo
139 };
140
141 /*
142 * Driver definition
143 */
144 const struct blkid_chaindrv partitions_drv = {
145 .id = BLKID_CHAIN_PARTS,
146 .name = "partitions",
147 .dflt_enabled = FALSE,
148 .idinfos = idinfos,
149 .nidinfos = ARRAY_SIZE(idinfos),
150 .has_fltr = TRUE,
151 .probe = partitions_probe,
152 .safeprobe = partitions_probe,
153 .free_data = partitions_free_data
154 };
155
156
157 /*
158 * For compatibility with the rest of libblkid API (with the old high-level
159 * API) we use completely opaque typedefs for all structs. Don't forget that
160 * the final blkid_* types are pointers! See blkid.h.
161 *
162 * [Just for the record, I hate typedef for pointers --kzak]
163 */
164
165 /* exported as opaque type "blkid_parttable" */
166 struct blkid_struct_parttable {
167 const char *type; /* partition table type */
168 uint64_t offset; /* begin of the partition table (in bytes) */
169 int nparts; /* number of partitions */
170 blkid_partition parent; /* parent of nested partition table */
171 char id[UUID_STR_LEN]; /* PT identifier (e.g. UUID for GPT) */
172
173 struct list_head t_tabs; /* all tables */
174 };
175
176 /* exported as opaque type "blkid_partition" */
177 struct blkid_struct_partition {
178 uint64_t start; /* begin of the partition (512-bytes sectors) */
179 uint64_t size; /* size of the partitions (512-bytes sectors) */
180
181 int type; /* partition type */
182 char typestr[UUID_STR_LEN]; /* partition type string (GPT and Mac) */
183
184 unsigned long long flags; /* partition flags / attributes */
185
186 int partno; /* partition number */
187 char uuid[UUID_STR_LEN]; /* UUID (when supported by PT), e.g. GPT */
188 unsigned char name[128]; /* Partition in UTF8 name (when supported by PT), e.g. Mac */
189
190 blkid_parttable tab; /* partition table */
191 };
192
193 /* exported as opaque type "blkid_partlist" */
194 struct blkid_struct_partlist {
195 int next_partno; /* next partition number */
196 blkid_partition next_parent; /* next parent if parsing nested PT */
197
198 int nparts; /* number of partitions */
199 int nparts_max; /* max.number of partitions */
200 blkid_partition parts; /* array of partitions */
201
202 struct list_head l_tabs; /* list of partition tables */
203 };
204
205 static int blkid_partitions_probe_partition(blkid_probe pr);
206
207 /**
208 * blkid_probe_enable_partitions:
209 * @pr: probe
210 * @enable: TRUE/FALSE
211 *
212 * Enables/disables the partitions probing for non-binary interface.
213 *
214 * Returns: 0 on success, or -1 in case of error.
215 */
216 int blkid_probe_enable_partitions(blkid_probe pr, int enable)
217 {
218 pr->chains[BLKID_CHAIN_PARTS].enabled = enable;
219 return 0;
220 }
221
222 /**
223 * blkid_probe_set_partitions_flags:
224 * @pr: prober
225 * @flags: BLKID_PARTS_* flags
226 *
227 * Sets probing flags to the partitions prober. This function is optional.
228 *
229 * Returns: 0 on success, or -1 in case of error.
230 */
231 int blkid_probe_set_partitions_flags(blkid_probe pr, int flags)
232 {
233 pr->chains[BLKID_CHAIN_PARTS].flags = flags;
234 return 0;
235 }
236
237 int blkid_probe_get_partitions_flags(blkid_probe pr)
238 {
239 return pr->chains[BLKID_CHAIN_PARTS].flags;
240 }
241
242 /**
243 * blkid_probe_reset_partitions_filter:
244 * @pr: prober
245 *
246 * Resets partitions probing filter
247 *
248 * Returns: 0 on success, or -1 in case of error.
249 */
250 int blkid_probe_reset_partitions_filter(blkid_probe pr)
251 {
252 return __blkid_probe_reset_filter(pr, BLKID_CHAIN_PARTS);
253 }
254
255 /**
256 * blkid_probe_invert_partitions_filter:
257 * @pr: prober
258 *
259 * Inverts partitions probing filter
260 *
261 * Returns: 0 on success, or -1 in case of error.
262 */
263 int blkid_probe_invert_partitions_filter(blkid_probe pr)
264 {
265 return __blkid_probe_invert_filter(pr, BLKID_CHAIN_PARTS);
266 }
267
268 /**
269 * blkid_probe_filter_partitions_type:
270 * @pr: prober
271 * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
272 * @names: NULL terminated array of probing function names (e.g. "vfat").
273 *
274 * %BLKID_FLTR_NOTIN - probe for all items which are NOT IN @names
275 *
276 * %BLKID_FLTR_ONLYIN - probe for items which are IN @names
277 *
278 * Returns: 0 on success, or -1 in case of error.
279 */
280 int blkid_probe_filter_partitions_type(blkid_probe pr, int flag, char *names[])
281 {
282 return __blkid_probe_filter_types(pr, BLKID_CHAIN_PARTS, flag, names);
283 }
284
285 /**
286 * blkid_probe_get_partitions:
287 * @pr: probe
288 *
289 * This is a binary interface for partitions. See also blkid_partlist_*
290 * functions.
291 *
292 * This function is independent on blkid_do_[safe,full]probe() and
293 * blkid_probe_enable_partitions() calls.
294 *
295 * WARNING: the returned object will be overwritten by the next
296 * blkid_probe_get_partitions() call for the same @pr. If you want to
297 * use more blkid_partlist objects in the same time you have to create
298 * more blkid_probe handlers (see blkid_new_probe()).
299 *
300 * Returns: list of partitions, or NULL in case of error.
301 */
302 blkid_partlist blkid_probe_get_partitions(blkid_probe pr)
303 {
304 return (blkid_partlist) blkid_probe_get_binary_data(pr,
305 &pr->chains[BLKID_CHAIN_PARTS]);
306 }
307
308 /* for internal usage only */
309 blkid_partlist blkid_probe_get_partlist(blkid_probe pr)
310 {
311 return (blkid_partlist) pr->chains[BLKID_CHAIN_PARTS].data;
312 }
313
314 static void blkid_probe_set_partlist(blkid_probe pr, blkid_partlist ls)
315 {
316 pr->chains[BLKID_CHAIN_PARTS].data = ls;
317 }
318
319 static void ref_parttable(blkid_parttable tab)
320 {
321 if (tab)
322 tab->nparts++;
323 }
324
325 static void unref_parttable(blkid_parttable tab)
326 {
327 if (!tab)
328 return;
329
330 tab->nparts--;
331 if (tab->nparts <= 0) {
332 list_del(&tab->t_tabs);
333 free(tab);
334 }
335 }
336
337 /* free all allocated parttables */
338 static void free_parttables(blkid_partlist ls)
339 {
340 if (!ls || !ls->l_tabs.next)
341 return;
342
343 /* remove unassigned partition tables */
344 while (!list_empty(&ls->l_tabs)) {
345 blkid_parttable tab = list_entry(ls->l_tabs.next,
346 struct blkid_struct_parttable, t_tabs);
347 unref_parttable(tab);
348 }
349 }
350
351 static void reset_partlist(blkid_partlist ls)
352 {
353 if (!ls)
354 return;
355
356 free_parttables(ls);
357
358 if (ls->next_partno) {
359 /* already initialized - reset */
360 int tmp_nparts = ls->nparts_max;
361 blkid_partition tmp_parts = ls->parts;
362
363 memset(ls, 0, sizeof(struct blkid_struct_partlist));
364
365 ls->nparts_max = tmp_nparts;
366 ls->parts = tmp_parts;
367 }
368
369 ls->nparts = 0;
370 ls->next_partno = 1;
371 INIT_LIST_HEAD(&ls->l_tabs);
372
373 DBG(LOWPROBE, ul_debug("partlist reset"));
374 }
375
376 static blkid_partlist partitions_init_data(struct blkid_chain *chn)
377 {
378 blkid_partlist ls;
379
380 if (chn->data)
381 ls = (blkid_partlist) chn->data;
382 else {
383 /* allocate the new list of partitions */
384 ls = calloc(1, sizeof(struct blkid_struct_partlist));
385 if (!ls)
386 return NULL;
387 chn->data = (void *) ls;
388 }
389
390 reset_partlist(ls);
391
392 DBG(LOWPROBE, ul_debug("parts: initialized partitions list (size=%d)", ls->nparts_max));
393 return ls;
394 }
395
396 static void partitions_free_data(blkid_probe pr __attribute__((__unused__)),
397 void *data)
398 {
399 blkid_partlist ls = (blkid_partlist) data;
400
401 if (!ls)
402 return;
403
404 free_parttables(ls);
405
406 /* deallocate partitions and partlist */
407 free(ls->parts);
408 free(ls);
409 }
410
411 blkid_parttable blkid_partlist_new_parttable(blkid_partlist ls,
412 const char *type, uint64_t offset)
413 {
414 blkid_parttable tab;
415
416 tab = calloc(1, sizeof(struct blkid_struct_parttable));
417 if (!tab)
418 return NULL;
419 tab->type = type;
420 tab->offset = offset;
421 tab->parent = ls->next_parent;
422
423 INIT_LIST_HEAD(&tab->t_tabs);
424 list_add_tail(&tab->t_tabs, &ls->l_tabs);
425
426 DBG(LOWPROBE, ul_debug("parts: create a new partition table "
427 "(type=%s, offset=%"PRId64")", type, offset));
428 return tab;
429 }
430
431 static blkid_partition new_partition(blkid_partlist ls, blkid_parttable tab)
432 {
433 blkid_partition par;
434
435 if (ls->nparts + 1 > ls->nparts_max) {
436 /* Linux kernel has DISK_MAX_PARTS=256, but it's too much for
437 * generic Linux machine -- let start with 32 partitions.
438 */
439 void *tmp = realloc(ls->parts, (ls->nparts_max + 32) *
440 sizeof(struct blkid_struct_partition));
441 if (!tmp)
442 return NULL;
443 ls->parts = tmp;
444 ls->nparts_max += 32;
445 }
446
447 par = &ls->parts[ls->nparts++];
448 memset(par, 0, sizeof(struct blkid_struct_partition));
449
450 ref_parttable(tab);
451 par->tab = tab;
452 par->partno = blkid_partlist_increment_partno(ls);
453
454 return par;
455 }
456
457 blkid_partition blkid_partlist_add_partition(blkid_partlist ls,
458 blkid_parttable tab, uint64_t start, uint64_t size)
459 {
460 blkid_partition par = new_partition(ls, tab);
461
462 if (!par)
463 return NULL;
464
465 par->start = start;
466 par->size = size;
467
468 DBG(LOWPROBE, ul_debug("parts: add partition (start=%"
469 PRIu64 ", size=%" PRIu64 ")",
470 par->start, par->size));
471 return par;
472 }
473
474 /* can be used to modify used partitions numbers (for example for logical partitions) */
475 int blkid_partlist_set_partno(blkid_partlist ls, int partno)
476 {
477 if (!ls)
478 return -1;
479 ls->next_partno = partno;
480 return 0;
481 }
482
483 int blkid_partlist_increment_partno(blkid_partlist ls)
484 {
485 return ls ? ls->next_partno++ : -1;
486 }
487
488 /* can be used to set "parent" for the next nested partition */
489 static int blkid_partlist_set_parent(blkid_partlist ls, blkid_partition par)
490 {
491 if (!ls)
492 return -1;
493 ls->next_parent = par;
494 return 0;
495 }
496
497 blkid_partition blkid_partlist_get_parent(blkid_partlist ls)
498 {
499 if (!ls)
500 return NULL;
501 return ls->next_parent;
502 }
503
504 int blkid_partitions_need_typeonly(blkid_probe pr)
505 {
506 struct blkid_chain *chn = blkid_probe_get_chain(pr);
507
508 return chn && chn->data && chn->binary ? FALSE : TRUE;
509 }
510
511 /* get private chain flags */
512 int blkid_partitions_get_flags(blkid_probe pr)
513 {
514 struct blkid_chain *chn = blkid_probe_get_chain(pr);
515
516 return chn ? chn->flags : 0;
517 }
518
519 /* check if @start and @size are within @par partition */
520 int blkid_is_nested_dimension(blkid_partition par,
521 uint64_t start, uint64_t size)
522 {
523 uint64_t pstart;
524 uint64_t psize;
525
526 if (!par)
527 return 0;
528
529 pstart = blkid_partition_get_start(par);
530 psize = blkid_partition_get_size(par);
531
532 if (start < pstart || start + size > pstart + psize)
533 return 0;
534
535 return 1;
536 }
537
538 static int idinfo_probe(blkid_probe pr, const struct blkid_idinfo *id,
539 struct blkid_chain *chn)
540 {
541 const struct blkid_idmag *mag = NULL;
542 uint64_t off;
543 int rc = BLKID_PROBE_NONE; /* default is nothing */
544
545 if (pr->size <= 0 || (id->minsz && (unsigned)id->minsz > pr->size))
546 goto nothing; /* the device is too small */
547 if (pr->flags & BLKID_FL_NOSCAN_DEV)
548 goto nothing;
549
550 rc = blkid_probe_get_idmag(pr, id, &off, &mag);
551 if (rc != BLKID_PROBE_OK)
552 goto nothing;
553
554 /* final check by probing function */
555 if (id->probefunc) {
556 DBG(LOWPROBE, ul_debug(
557 "%s: ---> call probefunc()", id->name));
558 rc = id->probefunc(pr, mag);
559 if (rc < 0) {
560 /* reset after error */
561 reset_partlist(blkid_probe_get_partlist(pr));
562 if (chn && !chn->binary)
563 blkid_probe_chain_reset_values(pr, chn);
564 DBG(LOWPROBE, ul_debug("%s probefunc failed, rc %d",
565 id->name, rc));
566 }
567 if (rc == BLKID_PROBE_OK && mag && chn && !chn->binary)
568 rc = blkid_probe_set_magic(pr, off, mag->len,
569 (const unsigned char *) mag->magic);
570
571 DBG(LOWPROBE, ul_debug("%s: <--- (rc = %d)", id->name, rc));
572 }
573
574 return rc;
575
576 nothing:
577 return BLKID_PROBE_NONE;
578 }
579
580 /*
581 * The blkid_do_probe() backend.
582 */
583 static int partitions_probe(blkid_probe pr, struct blkid_chain *chn)
584 {
585 int rc = BLKID_PROBE_NONE;
586 size_t i;
587
588 if (!pr || chn->idx < -1)
589 return -EINVAL;
590
591 blkid_probe_chain_reset_values(pr, chn);
592
593 if (pr->flags & BLKID_FL_NOSCAN_DEV)
594 return BLKID_PROBE_NONE;
595
596 if (chn->binary)
597 partitions_init_data(chn);
598
599 if (!pr->wipe_size && (pr->prob_flags & BLKID_PROBE_FL_IGNORE_PT))
600 goto details_only;
601
602 DBG(LOWPROBE, ul_debug("--> starting probing loop [PARTS idx=%d]",
603 chn->idx));
604
605 i = chn->idx < 0 ? 0 : chn->idx + 1U;
606
607 for ( ; i < ARRAY_SIZE(idinfos); i++) {
608 const char *name;
609
610 chn->idx = i;
611
612 /* apply filter */
613 if (chn->fltr && blkid_bmp_get_item(chn->fltr, i))
614 continue;
615
616 /* apply checks from idinfo */
617 rc = idinfo_probe(pr, idinfos[i], chn);
618 if (rc < 0)
619 break;
620 if (rc != BLKID_PROBE_OK)
621 continue;
622
623 name = idinfos[i]->name;
624
625 if (!chn->binary)
626 /*
627 * Non-binary interface, set generic variables. Note
628 * that the another variables could be set in prober
629 * functions.
630 */
631 blkid_probe_set_value(pr, "PTTYPE",
632 (const unsigned char *) name,
633 strlen(name) + 1);
634
635 DBG(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [PARTS idx=%d]",
636 name, chn->idx));
637 rc = BLKID_PROBE_OK;
638 break;
639 }
640
641 if (rc != BLKID_PROBE_OK) {
642 DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed=%d) [PARTS idx=%d]",
643 rc, chn->idx));
644 }
645
646 details_only:
647 /*
648 * Gather PART_ENTRY_* values if the current device is a partition.
649 */
650 if ((rc == BLKID_PROBE_OK || rc == BLKID_PROBE_NONE) && !chn->binary &&
651 (blkid_partitions_get_flags(pr) & BLKID_PARTS_ENTRY_DETAILS)) {
652
653 int xrc = blkid_partitions_probe_partition(pr);
654
655 /* partition entry probing is optional, and "not-found" from
656 * this sub-probing must not to overwrite previous success. */
657 if (xrc < 0)
658 rc = xrc; /* always propagate errors */
659 else if (rc == BLKID_PROBE_NONE)
660 rc = xrc;
661 }
662
663 DBG(LOWPROBE, ul_debug("partitions probe done [rc=%d]", rc));
664 return rc;
665 }
666
667 /* Probe for nested partition table within the parental partition */
668 int blkid_partitions_do_subprobe(blkid_probe pr, blkid_partition parent,
669 const struct blkid_idinfo *id)
670 {
671 blkid_probe prc;
672 int rc;
673 blkid_partlist ls;
674 uint64_t sz, off;
675
676 DBG(LOWPROBE, ul_debug(
677 "parts: ----> %s subprobe requested)",
678 id->name));
679
680 if (!pr || !parent || !parent->size)
681 return -EINVAL;
682 if (pr->flags & BLKID_FL_NOSCAN_DEV)
683 return BLKID_PROBE_NONE;
684
685 /* range defined by parent */
686 sz = parent->size << 9;
687 off = parent->start << 9;
688
689 if (off < pr->off || pr->off + pr->size < off + sz) {
690 DBG(LOWPROBE, ul_debug(
691 "ERROR: parts: <---- '%s' subprobe: overflow detected.",
692 id->name));
693 return -ENOSPC;
694 }
695
696 /* create private prober */
697 prc = blkid_clone_probe(pr);
698 if (!prc)
699 return -ENOMEM;
700
701 blkid_probe_set_dimension(prc, off, sz);
702
703 /* clone is always with reset chain, fix it */
704 prc->cur_chain = blkid_probe_get_chain(pr);
705
706 /*
707 * Set 'parent' to the current list of the partitions and use the list
708 * in cloned prober (so the cloned prober will extend the current list
709 * of partitions rather than create a new).
710 */
711 ls = blkid_probe_get_partlist(pr);
712 blkid_partlist_set_parent(ls, parent);
713
714 blkid_probe_set_partlist(prc, ls);
715
716 rc = idinfo_probe(prc, id, blkid_probe_get_chain(pr));
717
718 blkid_probe_set_partlist(prc, NULL);
719 blkid_partlist_set_parent(ls, NULL);
720
721 blkid_free_probe(prc); /* free cloned prober */
722
723 DBG(LOWPROBE, ul_debug(
724 "parts: <---- %s subprobe done (rc=%d)",
725 id->name, rc));
726
727 return rc;
728 }
729
730 static int blkid_partitions_probe_partition(blkid_probe pr)
731 {
732 blkid_probe disk_pr = NULL;
733 blkid_partlist ls;
734 blkid_partition par;
735 dev_t devno;
736
737 DBG(LOWPROBE, ul_debug("parts: start probing for partition entry"));
738
739 if (pr->flags & BLKID_FL_NOSCAN_DEV)
740 goto nothing;
741
742 devno = blkid_probe_get_devno(pr);
743 if (!devno)
744 goto nothing;
745
746 disk_pr = blkid_probe_get_wholedisk_probe(pr);
747 if (!disk_pr)
748 goto nothing;
749
750 /* parse PT */
751 ls = blkid_probe_get_partitions(disk_pr);
752 if (!ls)
753 goto nothing;
754
755 par = blkid_partlist_devno_to_partition(ls, devno);
756 if (!par)
757 goto nothing;
758 else {
759 const char *v;
760 blkid_parttable tab = blkid_partition_get_table(par);
761 dev_t disk = blkid_probe_get_devno(disk_pr);
762
763 if (tab) {
764 v = blkid_parttable_get_type(tab);
765 if (v)
766 blkid_probe_set_value(pr, "PART_ENTRY_SCHEME",
767 (const unsigned char *) v, strlen(v) + 1);
768 }
769
770 v = blkid_partition_get_name(par);
771 if (v)
772 blkid_probe_set_value(pr, "PART_ENTRY_NAME",
773 (const unsigned char *) v, strlen(v) + 1);
774
775 v = blkid_partition_get_uuid(par);
776 if (v)
777 blkid_probe_set_value(pr, "PART_ENTRY_UUID",
778 (const unsigned char *) v, strlen(v) + 1);
779
780 /* type */
781 v = blkid_partition_get_type_string(par);
782 if (v)
783 blkid_probe_set_value(pr, "PART_ENTRY_TYPE",
784 (const unsigned char *) v, strlen(v) + 1);
785 else
786 blkid_probe_sprintf_value(pr, "PART_ENTRY_TYPE",
787 "0x%x", blkid_partition_get_type(par));
788
789 if (blkid_partition_get_flags(par))
790 blkid_probe_sprintf_value(pr, "PART_ENTRY_FLAGS",
791 "0x%llx", blkid_partition_get_flags(par));
792
793 blkid_probe_sprintf_value(pr, "PART_ENTRY_NUMBER",
794 "%d", blkid_partition_get_partno(par));
795
796 blkid_probe_sprintf_value(pr, "PART_ENTRY_OFFSET", "%jd",
797 (intmax_t)blkid_partition_get_start(par));
798 blkid_probe_sprintf_value(pr, "PART_ENTRY_SIZE", "%jd",
799 (intmax_t)blkid_partition_get_size(par));
800
801 blkid_probe_sprintf_value(pr, "PART_ENTRY_DISK", "%u:%u",
802 major(disk), minor(disk));
803 }
804
805 DBG(LOWPROBE, ul_debug("parts: end probing for partition entry [success]"));
806 return BLKID_PROBE_OK;
807
808 nothing:
809 DBG(LOWPROBE, ul_debug("parts: end probing for partition entry [nothing]"));
810 return BLKID_PROBE_NONE;
811
812
813 }
814
815 /*
816 * Returns 1 if the device is whole-disk and the area specified by @offset and
817 * @size is covered by any partition.
818 */
819 int blkid_probe_is_covered_by_pt(blkid_probe pr,
820 uint64_t offset, uint64_t size)
821 {
822 blkid_probe prc = NULL;
823 blkid_partlist ls = NULL;
824 uint64_t start, end;
825 int nparts, i, rc = 0;
826
827 DBG(LOWPROBE, ul_debug(
828 "=> checking if off=%"PRIu64" size=%"PRIu64" covered by PT",
829 offset, size));
830
831 if (pr->flags & BLKID_FL_NOSCAN_DEV)
832 goto done;
833
834 prc = blkid_clone_probe(pr);
835 if (!prc)
836 goto done;
837
838 ls = blkid_probe_get_partitions(prc);
839 if (!ls)
840 goto done;
841
842 nparts = blkid_partlist_numof_partitions(ls);
843 if (!nparts)
844 goto done;
845
846 end = (offset + size) >> 9;
847 start = offset >> 9;
848
849 /* check if the partition table fits into the device */
850 for (i = 0; i < nparts; i++) {
851 blkid_partition par = &ls->parts[i];
852
853 if (par->start + par->size > (pr->size >> 9)) {
854 DBG(LOWPROBE, ul_debug("partition #%d overflows "
855 "device (off=%" PRId64 " size=%" PRId64 ")",
856 par->partno, par->start, par->size));
857 goto done;
858 }
859 }
860
861 /* check if the requested area is covered by PT */
862 for (i = 0; i < nparts; i++) {
863 blkid_partition par = &ls->parts[i];
864
865 if (start >= par->start && end <= par->start + par->size) {
866 rc = 1;
867 break;
868 }
869 }
870 done:
871 blkid_free_probe(prc);
872
873 DBG(LOWPROBE, ul_debug("<= %s covered by PT", rc ? "IS" : "NOT"));
874 return rc;
875 }
876
877 /**
878 * blkid_known_pttype:
879 * @pttype: partition name
880 *
881 * Returns: 1 for known or 0 for unknown partition type.
882 */
883 int blkid_known_pttype(const char *pttype)
884 {
885 size_t i;
886
887 if (!pttype)
888 return 0;
889
890 for (i = 0; i < ARRAY_SIZE(idinfos); i++) {
891 const struct blkid_idinfo *id = idinfos[i];
892 if (strcmp(id->name, pttype) == 0)
893 return 1;
894 }
895 return 0;
896 }
897
898 /**
899 * blkid_partitions_get_name:
900 * @idx: number >= 0
901 * @name: returns name of a supported partition
902 *
903 * Since: 2.30
904 *
905 * Returns: -1 if @idx is out of range, or 0 on success.
906 */
907 int blkid_partitions_get_name(const size_t idx, const char **name)
908 {
909 if (idx < ARRAY_SIZE(idinfos)) {
910 *name = idinfos[idx]->name;
911 return 0;
912 }
913 return -1;
914 }
915
916 /**
917 * blkid_partlist_numof_partitions:
918 * @ls: partitions list
919 *
920 * Returns: number of partitions in the list or -1 in case of error.
921 */
922 int blkid_partlist_numof_partitions(blkid_partlist ls)
923 {
924 return ls->nparts;
925 }
926
927 /**
928 * blkid_partlist_get_table:
929 * @ls: partitions list
930 *
931 * Returns: top-level partition table or NULL if there is not a partition table
932 * on the device.
933 */
934 blkid_parttable blkid_partlist_get_table(blkid_partlist ls)
935 {
936 if (list_empty(&ls->l_tabs))
937 return NULL;
938
939 return list_entry(ls->l_tabs.next,
940 struct blkid_struct_parttable, t_tabs);
941 }
942
943
944 /**
945 * blkid_partlist_get_partition:
946 * @ls: partitions list
947 * @n: partition number in range 0..N, where 'N' is blkid_partlist_numof_partitions().
948 *
949 * It's possible that the list of partitions is *empty*, but there is a valid
950 * partition table on the disk. This happen when on-disk details about
951 * partitions are unknown or the partition table is empty.
952 *
953 * See also blkid_partlist_get_table().
954 *
955 * Returns: partition object or NULL in case or error.
956 */
957 blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n)
958 {
959 if (n < 0 || n >= ls->nparts)
960 return NULL;
961
962 return &ls->parts[n];
963 }
964
965 blkid_partition blkid_partlist_get_partition_by_start(blkid_partlist ls, uint64_t start)
966 {
967 int i, nparts;
968 blkid_partition par;
969
970 nparts = blkid_partlist_numof_partitions(ls);
971 for (i = 0; i < nparts; i++) {
972 par = blkid_partlist_get_partition(ls, i);
973 if ((uint64_t) blkid_partition_get_start(par) == start)
974 return par;
975 }
976 return NULL;
977 }
978
979 /**
980 * blkid_partlist_get_partition_by_partno
981 * @ls: partitions list
982 * @n: the partition number (e.g. 'N' from sda'N')
983 *
984 * This does not assume any order of the input blkid_partlist. And correctly
985 * handles "out of order" partition tables. partition N is located after
986 * partition N+1 on the disk.
987 *
988 * Returns: partition object or NULL in case or error.
989 */
990 blkid_partition blkid_partlist_get_partition_by_partno(blkid_partlist ls, int n)
991 {
992 int i, nparts;
993 blkid_partition par;
994
995 nparts = blkid_partlist_numof_partitions(ls);
996 for (i = 0; i < nparts; i++) {
997 par = blkid_partlist_get_partition(ls, i);
998 if (n == blkid_partition_get_partno(par))
999 return par;
1000 }
1001 return NULL;
1002 }
1003
1004
1005 /**
1006 * blkid_partlist_devno_to_partition:
1007 * @ls: partitions list
1008 * @devno: requested partition
1009 *
1010 * This function tries to get start and size for @devno from sysfs and
1011 * returns a partition from @ls which matches with the values from sysfs.
1012 *
1013 * This function is necessary when you want to make a relation between an entry
1014 * in the partition table (@ls) and block devices in your system.
1015 *
1016 * Returns: partition object or NULL in case or error.
1017 */
1018 blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno)
1019 {
1020 struct path_cxt *pc;
1021 uint64_t start = 0, size;
1022 int i, rc, partno = 0;
1023
1024 DBG(LOWPROBE, ul_debug("trying to convert devno 0x%llx to partition",
1025 (long long) devno));
1026
1027
1028 pc = ul_new_sysfs_path(devno, NULL, NULL);
1029 if (!pc) {
1030 DBG(LOWPROBE, ul_debug("failed t init sysfs context"));
1031 return NULL;
1032 }
1033 rc = ul_path_read_u64(pc, &size, "size");
1034 if (!rc) {
1035 rc = ul_path_read_u64(pc, &start, "start");
1036 if (rc) {
1037 /* try to get partition number from DM uuid.
1038 */
1039 char *uuid = NULL, *tmp, *prefix;
1040
1041 ul_path_read_string(pc, &uuid, "dm/uuid");
1042 tmp = uuid;
1043 prefix = uuid ? strsep(&tmp, "-") : NULL;
1044
1045 if (prefix && strncasecmp(prefix, "part", 4) == 0) {
1046 char *end = NULL;
1047
1048 errno = 0;
1049 partno = strtol(prefix + 4, &end, 10);
1050 if (errno || prefix == end || (end && *end))
1051 partno = 0;
1052 else
1053 rc = 0; /* success */
1054 }
1055 free(uuid);
1056 }
1057 }
1058
1059 ul_unref_path(pc);
1060
1061 if (rc)
1062 return NULL;
1063
1064 if (partno) {
1065 DBG(LOWPROBE, ul_debug("mapped by DM, using partno %d", partno));
1066
1067 /*
1068 * Partition mapped by kpartx does not provide "start" offset
1069 * in /sys, but if we know partno and size of the partition
1070 * that we can probably make the relation between the device
1071 * and an entry in partition table.
1072 */
1073 for (i = 0; i < ls->nparts; i++) {
1074 blkid_partition par = &ls->parts[i];
1075
1076 if (partno != blkid_partition_get_partno(par))
1077 continue;
1078
1079 if (size == (uint64_t)blkid_partition_get_size(par) ||
1080 (blkid_partition_is_extended(par) && size <= 1024ULL))
1081 return par;
1082
1083 }
1084 return NULL;
1085 }
1086
1087 DBG(LOWPROBE, ul_debug("searching by offset/size"));
1088
1089 for (i = 0; i < ls->nparts; i++) {
1090 blkid_partition par = &ls->parts[i];
1091
1092 if ((uint64_t)blkid_partition_get_start(par) == start &&
1093 (uint64_t)blkid_partition_get_size(par) == size)
1094 return par;
1095
1096 /* exception for extended dos partitions */
1097 if ((uint64_t)blkid_partition_get_start(par) == start &&
1098 blkid_partition_is_extended(par) && size <= 1024ULL)
1099 return par;
1100
1101 }
1102
1103 DBG(LOWPROBE, ul_debug("not found partition for device"));
1104 return NULL;
1105 }
1106
1107
1108 int blkid_parttable_set_uuid(blkid_parttable tab, const unsigned char *id)
1109 {
1110 if (!tab)
1111 return -1;
1112
1113 blkid_unparse_uuid(id, tab->id, sizeof(tab->id));
1114 return 0;
1115 }
1116
1117 int blkid_parttable_set_id(blkid_parttable tab, const unsigned char *id)
1118 {
1119 if (!tab)
1120 return -1;
1121
1122 xstrncpy(tab->id, (const char *) id, sizeof(tab->id));
1123 return 0;
1124 }
1125
1126 /* set PTUUID variable for non-binary API */
1127 int blkid_partitions_set_ptuuid(blkid_probe pr, unsigned char *uuid)
1128 {
1129 struct blkid_chain *chn = blkid_probe_get_chain(pr);
1130 struct blkid_prval *v;
1131
1132 if (chn->binary || blkid_uuid_is_empty(uuid, 16))
1133 return 0;
1134
1135 v = blkid_probe_assign_value(pr, "PTUUID");
1136 if (!v)
1137 return -ENOMEM;
1138
1139 v->len = UUID_STR_LEN;
1140 v->data = calloc(1, v->len);
1141 if (v->data) {
1142 blkid_unparse_uuid(uuid, (char *) v->data, v->len);
1143 return 0;
1144 }
1145
1146 blkid_probe_free_value(v);
1147 return -ENOMEM;
1148 }
1149
1150 /* set PTUUID variable for non-binary API for tables where
1151 * the ID is just a string */
1152 int blkid_partitions_strcpy_ptuuid(blkid_probe pr, const char *str)
1153 {
1154 struct blkid_chain *chn = blkid_probe_get_chain(pr);
1155
1156 if (chn->binary || !str || !*str)
1157 return 0;
1158
1159 if (!blkid_probe_set_value(pr, "PTUUID", (unsigned char *) str, strlen(str) + 1))
1160 return -ENOMEM;
1161
1162 return 0;
1163 }
1164
1165 /**
1166 * blkid_parttable_get_id:
1167 * @tab: partition table
1168 *
1169 * The ID is GPT disk UUID or DOS disk ID (in hex format).
1170 *
1171 * Returns: partition table ID (for example GPT disk UUID) or NULL
1172 */
1173 const char *blkid_parttable_get_id(blkid_parttable tab)
1174 {
1175 return *tab->id ? tab->id : NULL;
1176 }
1177
1178
1179 int blkid_partition_set_type(blkid_partition par, int type)
1180 {
1181 par->type = type;
1182 return 0;
1183 }
1184
1185 /**
1186 * blkid_parttable_get_type:
1187 * @tab: partition table
1188 *
1189 * Returns: partition table type (type name, e.g. "dos", "gpt", ...)
1190 */
1191 const char *blkid_parttable_get_type(blkid_parttable tab)
1192 {
1193 return tab->type;
1194 }
1195
1196 /**
1197 * blkid_parttable_get_parent:
1198 * @tab: partition table
1199 *
1200 * Returns: parent for nested partition tables or NULL.
1201 */
1202 blkid_partition blkid_parttable_get_parent(blkid_parttable tab)
1203 {
1204 return tab->parent;
1205 }
1206
1207 /**
1208 * blkid_parttable_get_offset:
1209 * @tab: partition table
1210 *
1211 * Note the position is relative to begin of the device as defined by
1212 * blkid_probe_set_device() for primary partition table, and relative
1213 * to parental partition for nested partition tables.
1214 *
1215 * <informalexample>
1216 * <programlisting>
1217 * off_t offset;
1218 * blkid_partition parent = blkid_parttable_get_parent(tab);
1219 *
1220 * offset = blkid_parttable_get_offset(tab);
1221 *
1222 * if (parent)
1223 * / * 'tab' is nested partition table * /
1224 * offset += blkid_partition_get_start(parent);
1225 * </programlisting>
1226 * </informalexample>
1227
1228 * Returns: position (in bytes) of the partition table or -1 in case of error.
1229 *
1230 */
1231 blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab)
1232 {
1233 return (blkid_loff_t)tab->offset;
1234 }
1235
1236 /**
1237 * blkid_partition_get_table:
1238 * @par: partition
1239 *
1240 * The "parttable" describes partition table. The table is usually the same for
1241 * all partitions -- except nested partition tables.
1242 *
1243 * For example bsd, solaris, etc. use a nested partition table within
1244 * standard primary dos partition:
1245 *
1246 * <informalexample>
1247 * <programlisting>
1248 *
1249 * -- dos partition table
1250 * 0: sda1 dos primary partition
1251 * 1: sda2 dos primary partition
1252 * -- bsd partition table (with in sda2)
1253 * 2: sda5 bds partition
1254 * 3: sda6 bds partition
1255 *
1256 * </programlisting>
1257 * </informalexample>
1258 *
1259 * The library does not to use a separate partition table object for dos logical
1260 * partitions (partitions within extended partition). It's possible to
1261 * differentiate between logical, extended and primary partitions by
1262 *
1263 * blkid_partition_is_{extended,primary,logical}().
1264 *
1265 * Returns: partition table object or NULL in case of error.
1266 */
1267 blkid_parttable blkid_partition_get_table(blkid_partition par)
1268 {
1269 return par->tab;
1270 }
1271
1272 static int partition_get_logical_type(blkid_partition par)
1273 {
1274 blkid_parttable tab;
1275
1276 if (!par)
1277 return -1;
1278
1279 tab = blkid_partition_get_table(par);
1280 if (!tab || !tab->type)
1281 return -1;
1282
1283 if (tab->parent)
1284 return 'L'; /* report nested partitions as logical */
1285
1286 if (!strcmp(tab->type, "dos")) {
1287 if (par->partno > 4)
1288 return 'L'; /* logical */
1289
1290 if(par->type == MBR_DOS_EXTENDED_PARTITION ||
1291 par->type == MBR_W95_EXTENDED_PARTITION ||
1292 par->type == MBR_LINUX_EXTENDED_PARTITION)
1293 return 'E';
1294 }
1295 return 'P';
1296 }
1297
1298 /**
1299 * blkid_partition_is_primary:
1300 * @par: partition
1301 *
1302 * Note, this function returns FALSE for DOS extended partitions and
1303 * all partitions in nested partition tables.
1304 *
1305 * Returns: 1 if the partitions is primary partition or 0 if not.
1306 */
1307 int blkid_partition_is_primary(blkid_partition par)
1308 {
1309 return partition_get_logical_type(par) == 'P' ? TRUE : FALSE;
1310 }
1311
1312 /**
1313 * blkid_partition_is_extended:
1314 * @par: partition
1315 *
1316 * Returns: 1 if the partitions is extended (dos, windows or linux)
1317 * partition or 0 if not.
1318 */
1319 int blkid_partition_is_extended(blkid_partition par)
1320 {
1321 return partition_get_logical_type(par) == 'E' ? TRUE : FALSE;
1322 }
1323
1324 /**
1325 * blkid_partition_is_logical:
1326 * @par: partition
1327 *
1328 * Note that this function returns TRUE for all partitions in all
1329 * nested partition tables (e.g. BSD labels).
1330 *
1331 * Returns: 1 if the partitions is logical partition or 0 if not.
1332 */
1333 int blkid_partition_is_logical(blkid_partition par)
1334 {
1335 return partition_get_logical_type(par) == 'L' ? TRUE : FALSE;
1336 }
1337
1338 static void set_string(unsigned char *item, size_t max,
1339 const unsigned char *data, size_t len)
1340 {
1341 if (len >= max)
1342 len = max - 1;
1343
1344 memcpy(item, data, len);
1345 item[len] = '\0';
1346
1347 blkid_rtrim_whitespace(item);
1348 }
1349
1350 int blkid_partition_set_name(blkid_partition par,
1351 const unsigned char *name, size_t len)
1352 {
1353 if (!par)
1354 return -1;
1355
1356 set_string(par->name, sizeof(par->name), name, len);
1357 return 0;
1358 }
1359
1360 int blkid_partition_set_utf8name(blkid_partition par, const unsigned char *name,
1361 size_t len, int enc)
1362 {
1363 if (!par)
1364 return -1;
1365
1366 ul_encode_to_utf8(enc, par->name, sizeof(par->name), name, len);
1367 blkid_rtrim_whitespace(par->name);
1368 return 0;
1369 }
1370
1371 int blkid_partition_set_uuid(blkid_partition par, const unsigned char *uuid)
1372 {
1373 if (!par)
1374 return -1;
1375
1376 blkid_unparse_uuid(uuid, par->uuid, sizeof(par->uuid));
1377 return 0;
1378 }
1379
1380 int blkid_partition_gen_uuid(blkid_partition par)
1381 {
1382 if (!par || !par->tab || !*par->tab->id)
1383 return -1;
1384
1385 snprintf(par->uuid, sizeof(par->uuid), "%.33s-%02x",
1386 par->tab->id, par->partno);
1387 return 0;
1388 }
1389
1390 /**
1391 * blkid_partition_get_name:
1392 * @par: partition
1393 *
1394 * Returns: partition name string if supported by PT (e.g. Mac) or NULL.
1395 */
1396 const char *blkid_partition_get_name(blkid_partition par)
1397 {
1398 return *par->name ? (char *) par->name : NULL;
1399 }
1400
1401 /**
1402 * blkid_partition_get_uuid:
1403 * @par: partition
1404 *
1405 * Returns: partition UUID string if supported by PT (e.g. GPT) or NULL.
1406 */
1407 const char *blkid_partition_get_uuid(blkid_partition par)
1408 {
1409 return *par->uuid ? par->uuid : NULL;
1410 }
1411
1412 /**
1413 * blkid_partition_get_partno:
1414 * @par: partition
1415 *
1416 * Returns: proposed partition number (e.g. 'N' from sda'N') or -1 in case of
1417 * error. Note that the number is generated by library independently of your OS.
1418 */
1419 int blkid_partition_get_partno(blkid_partition par)
1420 {
1421 return par->partno;
1422 }
1423
1424 /**
1425 * blkid_partition_get_start:
1426 * @par: partition
1427 *
1428 * Be careful if you _not_ probe whole disk:
1429 *
1430 * 1) the offset is usually relative to begin of the disk -- but if you probe a
1431 * fragment of the disk only -- then the offset could be still relative to
1432 * the begin of the disk rather that relative to the fragment.
1433 *
1434 * 2) the offset for nested partitions could be relative to parent (e.g. Solaris)
1435 * _or_ relative to the begin of the whole disk (e.g. bsd).
1436 *
1437 * You don't have to care about such details if you probe whole disk. In such
1438 * a case libblkid always returns the offset relative to the begin of the disk.
1439 *
1440 * Returns: start of the partition (in 512-sectors).
1441 */
1442 blkid_loff_t blkid_partition_get_start(blkid_partition par)
1443 {
1444 return (blkid_loff_t)par->start;
1445 }
1446
1447 /**
1448 * blkid_partition_get_size:
1449 * @par: partition
1450 *
1451 * WARNING: be very careful when you work with MS-DOS extended partitions. The
1452 * library always returns full size of the partition. If you want to
1453 * add the partition to the Linux system (BLKPG_ADD_PARTITION ioctl)
1454 * you need to reduce the size of the partition to 1 or 2 blocks. The
1455 * rest of the partition has to be inaccessible for mkfs or mkswap
1456 * programs, we need a small space for boot loaders only.
1457 *
1458 * For some unknown reason this (safe) practice is not to used for
1459 * nested BSD, Solaris, ..., partition tables in Linux kernel.
1460 *
1461 * Returns: size of the partition (in 512-sectors).
1462 */
1463 blkid_loff_t blkid_partition_get_size(blkid_partition par)
1464 {
1465 return (blkid_loff_t)par->size;
1466 }
1467
1468 /**
1469 * blkid_partition_get_type:
1470 * @par: partition
1471 *
1472 * Returns: partition type.
1473 */
1474 int blkid_partition_get_type(blkid_partition par)
1475 {
1476 return par->type;
1477 }
1478
1479 /* Sets partition 'type' for PT where the type is defined by string rather
1480 * than by number
1481 */
1482 int blkid_partition_set_type_string(blkid_partition par,
1483 const unsigned char *type, size_t len)
1484 {
1485 set_string((unsigned char *) par->typestr,
1486 sizeof(par->typestr), type, len);
1487 return 0;
1488 }
1489
1490 /* Sets partition 'type' for PT where the type is defined by UUID rather
1491 * than by number
1492 */
1493 int blkid_partition_set_type_uuid(blkid_partition par, const unsigned char *uuid)
1494 {
1495 blkid_unparse_uuid(uuid, par->typestr, sizeof(par->typestr));
1496 return 0;
1497 }
1498
1499 /**
1500 * blkid_partition_get_type_string:
1501 * @par: partition
1502 *
1503 * The type string is supported by a small subset of partition tables (e.g. Mac
1504 * and EFI GPT). Note that GPT uses type UUID and this function returns this
1505 * UUID as string.
1506 *
1507 * Returns: partition type string or NULL.
1508 */
1509 const char *blkid_partition_get_type_string(blkid_partition par)
1510 {
1511 return *par->typestr ? par->typestr : NULL;
1512 }
1513
1514
1515 int blkid_partition_set_flags(blkid_partition par, unsigned long long flags)
1516 {
1517 par->flags = flags;
1518 return 0;
1519 }
1520
1521 /**
1522 * blkid_partition_get_flags
1523 * @par: partition
1524 *
1525 * Returns: partition flags (or attributes for gpt).
1526 */
1527 unsigned long long blkid_partition_get_flags(blkid_partition par)
1528 {
1529 return par->flags;
1530 }
1531