]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - shlibs/blkid/src/probe.c
build-sys: include <uuid.h> rather than <uuid/uuid.h>
[thirdparty/util-linux.git] / shlibs / blkid / src / probe.c
index a268161a11431243d6fbea3c5bedf32401f5817a..8b8982509553b55eae78c53a448f1f6af2e0348b 100644 (file)
@@ -1,12 +1,89 @@
 /*
- * probe.c - reads tags (LABEL, UUID, FS type, ..) from a block device
+ * Low-level libblkid probing API
  *
- * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
  *
  * This file may be redistributed under the terms of the
  * GNU Lesser General Public License.
  */
 
+/**
+ * SECTION: lowprobe
+ * @title: Low-level probing
+ * @short_description: low-level prober initialization
+ *
+ * The low-level probing routines always and directly read information from
+ * the selected (see blkid_probe_set_device()) device.
+ *
+ * The probing routines are grouped together into separate chains. Currently,
+ * the library provides superblocks, partitions and topology chains.
+ *
+ * The probing routines is possible to filter (enable/disable) by type (e.g.
+ * fstype "vfat" or partype "gpt") or by usage flags (e.g. BLKID_USAGE_RAID).
+ * These filters are per-chain. Note that always when you touch the chain
+ * filter the current probing position is reseted and probing starts from
+ * scratch.  It means that the chain filter should not be modified during
+ * probing, for example in loop where you call blkid_do_probe().
+ *
+ * For more details see the chain specific documentation.
+ *
+ * The low-level API provides two ways how access to probing results.
+ *
+ *   1. The NAME=value (tag) interface. This interface is older and returns all data
+ *      as strings. This interface is generic for all chains.
+ *
+ *   2. The binary interfaces. These interfaces return data in the native formats.
+ *      The interface is always specific to the probing chain.
+ *
+ *  Note that the previous probing result (binary or NAME=value) is always
+ *  zeroized when a chain probing function is called. For example
+ *
+ * <informalexample>
+ *   <programlisting>
+ *     blkid_probe_enable_partitions(pr, TRUE);
+ *     blkid_probe_enable_superblocks(pr, FALSE);
+ *
+ *     blkid_do_safeprobe(pr);
+ *   </programlisting>
+ * </informalexample>
+ *
+ * overwrites the previous probing result for the partitions chain, the superblocks
+ * result is not modified.
+ */
+
+/**
+ * SECTION: lowprobe-tags
+ * @title: Low-level tags
+ * @short_description: generic NAME=value interface.
+ *
+ * The probing routines inside the chain are mutually exclusive by default --
+ * only few probing routines are marked as "tolerant". The "tolerant" probing
+ * routines are used for filesystem which can share the same device with any
+ * other filesystem. The blkid_do_safeprobe() checks for the "tolerant" flag.
+ *
+ * The SUPERBLOCKS chain is enabled by default. The all others chains is
+ * necessary to enable by blkid_probe_enable_'CHAINNAME'(). See chains specific
+ * documentation.
+ *
+ * The blkid_do_probe() function returns a result from only one probing
+ * routine, and the next call from the next probing routine. It means you need
+ * to call the function in loop, for example:
+ *
+ * <informalexample>
+ *   <programlisting>
+ *     while((blkid_do_probe(pr) == 0)
+ *             ... use result ...
+ *   </programlisting>
+ * </informalexample>
+ *
+ * The blkid_do_safeprobe() is the same as blkid_do_probe(), but returns only
+ * first probing result for every enabled chain. This function checks for
+ * ambivalent results (e.g. more "intolerant" filesystems superblocks on the
+ * device).
+ *
+ * The probing result is set of NAME=value pairs (the NAME is always unique).
+ */
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <fcntl.h>
 #include <ctype.h>
 #include <sys/types.h>
+#ifdef HAVE_LINUX_CDROM_H
+#include <linux/cdrom.h>
+#endif
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
-#ifdef HAVE_SYS_MKDEV_H
-#include <sys/mkdev.h>
-#endif
 #ifdef HAVE_ERRNO_H
 #include <errno.h>
 #endif
+#include <inttypes.h>
 #include <stdint.h>
 #include <stdarg.h>
 
 #ifdef HAVE_LIBUUID
-# ifdef HAVE_UUID_UUID_H
-#  include <uuid/uuid.h>
-# else
-#  include <uuid.h>
-# endif
+# include <uuid.h>
 #endif
 
-#include "blkdev.h"
 #include "blkidP.h"
 
-#include "superblocks/superblocks.h"
+/* chains */
+extern const struct blkid_chaindrv superblocks_drv;
+extern const struct blkid_chaindrv topology_drv;
+extern const struct blkid_chaindrv partitions_drv;
 
