From c81e70087cfebc299bdfbbd0675958483fc8a768 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 22 Apr 2010 21:30:04 +0200 Subject: [PATCH] libblkid: fix collision between RAID and PT probing The RAID signature is usually at end of the block device. We have to differentiate between: - RAID signature at the end of disk, and - RAID signature at the end of the last partition The position of the signature is same in both cases... It means we have to the parse partition table and check if the area where is RAID signature is covered by any partition. If yes, then the RAID signature belongs to the partition and has to be ignored during whole-disk probing. The second problem are RAID1 underlaying disks (=raid members). The RAID device could be partitioned, in such a case the partition table is visible from underlaying devices. These partition tables has to be ignored. The libblkid ignores partition tables on raid members now. Note that all these changes are implemented for blkid_do_safeprobe() only. The others functions allow to access all detected superblocks or partition tables. Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=543749 Signed-off-by: Karel Zak --- shlibs/blkid/src/blkidP.h | 8 ++- shlibs/blkid/src/partitions/partitions.c | 80 ++++++++++++++++++++++ shlibs/blkid/src/probe.c | 53 +++++++++++--- shlibs/blkid/src/superblocks/superblocks.c | 36 +++++++++- 4 files changed, 165 insertions(+), 12 deletions(-) diff --git a/shlibs/blkid/src/blkidP.h b/shlibs/blkid/src/blkidP.h index c37f7306..41eba910 100644 --- a/shlibs/blkid/src/blkidP.h +++ b/shlibs/blkid/src/blkidP.h @@ -196,6 +196,7 @@ struct blkid_struct_probe mode_t mode; /* struct stat.sb_mode */ int flags; /* private libray flags */ + int prob_flags; /* always zeroized by blkid_do_*() */ struct list_head buffers; /* list of buffers */ @@ -206,10 +207,12 @@ struct blkid_struct_probe int nvals; /* number of assigned vals */ }; -/* flags */ +/* private flags */ #define BLKID_PRIVATE_FD (1 << 1) /* see blkid_new_probe_from_filename() */ #define BLKID_TINY_DEV (1 << 2) /* <= 1.47MiB (floppy or so) */ #define BLKID_CDROM_DEV (1 << 3) /* is a CD/DVD drive */ +/* private probing flags */ +#define BLKID_PARTS_IGNORE_PT (1 << 1) /* ignore partition table */ /* * Evaluation methods (for blkid_eval_* API) @@ -390,6 +393,9 @@ extern int blkid_probe_set_dimension(blkid_probe pr, extern blkid_partlist blkid_probe_get_partlist(blkid_probe pr); +extern int blkid_probe_is_covered_by_pt(blkid_probe pr, + blkid_loff_t offset, blkid_loff_t size); + extern void blkid_probe_chain_reset_vals(blkid_probe pr, struct blkid_chain *chn); extern int blkid_probe_chain_copy_vals(blkid_probe pr, struct blkid_chain *chn, struct blkid_prval *vals, int nvals); diff --git a/shlibs/blkid/src/partitions/partitions.c b/shlibs/blkid/src/partitions/partitions.c index 293ee86a..9d8f3db7 100644 --- a/shlibs/blkid/src/partitions/partitions.c +++ b/shlibs/blkid/src/partitions/partitions.c @@ -582,6 +582,9 @@ static int partitions_probe(blkid_probe pr, struct blkid_chain *chn) if (chn->binary) partitions_init_data(pr, chn); + if (pr->prob_flags & BLKID_PARTS_IGNORE_PT) + goto details_only; + DBG(DEBUG_LOWPROBE, printf("--> starting probing loop [PARTS idx=%d]\n", chn->idx)); @@ -620,6 +623,7 @@ static int partitions_probe(blkid_probe pr, struct blkid_chain *chn) chn->idx)); } +details_only: /* * Gather PART_ENTRY_* values if the current device is a partition. */ @@ -764,6 +768,82 @@ nothing: return rc; } +/* + * This function is compatible with blkid_probe_get_partitions(), but the + * result is not stored in @pr and all probing is independent on the + * status of @pr. It's possible to call this function from arbitrary + * place without a care about @pr. + */ +static blkid_partlist blkid_probe_get_independent_partlist(blkid_probe pr) +{ + + blkid_partlist ls = NULL, org_ls = NULL; + struct blkid_chain *chn = &pr->chains[BLKID_CHAIN_PARTS]; + struct blkid_prval vals[BLKID_NVALS_PARTS]; + int nvals = BLKID_NVALS_PARTS; + int idx; + + /* save old results */ + nvals = blkid_probe_chain_copy_vals(pr, chn, vals, nvals); + idx = chn->idx; + if (chn->data) { + org_ls = chn->data; + chn->data = NULL; + } + + ls = blkid_probe_get_partitions(pr); + + /* restore original results */ + chn->data = org_ls; + chn->idx = idx; + + blkid_probe_chain_reset_vals(pr, chn); + blkid_probe_append_vals(pr, vals, nvals); + + return ls; +} + +/* + * Returns 1 if the device is whole-disk and the area specified by @offset and + * @size is covered by any partition. + */ +int blkid_probe_is_covered_by_pt(blkid_probe pr, + blkid_loff_t offset, blkid_loff_t size) +{ + blkid_partlist ls = NULL; + blkid_loff_t start, end; + int nparts, i, rc = 0; + + DBG(DEBUG_LOWPROBE, printf( + "=> checking if off=%jd size=%jd covered by PT\n", + offset, size)); + + ls = blkid_probe_get_independent_partlist(pr); + if (!ls) + goto done; + + nparts = blkid_partlist_numof_partitions(ls); + if (!nparts) + goto done; + + end = (offset + size) >> 9; + start = offset >> 9; + + for (i = 0; i < nparts; i++) { + blkid_partition par = &ls->parts[i]; + + if (start >= par->start && end <= par->start + par->size) { + rc = 1; + break; + } + } +done: + partitions_free_data(pr, (void *)ls); + + DBG(DEBUG_LOWPROBE, printf("<= %s covered by PT\n", rc ? "IS" : "NOT")); + return rc; +} + /** * blkid_known_pttype: * @pttype: partiton name diff --git a/shlibs/blkid/src/probe.c b/shlibs/blkid/src/probe.c index 56e66a31..9021a7e8 100644 --- a/shlibs/blkid/src/probe.c +++ b/shlibs/blkid/src/probe.c @@ -290,21 +290,33 @@ struct blkid_chain *blkid_probe_get_chain(blkid_probe pr) void *blkid_probe_get_binary_data(blkid_probe pr, struct blkid_chain *chn) { - int rc; + int rc, org_prob_flags; + struct blkid_chain *org_chn; if (!pr || !chn) return NULL; + /* save the current setting -- the binary API has to be completely + * independent on the current probing status + */ + org_chn = pr->cur_chain; + org_prob_flags = pr->prob_flags; + pr->cur_chain = chn; + pr->prob_flags = 0; chn->binary = TRUE; blkid_probe_chain_reset_position(chn); rc = chn->driver->probe(pr, chn); chn->binary = FALSE; - pr->cur_chain = NULL; blkid_probe_chain_reset_position(chn); + /* restore the original setting + */ + pr->cur_chain = org_chn; + pr->prob_flags = org_prob_flags; + if (rc != 0) return NULL; @@ -679,6 +691,22 @@ int blkid_probe_set_dimension(blkid_probe pr, return 0; } +static inline void blkid_probe_start(blkid_probe pr) +{ + if (pr) { + pr->cur_chain = NULL; + pr->prob_flags = 0; + } +} + +static inline void blkid_probe_end(blkid_probe pr) +{ + if (pr) { + pr->cur_chain = NULL; + pr->prob_flags = 0; + } +} + /** * blkid_do_probe: * @pr: prober @@ -730,9 +758,10 @@ int blkid_do_probe(blkid_probe pr) do { struct blkid_chain *chn = pr->cur_chain; - if (!chn) + if (!chn) { + blkid_probe_start(pr); chn = pr->cur_chain = &pr->chains[0]; - + } /* we go to the next chain only when the previous probing * result was nothing (rc == 1) and when the current chain is * disabled or we are at end of the current chain (chain->idx + @@ -747,8 +776,10 @@ int blkid_do_probe(blkid_probe pr) if (idx < BLKID_NCHAINS) chn = pr->cur_chain = &pr->chains[idx]; - else + else { + blkid_probe_end(pr); return 1; /* all chains already probed */ + } } chn->binary = FALSE; /* for sure... */ @@ -780,7 +811,9 @@ int blkid_do_probe(blkid_probe pr) * * Note about suberblocks chain -- the function does not check for filesystems * when a RAID signature is detected. The function also does not check for - * collision between RAIDs. The first detected RAID is returned. + * collision between RAIDs. The first detected RAID is returned. The function + * checks for collision between partition table and RAID signature -- it's + * recommended to enable partitions chain together with superblocks chain. * * Returns: 0 on success, 1 if nothing is detected, -2 if ambivalen result is * detected and -1 on case of error. @@ -792,6 +825,8 @@ int blkid_do_safeprobe(blkid_probe pr) if (!pr) return -1; + blkid_probe_start(pr); + for (i = 0; i < BLKID_NCHAINS; i++) { struct blkid_chain *chn; @@ -819,7 +854,7 @@ int blkid_do_safeprobe(blkid_probe pr) } done: - pr->cur_chain = NULL; + blkid_probe_end(pr); if (rc < 0) return rc; return count ? 0 : 1; @@ -844,6 +879,8 @@ int blkid_do_fullprobe(blkid_probe pr) if (!pr) return -1; + blkid_probe_start(pr); + for (i = 0; i < BLKID_NCHAINS; i++) { int rc; struct blkid_chain *chn; @@ -872,7 +909,7 @@ int blkid_do_fullprobe(blkid_probe pr) } done: - pr->cur_chain = NULL; + blkid_probe_end(pr); if (rc < 0) return rc; return count ? 0 : 1; diff --git a/shlibs/blkid/src/superblocks/superblocks.c b/shlibs/blkid/src/superblocks/superblocks.c index 6ed0de6e..b46e5963 100644 --- a/shlibs/blkid/src/superblocks/superblocks.c +++ b/shlibs/blkid/src/superblocks/superblocks.c @@ -478,14 +478,17 @@ static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn) int idx = -1; int count = 0; int intol = 0; - int rc; + int rc, bin_org = chn->binary; + + chn->binary = TRUE; while ((rc = superblocks_probe(pr, chn)) == 0) { - if (blkid_probe_is_tiny(pr) && !count) + if (blkid_probe_is_tiny(pr) && !count) { /* floppy or so -- returns the first result. */ + chn->binary = bin_org; return 0; - + } if (!count) { /* save the first result */ nvals = blkid_probe_chain_copy_vals(pr, chn, vals, nvals); @@ -500,6 +503,9 @@ static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn) if (!(idinfos[chn->idx]->flags & BLKID_IDINFO_TOLERANT)) intol++; } + + chn->binary = bin_org; + if (rc < 0) return rc; /* error */ if (count > 1 && intol) { @@ -519,6 +525,30 @@ static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn) superblocks_copy_data(chn->data, sb); chn->idx = idx; + /* + * Check for collisions between RAID and partition table + */ + if (sb && sb->usage == BLKID_USAGE_RAID && + sb->magic_off > pr->size / 2 && + (S_ISREG(pr->mode) || blkid_probe_is_wholedisk(pr)) && + blkid_probe_is_covered_by_pt(pr, sb->magic_off, 0x200)) { + /* + * Ignore the result if the detected RAID superblock is + * within some existing partition (for example RAID on + * the last partition). + */ + blkid_probe_chain_reset_vals(pr, chn); + return 1; + } + + /* + * The RAID device could be partitioned. The problem are RAID1 devices + * where the partition table is visible from underlaying devices. We + * have to ignore such partition tables. + */ + if (sb && sb->usage == BLKID_USAGE_RAID) + pr->prob_flags |= BLKID_PARTS_IGNORE_PT; + return 0; } -- 2.39.5