-static const struct blkid_idinfo *idinfos[] =
-{
-       /* RAIDs */
-       &linuxraid_idinfo,
-       &ddfraid_idinfo,
-       &iswraid_idinfo,
-       &lsiraid_idinfo,
-       &viaraid_idinfo,
-       &silraid_idinfo,
-       &nvraid_idinfo,
-       &pdcraid_idinfo,
-       &highpoint45x_idinfo,
-       &highpoint37x_idinfo,
-       &adraid_idinfo,
-       &jmraid_idinfo,
-       &lvm2_idinfo,
-       &lvm1_idinfo,
-       &snapcow_idinfo,
-       &luks_idinfo,
-
-       /* Filesystems */
-       &vfat_idinfo,
-       &swsuspend_idinfo,
-       &swap_idinfo,
-       &xfs_idinfo,
-       &ext4dev_idinfo,
-       &ext4_idinfo,
-       &ext3_idinfo,
-       &ext2_idinfo,
-       &jbd_idinfo,
-       &reiser_idinfo,
-       &reiser4_idinfo,
-       &jfs_idinfo,
-       &udf_idinfo,
-       &iso9660_idinfo,
-       &zfs_idinfo,
-       &hfsplus_idinfo,
-       &hfs_idinfo,
-       &ufs_idinfo,
-       &hpfs_idinfo,
-       &sysv_idinfo,
-        &xenix_idinfo,
-       &ntfs_idinfo,
-       &cramfs_idinfo,
-       &romfs_idinfo,
-       &minix_idinfo,
-       &gfs_idinfo,
-       &gfs2_idinfo,
-       &ocfs_idinfo,
-       &ocfs2_idinfo,
-       &oracleasm_idinfo,
-       &vxfs_idinfo,
-       &squashfs_idinfo,
-       &netware_idinfo,
-       &btrfs_idinfo
+/*
+ * All supported chains
+ */
+static const struct blkid_chaindrv *chains_drvs[] = {
+       [BLKID_CHAIN_SUBLKS] = &superblocks_drv,
+       [BLKID_CHAIN_TOPLGY] = &topology_drv,
+       [BLKID_CHAIN_PARTS] = &partitions_drv
 };
 
-#ifndef ARRAY_SIZE
-# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
-#endif
+static void blkid_probe_reset_vals(blkid_probe pr);
+static void blkid_probe_reset_buffer(blkid_probe pr);
 
-#define BLKID_FLTR_ITEMS       ARRAY_SIZE(idinfos)
-#define BLKID_FLTR_SIZE                blkid_bmp_nwords(BLKID_FLTR_ITEMS)
-
-static int blkid_probe_set_usage(blkid_probe pr, int usage);
-
-int blkid_known_fstype(const char *fstype)
+/**
+ * blkid_new_probe:
+ *
+ * Returns: a pointer to the newly allocated probe struct.
+ */
+blkid_probe blkid_new_probe(void)
 {
        int i;
+       blkid_probe pr;
 
-       if (!fstype)
-               return 0;
+       blkid_init_debug(0);
+       pr = calloc(1, sizeof(struct blkid_struct_probe));
+       if (!pr)
+               return NULL;
 
-       for (i = 0; i < ARRAY_SIZE(idinfos); i++) {
-               const struct blkid_idinfo *id = idinfos[i];
-               if (strcmp(id->name, fstype) == 0)
-                       return 1;
+       /* initialize chains */
+       for (i = 0; i < BLKID_NCHAINS; i++) {
+               pr->chains[i].driver = chains_drvs[i];
+               pr->chains[i].flags = chains_drvs[i]->dflt_flags;
+               pr->chains[i].enabled = chains_drvs[i]->dflt_enabled;
        }
-       return 0;
+       INIT_LIST_HEAD(&pr->buffers);
+       return pr;
 }
 
-/*
- * Returns a pointer to the newly allocated probe struct
+/**
+ * blkid_new_probe_from_filename:
+ * @filename: device or regular file
+ *
+ * This function is same as call open(filename), blkid_new_probe() and
+ * blkid_probe_set_device(pr, fd, 0, 0).
+ *
+ * The @filename is closed by blkid_free_probe() or by the
+ * blkid_probe_set_device() call.
+ *
+ * Returns: a pointer to the newly allocated probe struct or NULL in case of
+ * error.
  */
-blkid_probe blkid_new_probe(void)
+blkid_probe blkid_new_probe_from_filename(const char *filename)
 {
-       blkid_init_debug(0);
-       return calloc(1, sizeof(struct blkid_struct_probe));
+       int fd = -1;
+       blkid_probe pr = NULL;
+
+       if (!filename)
+               return NULL;
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0)
+               return NULL;
+
+       pr = blkid_new_probe();
+       if (!pr)
+               goto err;
+
+       if (blkid_probe_set_device(pr, fd, 0, 0))
+               goto err;
+
+       pr->flags |= BLKID_PRIVATE_FD;
+       return pr;
+err:
+       if (fd >= 0)
+               close(fd);
+       blkid_free_probe(pr);
+       return NULL;
 }
 
-/*
- * Deallocates probe struct, buffers and all allocated
+/**
+ * blkid_free_probe:
+ * @pr: probe
+ *
+ * Deallocates the probe struct, buffers and all allocated
  * data that are associated with this probing control struct.
  */
 void blkid_free_probe(blkid_probe pr)
 {
+       int i;
+
        if (!pr)
                return;
-       free(pr->fltr);
-       free(pr->buf);
-       free(pr->sbbuf);
+
+       for (i = 0; i < BLKID_NCHAINS; i++) {
+               struct blkid_chain *ch = &pr->chains[i];
+
+               if (ch->driver->free_data)
+                       ch->driver->free_data(pr, ch->data);
+               free(ch->fltr);
+       }
+
+       if ((pr->flags & BLKID_PRIVATE_FD) && pr->fd >= 0)
+               close(pr->fd);
+       blkid_probe_reset_buffer(pr);
        free(pr);
 }
 
+
 /*
  * Removes chain values from probing result.
  */
@@ -167,6 +246,12 @@ void blkid_probe_chain_reset_vals(blkid_probe pr, struct blkid_chain *chn)
        pr->nvals = nvals;
 }
 
+static void blkid_probe_chain_reset_position(struct blkid_chain *chn)
+{
+       if (chn)
+               chn->idx = -1;
+}
+
 /*
  * Copies chain values from probing result to @vals, the max size of @vals is
  * @nvals and returns real number of values.
@@ -199,32 +284,77 @@ void blkid_probe_append_vals(blkid_probe pr, struct blkid_prval *vals, int nvals
        }
 }
 
-
 static void blkid_probe_reset_vals(blkid_probe pr)
 {
        memset(pr->vals, 0, sizeof(pr->vals));
        pr->nvals = 0;
 }
 
-static void blkid_probe_reset_idx(blkid_probe pr)
+struct blkid_chain *blkid_probe_get_chain(blkid_probe pr)
+{
+       return pr->cur_chain;
+}
+
+void *blkid_probe_get_binary_data(blkid_probe pr, struct blkid_chain *chn)
 {
-       pr->idx = -1;
+       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;
+       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;
+
+       DBG(DEBUG_LOWPROBE,
+               printf("returning %s binary data\n", chn->driver->name));
+       return chn->data;
 }
 
+
+/**
+ * blkid_reset_probe:
+ * @pr: probe
+ *
+ * Zeroize probing results and resets the current probing (this has impact to
+ * blkid_do_probe() only). This function does not touch probing filters and
+ * keeps assigned device.
+ */
 void blkid_reset_probe(blkid_probe pr)
 {
+       int i;
+
        if (!pr)
                return;
-       DBG(DEBUG_LOWPROBE, printf("reseting blkid_probe\n"));
-       if (pr->buf)
-               memset(pr->buf, 0, pr->buf_max);
-       pr->buf_off = 0;
-       pr->buf_len = 0;
-       if (pr->sbbuf)
-               memset(pr->sbbuf, 0, BLKID_SB_BUFSIZ);
-       pr->sbbuf_len = 0;
+
+       blkid_probe_reset_buffer(pr);
        blkid_probe_reset_vals(pr);
-       blkid_probe_reset_idx(pr);
+
+       pr->cur_chain = NULL;
+
+       for (i = 0; i < BLKID_NCHAINS; i++)
+               blkid_probe_chain_reset_position(&pr->chains[i]);
 }
 
 /***
@@ -249,7 +379,6 @@ static int blkid_probe_dump_filter(blkid_probe pr, int chain)
                        id->name,
                        blkid_bmp_get_item(chn->fltr, i)
                                ? "disabled" : "enabled <--"));
-
        }
        return 0;
 }
@@ -270,7 +399,7 @@ unsigned long *blkid_probe_get_filter(blkid_probe pr, int chain, int create)
        /* always when you touch the chain filter all indexes are reseted and
         * probing starts from scratch
         */
-       chn->idx = -1;
+       blkid_probe_chain_reset_position(chn);
        pr->cur_chain = NULL;
 
        if (!chn->driver->has_fltr || (!chn->fltr && !create))
@@ -292,16 +421,14 @@ int __blkid_probe_invert_filter(blkid_probe pr, int chain)
 {
        int i;
        struct blkid_chain *chn;
-       unsigned long *fltr;
-
-       fltr = blkid_probe_get_filter(pr, chain, FALSE);
-       if (!fltr)
-               return -1;
 
        chn = &pr->chains[chain];
 
+       if (!chn->driver->has_fltr || !chn->fltr)
+               return -1;
+
        for (i = 0; i < blkid_bmp_nwords(chn->driver->nidinfos); i++)
-               fltr[i] = ~fltr[i];
+               chn->fltr[i] = ~chn->fltr[i];
 
        DBG(DEBUG_LOWPROBE, printf("probing filter inverted\n"));
        /* blkid_probe_dump_filter(pr, chain); */
@@ -352,245 +479,303 @@ int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[
        return 0;
 }
 
-/*
- * Note that we have two offsets:
- *
- *     1/ general device offset (pr->off), that's useful for example when we
- *        probe a partition from whole disk image:
- *                    blkid-low --offset  <partition_position> disk.img
- *
- *     2/ buffer offset (the 'off' argument), that useful for offsets in
- *        superbloks, ...
- *
- *     That means never use lseek(fd, 0, SEEK_SET), the zero position is always
- *     pr->off, so lseek(fd, pr->off, SEEK_SET).
- *
- */
 unsigned char *blkid_probe_get_buffer(blkid_probe pr,
                                blkid_loff_t off, blkid_loff_t len)
 {
-       ssize_t ret_read = 0;
+       struct list_head *p;
+       struct blkid_bufinfo *bf = NULL;
 
-       if (off < 0 || len < 0) {
-               DBG(DEBUG_LOWPROBE,
-                       printf("unexpected offset or length of buffer requested\n"));
+       if (pr->size <= 0)
                return NULL;
-       }
-       if (off + len <= BLKID_SB_BUFSIZ) {
-               if (!pr->sbbuf) {
-                       pr->sbbuf = malloc(BLKID_SB_BUFSIZ);
-                       if (!pr->sbbuf)
-                               return NULL;
-               }
-               if (!pr->sbbuf_len) {
-                       if (lseek(pr->fd, pr->off, SEEK_SET) < 0)
-                               return NULL;
-                       ret_read = read(pr->fd, pr->sbbuf, BLKID_SB_BUFSIZ);
-                       if (ret_read < 0)
-                               ret_read = 0;
-                       pr->sbbuf_len = ret_read;
+
+       list_for_each(p, &pr->buffers) {
+               struct blkid_bufinfo *x =
+                               list_entry(p, struct blkid_bufinfo, bufs);
+
+               if (x->off <= off && off + len <= x->off + x->len) {
+                       DBG(DEBUG_LOWPROBE,
+                               printf("\treuse buffer: off=%jd len=%jd\n",
+                                                       x->off, x->len));
+                       bf = x;
+                       break;
                }
-               if (off + len > pr->sbbuf_len)
+       }
+       if (!bf) {
+               ssize_t ret;
+
+               if (blkid_llseek(pr->fd, pr->off + off, SEEK_SET) < 0)
                        return NULL;
-               return pr->sbbuf + off;
-       } else {
-               unsigned char *newbuf = NULL;
-
-               if (len > pr->buf_max) {
-                       newbuf = realloc(pr->buf, len);
-                       if (!newbuf)
-                               return NULL;
-                       pr->buf = newbuf;
-                       pr->buf_max = len;
-                       pr->buf_off = 0;
-                       pr->buf_len = 0;
-               }
-               if (newbuf || off < pr->buf_off ||
-                   off + len > pr->buf_off + pr->buf_len) {
 
-                       if (blkid_llseek(pr->fd, pr->off + off, SEEK_SET) < 0)
-                               return NULL;
+               /* allocate info and space for data by why call */
+               bf = calloc(1, sizeof(struct blkid_bufinfo) + len);
+               if (!bf)
+                       return NULL;
+
+               bf->data = ((unsigned char *) bf) + sizeof(struct blkid_bufinfo);
+               bf->len = len;
+               bf->off = off;
+               INIT_LIST_HEAD(&bf->bufs);
+
+               DBG(DEBUG_LOWPROBE,
+                       printf("\tbuffer read: off=%jd len=%jd\n", off, len));
 
-                       ret_read = read(pr->fd, pr->buf, len);
-                       if (ret_read != (ssize_t) len)
-                               return NULL;
-                       pr->buf_off = off;
-                       pr->buf_len = len;
+               ret = read(pr->fd, bf->data, len);
+               if (ret != (ssize_t) len) {
+                       free(bf);
+                       return NULL;
                }
-               return off ? pr->buf + (off - pr->buf_off) : pr->buf;
+               list_add_tail(&bf->bufs, &pr->buffers);
+       }
+
+       return off ? bf->data + (off - bf->off) : bf->data;
+}
+
+
+static void blkid_probe_reset_buffer(blkid_probe pr)
+{
+       uint64_t read_ct = 0, len_ct = 0;
+
+       if (!pr || list_empty(&pr->buffers))
+               return;
+
+       DBG(DEBUG_LOWPROBE, printf("reseting probing buffers\n"));
+
+       while (!list_empty(&pr->buffers)) {
+               struct blkid_bufinfo *bf = list_entry(pr->buffers.next,
+                                               struct blkid_bufinfo, bufs);
+
+               read_ct++;
+               len_ct += bf->len;
+               list_del(&bf->bufs);
+               free(bf);
        }
+
+       DBG(DEBUG_LOWPROBE,
+               printf("buffers summary: %"PRIu64" bytes "
+                       "by %"PRIu64" read() call(s)\n",
+                       len_ct, read_ct));
+
+       INIT_LIST_HEAD(&pr->buffers);
 }
 
 /*
- * Assignes the device to probe control struct, resets internal buffers and
- * reads 512 bytes from device to the buffers.
+ * Small devices need a special care.
+ */
+int blkid_probe_is_tiny(blkid_probe pr)
+{
+       return pr && (pr->flags & BLKID_TINY_DEV);
+}
+
+/*
+ * CDROMs may fail when probed for RAID (last sector problem)
+ */
+int blkid_probe_is_cdrom(blkid_probe pr)
+{
+       return pr && (pr->flags & BLKID_CDROM_DEV);
+}
+
+/**
+ * blkid_probe_set_device:
+ * @pr: probe
+ * @fd: device file descriptor
+ * @off: begin of probing area
+ * @size: size of probing area (zero means whole device/file)
  *
- * Returns -1 in case of failure, or 0 on success.
+ * Assigns the device to probe control struct, resets internal buffers and
+ * resets the current probing.
+ *
+ * Returns: -1 in case of failure, or 0 on success.
  */
 int blkid_probe_set_device(blkid_probe pr, int fd,
                blkid_loff_t off, blkid_loff_t size)
 {
+       struct stat sb;
+
        if (!pr)
                return -1;
 
        blkid_reset_probe(pr);
 
+       if ((pr->flags & BLKID_PRIVATE_FD) && pr->fd >= 0)
+               close(pr->fd);
+
+       pr->flags &= ~BLKID_PRIVATE_FD;
+       pr->flags &= ~BLKID_TINY_DEV;
+       pr->flags &= ~BLKID_CDROM_DEV;
        pr->fd = fd;
        pr->off = off;
        pr->size = 0;
+       pr->devno = 0;
+       pr->mode = 0;
+       pr->blkssz = 0;
+
+#if defined(POSIX_FADV_RANDOM) && defined(HAVE_POSIX_FADVISE)
+       /* Disable read-ahead */
+       posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM);
+#endif
+       if (fstat(fd, &sb))
+               goto err;
+
+       if (!S_ISBLK(sb.st_mode) && !S_ISCHR(sb.st_mode) && !S_ISREG(sb.st_mode))
+               goto err;
+
+       pr->mode = sb.st_mode;
+       if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode))
+               pr->devno = sb.st_rdev;
 
        if (size)
                pr->size = size;
        else {
-               struct stat sb;
+               if (S_ISBLK(sb.st_mode)) {
+                       if (blkdev_get_size(fd, (unsigned long long *) &pr->size)) {
+                               DBG(DEBUG_LOWPROBE, printf(
+                                       "failed to get device size\n"));
+                               goto err;
+                       }
+               } else if (S_ISCHR(sb.st_mode))
+                       pr->size = 1;           /* UBI devices are char... */
+               else if (S_ISREG(sb.st_mode))
+                       pr->size = sb.st_size;  /* regular file */
 
-               if (fstat(fd, &sb))
-                       return -1;
+               if (pr->off > pr->size)
+                       goto err;
 
-               if (S_ISBLK(sb.st_mode))
-                       blkdev_get_size(fd, (unsigned long long *) &pr->size);
-               else
-                       pr->size = sb.st_size;
+               /* The probing area cannot be larger than whole device, pr->off
+                * is offset within the device */
+               pr->size -= pr->off;
        }
-       if (!pr->size)
-               return -1;
 
-       /* read SB to test if the device is readable */
-       if (!blkid_probe_get_buffer(pr, 0, 0x200)) {
-               DBG(DEBUG_LOWPROBE,
-                       printf("failed to prepare a device for low-probing\n"));
-               return -1;
-       }
+       if (pr->size <= 1440 * 1024 && !S_ISCHR(sb.st_mode))
+               pr->flags |= BLKID_TINY_DEV;
 
-       DBG(DEBUG_LOWPROBE, printf("ready for low-probing, offset=%zd, size=%zd\n",
+#ifdef CDROM_GET_CAPABILITY
+       if (S_ISBLK(sb.st_mode) && ioctl(fd, CDROM_GET_CAPABILITY, NULL) >= 0)
+               pr->flags |= BLKID_CDROM_DEV;
+#endif
+
+       DBG(DEBUG_LOWPROBE, printf("ready for low-probing, offset=%jd, size=%jd\n",
                                pr->off, pr->size));
+       DBG(DEBUG_LOWPROBE, printf("whole-disk: %s, regfile: %s\n",
+               blkid_probe_is_wholedisk(pr) ?"YES" : "NO",
+               S_ISREG(pr->mode) ? "YES" : "NO"));
+
        return 0;
+err:
+       DBG(DEBUG_LOWPROBE,
+               printf("failed to prepare a device for low-probing\n"));
+       return -1;
+
 }
 
-int blkid_probe_set_request(blkid_probe pr, int flags)
+int blkid_probe_get_dimension(blkid_probe pr,
+               blkid_loff_t *off, blkid_loff_t *size)
 {
        if (!pr)
                return -1;
-       pr->probreq = flags;
+
+       *off = pr->off;
+       *size = pr->size;
        return 0;
 }
 
-int blkid_probe_reset_filter(blkid_probe pr)
+int blkid_probe_set_dimension(blkid_probe pr,
+               blkid_loff_t off, blkid_loff_t size)
 {
        if (!pr)
                return -1;
-       if (pr->fltr)
-               memset(pr->fltr, 0, BLKID_FLTR_SIZE * sizeof(unsigned long));
-       blkid_probe_reset_idx(pr);
-       return 0;
-}
 
-/*
- * flag:
- *
- *  BLKID_FLTR_NOTIN  - probe all filesystems which are NOT IN names[]
- *
- *  BLKID_FLTR_ONLYIN - probe filesystem which are IN names[]
- */
-int blkid_probe_filter_types(blkid_probe pr, int flag, char *names[])
-{
-       int i;
+       DBG(DEBUG_LOWPROBE, printf(
+               "changing probing area: size=%llu, off=%llu "
+               "-to-> size=%llu, off=%llu\n",
+               (unsigned long long) pr->size,
+               (unsigned long long) pr->off,
+               (unsigned long long) size,
+               (unsigned long long) off));
 
-       if (!pr || !names)
-               return -1;
-       if (!pr->fltr) {
-               pr->fltr = calloc(BLKID_FLTR_SIZE, sizeof(unsigned long));
-               blkid_probe_reset_idx(pr);
-       } else
-               blkid_probe_reset_filter(pr);
+       pr->off = off;
+       pr->size = size;
+       pr->flags &= ~BLKID_TINY_DEV;
 
-       if (!pr->fltr)
-               return -1;
+       if (pr->size <= 1440 * 1024 && !S_ISCHR(pr->mode))
+               pr->flags |= BLKID_TINY_DEV;
 
-       for (i = 0; i < ARRAY_SIZE(idinfos); i++) {
-               int has = 0;
-               const struct blkid_idinfo *id = idinfos[i];
-               char **n;
+       blkid_probe_reset_buffer(pr);
 
-               for (n = names; *n; n++) {
-                       if (!strcmp(id->name, *n)) {
-                               has = 1;
-                               break;
-                       }
-               }
-               /* The default is enable all filesystems,
-                * set relevant bitmap bit means disable the filesystem.
-                */
-               if (flag & BLKID_FLTR_ONLYIN) {
-                      if (!has)
-                               blkid_bmp_set_item(pr->fltr, i);
-               } else if (flag & BLKID_FLTR_NOTIN) {
-                       if (has)
-                               blkid_bmp_set_item(pr->fltr, i);
-               }
-       }
-       DBG(DEBUG_LOWPROBE, printf("a new probing type-filter initialized\n"));
        return 0;
 }
 
-/*
- * flag:
- *
- *  BLKID_FLTR_NOTIN  - probe all filesystems which are NOT IN "usage"
- *
- *  BLKID_FLTR_ONLYIN - probe filesystem which are IN "usage"
- *
- * where the "usage" is a set of filesystem according the usage flag (crypto,
- * raid, filesystem, ...)
- */
-int blkid_probe_filter_usage(blkid_probe pr, int flag, int usage)
+int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
+                       blkid_loff_t *offset, const struct blkid_idmag **res)
 {
-       int i;
+       const struct blkid_idmag *mag = NULL;
+       blkid_loff_t off = 0;
 
-       if (!pr || !usage)
-               return -1;
-       if (!pr->fltr) {
-               pr->fltr = calloc(BLKID_FLTR_SIZE, sizeof(unsigned long));
-               blkid_probe_reset_idx(pr);
-       } else
-               blkid_probe_reset_filter(pr);
+       if (id)
+               mag = id->magics ? &id->magics[0] : NULL;
+       if (res)
+               *res = NULL;
 
-       if (!pr->fltr)
-               return -1;
+       /* try to detect by magic string */
+       while(mag && mag->magic) {
+               unsigned char *buf;
 
-       for (i = 0; i < ARRAY_SIZE(idinfos); i++) {
-               const struct blkid_idinfo *id = idinfos[i];
+               off = (mag->kboff + (mag->sboff >> 10)) << 10;
+               buf = blkid_probe_get_buffer(pr, off, 1024);
 
-               if (id->usage & usage) {
-                       if (flag & BLKID_FLTR_NOTIN)
-                               blkid_bmp_set_item(pr->fltr, i);
-               } else if (flag & BLKID_FLTR_ONLYIN)
-                       blkid_bmp_set_item(pr->fltr, i);
+               if (buf && !memcmp(mag->magic,
+                               buf + (mag->sboff & 0x3ff), mag->len)) {
+                       DBG(DEBUG_LOWPROBE, printf(
+                               "\tmagic sboff=%u, kboff=%ld\n",
+                               mag->sboff, mag->kboff));
+                       if (offset)
+                               *offset = off + (mag->sboff & 0x3ff);
+                       if (res)
+                               *res = mag;
+                       return 0;
+               }
+               mag++;
        }
-       DBG(DEBUG_LOWPROBE, printf("a new probing usage-filter initialized\n"));
+
+       if (id->magics && id->magics[0].magic)
+               /* magic string(s) defined, but not found */
+               return 1;
+
        return 0;
 }
 
-
-int blkid_probe_invert_filter(blkid_probe pr)
+static inline void blkid_probe_start(blkid_probe pr)
 {
-       int i;
-
-       if (!pr || !pr->fltr)
-               return -1;
-       for (i = 0; i < BLKID_FLTR_SIZE; i++)
-               pr->fltr[i] = ~pr->fltr[i];
+       if (pr) {
+               pr->cur_chain = NULL;
+               pr->prob_flags = 0;
+               blkid_probe_set_wiper(pr, 0, 0);
+       }
+}
 
-       blkid_probe_reset_idx(pr);
-       DBG(DEBUG_LOWPROBE, printf("probing filter inverted\n"));
-       return 0;
+static inline void blkid_probe_end(blkid_probe pr)
+{
+       if (pr) {
+               pr->cur_chain = NULL;
+               pr->prob_flags = 0;
+               blkid_probe_set_wiper(pr, 0, 0);
+       }
 }
 
-/*
- * The blkid_do_probe() calls the probe functions. This routine could be used
- * in a loop when you need to probe for all possible filesystems/raids.
+/**
+ * blkid_do_probe:
+ * @pr: prober
  *
- * 1/ basic case -- use the first result:
+ * Calls probing functions in all enabled chains. The superblocks chain is
+ * enabled by default. The blkid_do_probe() stores result from only one
+ * probing function. It's necessary to call this routine in a loop to get
+ * results from all probing functions in all chains. The probing is reseted
+ * by blkid_reset_probe() or by filter functions.
+ *
+ * This is string-based NAME=value interface only.
+ *
+ * <example>
+ *   <title>basic case - use the first result only</title>
+ *   <programlisting>
  *
  *     if (blkid_do_probe(pr) == 0) {
  *             int nvals = blkid_probe_numof_values(pr);
@@ -599,159 +784,196 @@ int blkid_probe_invert_filter(blkid_probe pr)
  *                             printf("%s = %s\n", name, data);
  *             }
  *     }
+ *  </programlisting>
+ * </example>
  *
- * 2/ advanced case -- probe for all signatures (don't forget that some
- *                     filesystems can co-exist on one volume (e.g. CD-ROM).
+ * <example>
+ *   <title>advanced case - probe for all signatures</title>
+ *   <programlisting>
  *
  *     while (blkid_do_probe(pr) == 0) {
  *             int nvals = blkid_probe_numof_values(pr);
  *             ...
  *     }
+ *  </programlisting>
+ * </example>
  *
- *    The internal probing index (pointer to the last probing function) is
- *    always reseted when you touch probing filter or set a new device. It
- *    means you cannot use:
+ * See also blkid_reset_probe().
  *
- *      blkid_probe_invert_filter()
- *      blkid_probe_filter_usage()
- *      blkid_probe_filter_types()
- *      blkid_probe_reset_filter()
- *      blkid_probe_set_device()
- *
- *    in the loop (e.g while()) when you iterate on all signatures.
+ * Returns: 0 on success, 1 when probing is done and -1 in case of error.
  */
 int blkid_do_probe(blkid_probe pr)
 {
-       int i = 0;
+       int rc = 1;
 
-       if (!pr || pr->idx < -1)
+       if (!pr)
                return -1;
 
-       blkid_probe_reset_vals(pr);
+       do {
+               struct blkid_chain *chn = pr->cur_chain;
 
-       DBG(DEBUG_LOWPROBE,
-               printf("--> starting probing loop [idx=%d]\n",
-               pr->idx));
+               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 +
+                * 1 == sizeof chain) or the current chain bailed out right at
+                * the start (chain->idx == -1)
+                */
+               else if (rc == 1 && (chn->enabled == FALSE ||
+                                    chn->idx + 1 == chn->driver->nidinfos ||
+                                    chn->idx == -1)) {
+
+                       int idx = chn->driver->id + 1;
 
-       i = pr->idx + 1;
+                       if (idx < BLKID_NCHAINS)
+                               chn = pr->cur_chain = &pr->chains[idx];
+                       else {
+                               blkid_probe_end(pr);
+                               return 1;       /* all chains already probed */
+                       }
+               }
 
-       for ( ; i < ARRAY_SIZE(idinfos); i++) {
-               const struct blkid_idinfo *id;
-               const struct blkid_idmag *mag;
-               int hasmag = 0;
+               chn->binary = FALSE;            /* for sure... */
 
-               pr->idx = i;
+               DBG(DEBUG_LOWPROBE, printf("chain probe %s %s (idx=%d)\n",
+                               chn->driver->name,
+                               chn->enabled? "ENABLED" : "DISABLED",
+                               chn->idx));
 
-               if (pr->fltr && blkid_bmp_get_item(pr->fltr, i))
+               if (!chn->enabled)
                        continue;
 
-               id = idinfos[i];
-               mag = id->magics ? &id->magics[0] : NULL;
+               /* rc: -1 = error, 0 = success, 1 = no result */
+               rc = chn->driver->probe(pr, chn);
+
+       } while (rc == 1);
+
+       return rc;
+}
 
-               /* try to detect by magic string */
-               while(mag && mag->magic) {
-                       int idx;
-                       unsigned char *buf;
+/**
+ * blkid_do_safeprobe:
+ * @pr: prober
+ *
+ * This function gathers probing results from all enabled chains and checks
+ * for ambivalent results (e.g. more filesystems on the device).
+ *
+ * This is string-based NAME=value interface only.
+ *
+ * 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. 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.
+ */
+int blkid_do_safeprobe(blkid_probe pr)
+{
+       int i, count = 0, rc = 0;
 
-                       idx = mag->kboff + (mag->sboff >> 10);
-                       buf = blkid_probe_get_buffer(pr, idx << 10, 1024);
+       if (!pr)
+               return -1;
 
-                       if (buf && !memcmp(mag->magic,
-                                       buf + (mag->sboff & 0x3ff), mag->len)) {
-                               DBG(DEBUG_LOWPROBE, printf(
-                                       "%s: magic sboff=%u, kboff=%ld\n",
-                                       id->name, mag->sboff, mag->kboff));
-                               hasmag = 1;
-                               break;
-                       }
-                       mag++;
-               }
+       blkid_probe_start(pr);
 
-               if (hasmag == 0 && id->magics && id->magics[0].magic)
-                       /* magic string(s) defined, but not found */
+       for (i = 0; i < BLKID_NCHAINS; i++) {
+               struct blkid_chain *chn;
+
+               chn = pr->cur_chain = &pr->chains[i];
+               chn->binary = FALSE;            /* for sure... */
+
+               DBG(DEBUG_LOWPROBE, printf("chain safeprobe %s %s\n",
+                               chn->driver->name,
+                               chn->enabled? "ENABLED" : "DISABLED"));
+
+               if (!chn->enabled)
                        continue;
 
-               /* final check by probing function */
-               if (id->probefunc) {
-                       DBG(DEBUG_LOWPROBE, printf(
-                               "%s: call probefunc()\n", id->name));
-                       if (id->probefunc(pr, mag) != 0)
-                               continue;
-               }
+               blkid_probe_chain_reset_position(chn);
 
-               /* all cheks passed */
-               if (pr->probreq & BLKID_PROBREQ_TYPE)
-                       blkid_probe_set_value(pr, "TYPE",
-                               (unsigned char *) id->name,
-                               strlen(id->name) + 1);
-               if (pr->probreq & BLKID_PROBREQ_USAGE)
-                       blkid_probe_set_usage(pr, id->usage);
+               rc = chn->driver->safeprobe(pr, chn);
 
-               DBG(DEBUG_LOWPROBE,
-                       printf("<-- leaving probing loop (type=%s) [idx=%d]\n",
-                       id->name, pr->idx));
-               return 0;
+               blkid_probe_chain_reset_position(chn);
+
+               /* rc: -2 ambivalent, -1 = error, 0 = success, 1 = no result */
+               if (rc < 0)
+                       goto done;      /* error */
+               if (rc == 0)
+                       count++;        /* success */
        }
-       DBG(DEBUG_LOWPROBE,
-               printf("<-- leaving probing loop (failed) [idx=%d]\n",
-               pr->idx));
-       return 1;
+
+done:
+       blkid_probe_end(pr);
+       if (rc < 0)
+               return rc;
+       return count ? 0 : 1;
 }
 
-/*
- * This is the same function as blkid_do_probe(), but returns only one result
- * (cannot be used in while()) and checks for ambivalen results (more
- * filesystems on the device) -- in such case returns -2.
+/**
+ * blkid_do_fullprobe:
+ * @pr: prober
  *
- * 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.
+ * This function gathers probing results from all enabled chains. Same as
+ * blkid_so_safeprobe() but does not check for collision between probing
+ * result.
+ *
+ * This is string-based NAME=value interface only.
+ *
+ * Returns: 0 on success, 1 if nothing is detected or -1 on case of error.
  */
-int blkid_do_safeprobe(blkid_probe pr)
+int blkid_do_fullprobe(blkid_probe pr)
 {
-       struct blkid_struct_probe first;
-       int count = 0;
-       int intol = 0;
-       int rc;
+       int i, count = 0, rc = 0;
 
-       while ((rc = blkid_do_probe(pr)) == 0) {
-               if (!count) {
-                       /* store the fist result */
-                       memcpy(first.vals, pr->vals, sizeof(first.vals));
-                       first.nvals = pr->nvals;
-                       first.idx = pr->idx;
-               }
-               count++;
+       if (!pr)
+               return -1;
 
-               if (idinfos[pr->idx]->usage & BLKID_USAGE_RAID)
-                       break;
-               if (!(idinfos[pr->idx]->flags & BLKID_IDINFO_TOLERANT))
-                       intol++;
-       }
-       if (rc < 0)
-               return rc;              /* error */
-       if (count > 1 && intol) {
-               DBG(DEBUG_LOWPROBE,
-                       printf("ERROR: ambivalent result detected (%d filesystems)!\n",
-                       count));
-               return -2;              /* error, ambivalent result (more FS) */
-       }
-       if (!count)
-               return 1;               /* nothing detected */
+       blkid_probe_start(pr);
 
-       /* restore the first result */
-       memcpy(pr->vals, first.vals, sizeof(first.vals));
-       pr->nvals = first.nvals;
-       pr->idx = first.idx;
+       for (i = 0; i < BLKID_NCHAINS; i++) {
+               int rc;
+               struct blkid_chain *chn;
 
-       return 0;
+               chn = pr->cur_chain = &pr->chains[i];
+               chn->binary = FALSE;            /* for sure... */
+
+               DBG(DEBUG_LOWPROBE, printf("chain fullprobe %s: %s\n",
+                               chn->driver->name,
+                               chn->enabled? "ENABLED" : "DISABLED"));
+
+               if (!chn->enabled)
+                       continue;
+
+               blkid_probe_chain_reset_position(chn);
+
+               rc = chn->driver->probe(pr, chn);
+
+               blkid_probe_chain_reset_position(chn);
+
+               /* rc: -1 = error, 0 = success, 1 = no result */
+               if (rc < 0)
+                       goto done;      /* error */
+               if (rc == 0)
+                       count++;        /* success */
+       }
+
+done:
+       blkid_probe_end(pr);
+       if (rc < 0)
+               return rc;
+       return count ? 0 : 1;
 }
 
-int blkid_probe_numof_values(blkid_probe pr)
+/* same sa blkid_probe_get_buffer() but works with 512-sectors */
+unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector)
 {
-       if (!pr)
-               return -1;
-       return pr->nvals;
+       return pr ? blkid_probe_get_buffer(pr,
+                       ((blkid_loff_t) sector) << 9, 0x200) : NULL;
 }
 
 struct blkid_prval *blkid_probe_assign_value(
@@ -774,6 +996,25 @@ struct blkid_prval *blkid_probe_assign_value(
        return v;
 }
 
+int blkid_probe_reset_last_value(blkid_probe pr)
+{
+       struct blkid_prval *v;
+
+       if (pr == NULL || pr->nvals == 0)
+               return -1;
+
+       v = &pr->vals[pr->nvals - 1];
+
+       DBG(DEBUG_LOWPROBE,
+               printf("un-assigning %s [%s]\n", v->name, v->chain->driver->name));
+
+       memset(v, 0, sizeof(struct blkid_prval));
+       pr->nvals--;
+
+       return 0;
+
+}
+
 int blkid_probe_set_value(blkid_probe pr, const char *name,
                unsigned char *data, size_t len)
 {
@@ -804,214 +1045,184 @@ int blkid_probe_vsprintf_value(blkid_probe pr, const char *name,
        len = vsnprintf((char *) v->data, sizeof(v->data), fmt, ap);
 
        if (len <= 0) {
-               pr->nvals--; /* reset the latest assigned value */
+               blkid_probe_reset_last_value(pr);
                return -1;
        }
        v->len = len + 1;
        return 0;
 }
 
-int blkid_probe_set_version(blkid_probe pr, const char *version)
+int blkid_probe_sprintf_value(blkid_probe pr, const char *name,
+               const char *fmt, ...)
 {
-       if (pr->probreq & BLKID_PROBREQ_VERSION)
-               return blkid_probe_set_value(pr, "VERSION",
-                          (unsigned char *) version, strlen(version) + 1);
-       return 0;
-}
-
-int blkid_probe_sprintf_version(blkid_probe pr, const char *fmt, ...)
-{
-       int rc = 0;
+       int rc;
+       va_list ap;
 
-       if (pr->probreq & BLKID_PROBREQ_VERSION) {
-               va_list ap;
+       va_start(ap, fmt);
+       rc = blkid_probe_vsprintf_value(pr, name, fmt, ap);
+       va_end(ap);
 
-               va_start(ap, fmt);
-               rc = blkid_probe_vsprintf_value(pr, "VERSION", fmt, ap);
-               va_end(ap);
-       }
        return rc;
 }
 
-static int blkid_probe_set_usage(blkid_probe pr, int usage)
+/**
+ * blkid_probe_get_devno:
+ * @pr: probe
+ *
+ * Returns: block device number, or 0 for regilar files.
+ */
+dev_t blkid_probe_get_devno(blkid_probe pr)
 {
-       char *u = NULL;
-
-       if (usage & BLKID_USAGE_FILESYSTEM)
-               u = "filesystem";
-       else if (usage & BLKID_USAGE_RAID)
-               u = "raid";
-       else if (usage & BLKID_USAGE_CRYPTO)
-               u = "crypto";
-       else if (usage & BLKID_USAGE_OTHER)
-               u = "other";
-       else
-               u = "unknown";
-
-       return blkid_probe_set_value(pr, "USAGE", (unsigned char *) u, strlen(u) + 1);
+       return pr->devno;
 }
 
-int blkid_probe_set_label(blkid_probe pr, unsigned char *label, size_t len)
+/**
+ * blkid_probe_get_wholedisk_devno:
+ * @pr: probe
+ *
+ * Returns: device number of the wholedisk, or 0 for regilar files.
+ */
+dev_t blkid_probe_get_wholedisk_devno(blkid_probe pr)
 {
-       struct blkid_prval *v;
-       int i;
-
-       if (len > BLKID_PROBVAL_BUFSIZ)
-               len = BLKID_PROBVAL_BUFSIZ;
-
-       if ((pr->probreq & BLKID_PROBREQ_LABELRAW) &&
-           blkid_probe_set_value(pr, "LABEL_RAW", label, len) < 0)
-               return -1;
-       if (!(pr->probreq & BLKID_PROBREQ_LABEL))
-               return 0;
-       v = blkid_probe_assign_value(pr, "LABEL");
-       if (!v)
-               return -1;
+       if (!pr->disk_devno) {
+               dev_t devno, disk_devno = 0;
 
-       memcpy(v->data, label, len);
-       v->data[len] = '\0';
+               devno = blkid_probe_get_devno(pr);
+               if (!devno)
+                       return 0;
 
-       /* remove trailing whitespace */
-       i = strnlen((char *) v->data, len);
-       while (i--) {
-               if (!isspace(v->data[i]))
-                       break;
+                if (blkid_devno_to_wholedisk(devno, NULL, 0, &disk_devno) == 0)
+                       pr->disk_devno = disk_devno;
        }
-       v->data[++i] = '\0';
-       v->len = i + 1;
-       return 0;
+       return pr->disk_devno;
 }
 
-int blkid_probe_set_utf8label(blkid_probe pr, unsigned char *label,
-                               size_t len, int enc)
+/**
+ * blkid_probe_is_wholedisk:
+ * @pr: probe
+ *
+ * Returns: 1 if the device is whole-disk or 0.
+ */
+int blkid_probe_is_wholedisk(blkid_probe pr)
 {
-       struct blkid_prval *v;
+       dev_t devno, disk_devno;
 
-       if ((pr->probreq & BLKID_PROBREQ_LABELRAW) &&
-           blkid_probe_set_value(pr, "LABEL_RAW", label, len) < 0)
-               return -1;
-       if (!(pr->probreq & BLKID_PROBREQ_LABEL))
+       devno = blkid_probe_get_devno(pr);
+       if (!devno)
                return 0;
-       v = blkid_probe_assign_value(pr, "LABEL");
-       if (!v)
-               return -1;
 
-       v->len = blkid_encode_to_utf8(enc, v->data, sizeof(v->data), label, len);
-       return 0;
+       disk_devno = blkid_probe_get_wholedisk_devno(pr);
+       if (!disk_devno)
+               return 0;
+
+       return devno == disk_devno;
 }
 
-/* like uuid_is_null() from libuuid, but works with arbitrary size of UUID */
-static int uuid_is_empty(const unsigned char *buf, size_t len)
+/**
+ * blkid_probe_get_size:
+ * @pr: probe
+ *
+ * This function returns size of probing area as defined by blkid_probe_set_device().
+ * If the size of the probing area is unrestricted then this function returns
+ * the real size of device. See also blkid_get_dev_size().
+ *
+ * Returns: size in bytes or -1 in case of error.
+ */
+blkid_loff_t blkid_probe_get_size(blkid_probe pr)
 {
-       int i;
-
-       for (i = 0; i < len; i++)
-               if (buf[i])
-                       return 0;
-       return 1;
+       return pr ? pr->size : -1;
 }
 
-int blkid_probe_sprintf_uuid(blkid_probe pr, unsigned char *uuid,
-                               size_t len, const char *fmt, ...)
+/**
+ * blkid_probe_get_offset:
+ * @pr: probe
+ *
+ * This function returns offset of probing area as defined by blkid_probe_set_device().
+ *
+ * Returns: offset in bytes or -1 in case of error.
+ */
+blkid_loff_t blkid_probe_get_offset(blkid_probe pr)
 {
-       int rc = -1;
-       va_list ap;
-
-       if (len > BLKID_PROBVAL_BUFSIZ)
-               len = BLKID_PROBVAL_BUFSIZ;
-
-       if (uuid_is_empty(uuid, len))
-               return 0;
-
-       if ((pr->probreq & BLKID_PROBREQ_UUIDRAW) &&
-           blkid_probe_set_value(pr, "UUID_RAW", uuid, len) < 0)
-               return -1;
-       if (!(pr->probreq & BLKID_PROBREQ_UUID))
-               return 0;
-
-       va_start(ap, fmt);
-       rc = blkid_probe_vsprintf_value(pr, "UUID", fmt, ap);
-       va_end(ap);
-
-       /* convert to lower case (..be paranoid) */
-       if (!rc) {
-               int i;
-               struct blkid_prval *v = &pr->vals[pr->nvals];
-
-               for (i = 0; i < v->len; i++)
-                       if (v->data[i] >= 'A' && v->data[i] <= 'F')
-                               v->data[i] = (v->data[i] - 'A') + 'a';
-       }
-       return rc;
+       return pr ? pr->off : -1;
 }
 
-/* function to set UUIDs that are in suberblocks stored as strings */
-int blkid_probe_strncpy_uuid(blkid_probe pr, unsigned char *str, size_t len)
+/**
+ * blkid_probe_get_fd:
+ * @pr: probe
+ *
+ * Returns: file descriptor for assigned device/file.
+ */
+int blkid_probe_get_fd(blkid_probe pr)
 {
-       struct blkid_prval *v;
-
-       if (str == NULL || *str == '\0')
-               return -1;
-       if (!len)
-               len = strlen((char *) str);
-       if (len > BLKID_PROBVAL_BUFSIZ)
-               len = BLKID_PROBVAL_BUFSIZ;
-
-       if ((pr->probreq & BLKID_PROBREQ_UUIDRAW) &&
-           blkid_probe_set_value(pr, "UUID_RAW", str, len) < 0)
-               return -1;
-       if (!(pr->probreq & BLKID_PROBREQ_UUID))
-               return 0;
-
-       v = blkid_probe_assign_value(pr, "UUID");
-       if (v) {
-               memcpy((char *) v->data, str, len);
-               *(v->data + len) = '\0';
-               v->len = len;
-               return 0;
-       }
-       return -1;
+       return pr ? pr->fd : -1;
 }
 
-/* default _set_uuid function to set DCE UUIDs */
-int blkid_probe_set_uuid_as(blkid_probe pr, unsigned char *uuid, const char *name)
+/**
+ * blkid_probe_get_sectorsize:
+ * @pr: probe or NULL (for NULL returns 512)
+ *
+ * Returns: block device logical sector size (BLKSSZGET ioctl, default 512).
+ */
+unsigned int blkid_probe_get_sectorsize(blkid_probe pr)
 {
-       struct blkid_prval *v;
-
-       if (uuid_is_empty(uuid, 16))
-               return 0;
+       if (!pr)
+               return DEFAULT_SECTOR_SIZE;  /*... and good luck! */
 
-       if (!name) {
-               if ((pr->probreq & BLKID_PROBREQ_UUIDRAW) &&
-                   blkid_probe_set_value(pr, "UUID_RAW", uuid, 16) < 0)
-                       return -1;
-               if (!(pr->probreq & BLKID_PROBREQ_UUID))
-                       return 0;
+       if (pr->blkssz)
+               return pr->blkssz;
 
-               v = blkid_probe_assign_value(pr, "UUID");
-       } else
-               v = blkid_probe_assign_value(pr, name);
+       if (S_ISBLK(pr->mode) &&
+           blkdev_get_sector_size(pr->fd, (int *) &pr->blkssz) == 0)
+               return pr->blkssz;
 
-       blkid_unparse_uuid(uuid, (char *) v->data, sizeof(v->data));
-       v->len = 37;
+       pr->blkssz = DEFAULT_SECTOR_SIZE;
+       return pr->blkssz;
+}
 
-       return 0;
+/**
+ * blkid_probe_get_sectors:
+ * @pr: probe
+ *
+ * Returns: 512-byte sector count or -1 in case of error.
+ */
+blkid_loff_t blkid_probe_get_sectors(blkid_probe pr)
+{
+       return pr ? pr->size >> 9 : -1;
 }
 
-int blkid_probe_set_uuid(blkid_probe pr, unsigned char *uuid)
+/**
+ * blkid_probe_numof_values:
+ * @pr: probe
+ *
+ * Returns: number of values in probing result or -1 in case of error.
+ */
+int blkid_probe_numof_values(blkid_probe pr)
 {
-       return blkid_probe_set_uuid_as(pr, uuid, NULL);
+       if (!pr)
+               return -1;
+       return pr->nvals;
 }
 
+/**
+ * blkid_probe_get_value:
+ * @pr: probe
+ * @num: wanted value in range 0..N, where N is blkid_probe_numof_values() - 1
+ * @name: pointer to return value name or NULL
+ * @data: pointer to return value data or NULL
+ * @len: pointer to return value length or NULL
+ *
+ * Note, the @len returns length of the @data, including the terminating
+ * '\0' character.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
 int blkid_probe_get_value(blkid_probe pr, int num, const char **name,
                        const char **data, size_t *len)
 {
-       struct blkid_prval *v;
+       struct blkid_prval *v = __blkid_probe_get_value(pr, num);
 
-       if (pr == NULL || num < 0 || num >= pr->nvals)
+       if (!v)
                return -1;
-
-       v = &pr->vals[num];
        if (name)
                *name = v->name;
        if (data)
@@ -1023,34 +1234,70 @@ int blkid_probe_get_value(blkid_probe pr, int num, const char **name,
        return 0;
 }
 
+/**
+ * blkid_probe_lookup_value:
+ * @pr: probe
+ * @name: name of value
+ * @data: pointer to return value data or NULL
+ * @len: pointer to return value length or NULL
+ *
+ * Note, the @len returns length of the @data, including the terminating
+ * '\0' character.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
 int blkid_probe_lookup_value(blkid_probe pr, const char *name,
                        const char **data, size_t *len)
+{
+       struct blkid_prval *v = __blkid_probe_lookup_value(pr, name);
+
+       if (!v)
+               return -1;
+       if (data)
+               *data = (char *) v->data;
+       if (len)
+               *len = v->len;
+       return 0;
+}
+
+/**
+ * blkid_probe_has_value:
+ * @pr: probe
+ * @name: name of value
+ *
+ * Returns: 1 if value exist in probing result, otherwise 0.
+ */
+int blkid_probe_has_value(blkid_probe pr, const char *name)
+{
+       if (blkid_probe_lookup_value(pr, name, NULL, NULL) == 0)
+               return 1;
+       return 0;
+}
+
+struct blkid_prval *__blkid_probe_get_value(blkid_probe pr, int num)
+{
+       if (pr == NULL || num < 0 || num >= pr->nvals)
+               return NULL;
+
+       return &pr->vals[num];
+}
+
+struct blkid_prval *__blkid_probe_lookup_value(blkid_probe pr, const char *name)
 {
        int i;
 
        if (pr == NULL || pr->nvals == 0 || name == NULL)
-               return -1;
+               return NULL;
 
        for (i = 0; i < pr->nvals; i++) {
                struct blkid_prval *v = &pr->vals[i];
 
                if (v->name && strcmp(name, v->name) == 0) {
-                       if (data)
-                               *data = (char *) v->data;
-                       if (len)
-                               *len = v->len;
                        DBG(DEBUG_LOWPROBE, printf("returning %s value\n", v->name));
-                       return 0;
+                       return v;
                }
        }
-       return -1;
-}
-
-int blkid_probe_has_value(blkid_probe pr, const char *name)
-{
-       if (blkid_probe_lookup_value(pr, name, NULL, NULL) == 0)
-               return 1;
-       return 0;
+       return NULL;
 }
 
 
@@ -1071,3 +1318,97 @@ void blkid_unparse_uuid(const unsigned char *uuid, char *str, size_t len)
 #endif
 }
 
+
+/* Removes whitespace from the right-hand side of a string (trailing
+ * whitespace).
+ *
+ * Returns size of the new string (without \0).
+ */
+size_t blkid_rtrim_whitespace(unsigned char *str)
+{
+       size_t i = strlen((char *) str);
+
+       while (i--) {
+               if (!isspace(str[i]))
+                       break;
+       }
+       str[++i] = '\0';
+       return i;
+}
+
+/*
+ * Some mkfs-like utils wipe some parts (usually begin) of the device.
+ * For example LVM (pvcreate) or mkswap(8). This information could be used
+ * for later resolution to conflicts between superblocks.
+ *
+ * For example we found valid LVM superblock, LVM wipes 8KiB at the begin of
+ * the device. If we found another signature (for example MBR) this wiped area
+ * then the signature has been added later and LVM superblock should be ignore.
+ *
+ * Note that this heuristic is not 100% reliable, for example "pvcreate --zero
+ * n" allows to keep the begin of the device unmodified. It's probably better
+ * to use this heuristic for conflicts between superblocks and partition tables
+ * than for conflicts between filesystem superblocks -- existence of unwanted
+ * partition table is very unusual, because PT is pretty visible (parsed and
+ * interpreted by kernel).
+ */
+void blkid_probe_set_wiper(blkid_probe pr, blkid_loff_t off, blkid_loff_t size)
+{
+       struct blkid_chain *chn;
+
+       if (!pr)
+               return;
+
+       if (!size) {
+               DBG(DEBUG_LOWPROBE, printf("zeroize wiper\n"));
+               pr->wipe_size = pr->wipe_off = 0;
+               pr->wipe_chain = NULL;
+               return;
+       }
+
+       chn = pr->cur_chain;
+
+       if (!chn || !chn->driver ||
+           chn->idx < 0 || chn->idx >= chn->driver->nidinfos)
+               return;
+
+       pr->wipe_size = size;
+       pr->wipe_off = off;
+       pr->wipe_chain = chn;
+
+       DBG(DEBUG_LOWPROBE,
+               printf("wiper set to %s::%s off=%jd size=%jd\n",
+                       chn->driver->name,
+                       chn->driver->idinfos[chn->idx]->name,
+                       pr->wipe_off, pr->wipe_size));
+       return;
+}
+
+/*
+ * Returns 1 if the <@off,@size> area was wiped
+ */
+int blkid_probe_is_wiped(blkid_probe pr, struct blkid_chain **chn,
+                    blkid_loff_t off, blkid_loff_t size)
+{
+       if (!pr || !size)
+               return 0;
+
+       if (pr->wipe_off <= off && off + size <= pr->wipe_off + pr->wipe_size) {
+               if (chn)
+                       *chn = pr->wipe_chain;
+               return 1;
+       }
+       return 0;
+}
+
+void blkid_probe_use_wiper(blkid_probe pr, blkid_loff_t off, blkid_loff_t size)
+{
+       struct blkid_chain *chn = NULL;
+
+       if (blkid_probe_is_wiped(pr, &chn, off, size) && chn) {
+               DBG(DEBUG_LOWPROBE, printf("wiped area detected -- ignore previous results\n"));
+               blkid_probe_set_wiper(pr, 0, 0);
+               blkid_probe_chain_reset_vals(pr, chn);
+       }
+}
+