Project import generated by Copybara.

GitOrigin-RevId: a827cd87d54a0f30a0a16cc52b01d4d88f44e6b7
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..41fe6fd
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,8 @@
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later
+version.
+
+The complete text of the license is available at the
+Documentation/licenses/COPYING.LGPLv2.1 file.
diff --git a/LICENSE b/LICENSE
index e5ab03e..4362b49 100644
--- a/LICENSE
+++ b/LICENSE
@@ -55,7 +55,7 @@
 that what they have is not the original version, so that the original
 author's reputation will not be affected by problems that might be
 introduced by others.
-
+
   Finally, software patents pose a constant threat to the existence of
 any free program.  We wish to make sure that a company cannot
 effectively restrict the users of a free program by obtaining a
@@ -111,7 +111,7 @@
 "work based on the library" and a "work that uses the library".  The
 former contains code derived from the library, whereas the latter must
 be combined with the library in order to run.
-
+
                   GNU LESSER GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
@@ -158,7 +158,7 @@
   You may charge a fee for the physical act of transferring a copy,
 and you may at your option offer warranty protection in exchange for a
 fee.
-
+
   2. You may modify your copy or copies of the Library or any portion
 of it, thus forming a work based on the Library, and copy and
 distribute such modifications or work under the terms of Section 1
@@ -216,7 +216,7 @@
 ordinary GNU General Public License has appeared, then you can specify
 that version instead if you wish.)  Do not make any other change in
 these notices.
-
+
   Once this change is made in a given copy, it is irreversible for
 that copy, so the ordinary GNU General Public License applies to all
 subsequent copies and derivative works made from that copy.
@@ -267,7 +267,7 @@
 distribute the object code for the work under the terms of Section 6.
 Any executables containing that work also fall under Section 6,
 whether or not they are linked directly with the Library itself.
-
+
   6. As an exception to the Sections above, you may also combine or
 link a "work that uses the Library" with the Library to produce a
 work containing portions of the Library, and distribute that work
@@ -329,7 +329,7 @@
 accompany the operating system.  Such a contradiction means you cannot
 use both them and the Library together in an executable that you
 distribute.
-
+
   7. You may place library facilities that are a work based on the
 Library side-by-side in a single library together with other library
 facilities not covered by this License, and distribute such a combined
@@ -370,7 +370,7 @@
 restrictions on the recipients' exercise of the rights granted herein.
 You are not responsible for enforcing compliance by third parties with
 this License.
-
+
   11. If, as a consequence of a court judgment or allegation of patent
 infringement or for any other reason (not limited to patent issues),
 conditions are imposed on you (whether by court order, agreement or
@@ -422,7 +422,7 @@
 the Free Software Foundation.  If the Library does not specify a
 license version number, you may choose any version ever published by
 the Free Software Foundation.
-
+
   14. If you wish to incorporate parts of the Library into other free
 programs whose distribution conditions are incompatible with these,
 write to the author to ask for permission.  For software which is
@@ -456,7 +456,7 @@
 DAMAGES.
 
                      END OF TERMS AND CONDITIONS
-
+
            How to Apply These Terms to Your New Libraries
 
   If you develop a new library, and you want it to be of the greatest
diff --git a/Makemodule.am b/Makemodule.am
new file mode 100644
index 0000000..b4f6f9c
--- /dev/null
+++ b/Makemodule.am
@@ -0,0 +1,16 @@
+if BUILD_LIBBLKID
+
+include libblkid/src/Makemodule.am
+include libblkid/samples/Makemodule.am
+
+if ENABLE_GTK_DOC
+# Docs uses separate Makefiles
+SUBDIRS += libblkid/docs
+endif
+
+pkgconfig_DATA += libblkid/blkid.pc
+PATHFILES      += libblkid/blkid.pc
+dist_man_MANS  += libblkid/libblkid.3
+EXTRA_DIST     += libblkid/libblkid.3 libblkid/COPYING
+
+endif # BUILD_LIBBLKID
diff --git a/blkid.pc.in b/blkid.pc.in
new file mode 100644
index 0000000..40ec8a9
--- /dev/null
+++ b/blkid.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@usrlib_execdir@
+includedir=@includedir@
+
+Name: blkid
+Description: Block device id library
+Version: @LIBBLKID_VERSION@
+Requires.private: uuid
+Cflags: -I${includedir}/blkid
+Libs: -L${libdir} -lblkid
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 0000000..e40d782
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,18 @@
+*.args
+*.bak
+*.types
+*-decl-list.txt
+*-decl.txt
+*.hierarchy
+html/*
+*.interfaces
+*-overrides.txt
+*.prerequisites
+*.signals
+*.stamp
+tmpl/*
+*-undeclared.txt
+*-undocumented.txt
+*-unused.txt
+version.xml
+xml/*
diff --git a/docs/Makefile.am b/docs/Makefile.am
new file mode 100644
index 0000000..d06038e
--- /dev/null
+++ b/docs/Makefile.am
@@ -0,0 +1,94 @@
+## Process this file with automake to produce Makefile.in
+
+# We require automake 1.10 at least.
+AUTOMAKE_OPTIONS = 1.10
+
+# This is a blank Makefile.am for using gtk-doc.
+# Copy this to your project's API docs directory and modify the variables to
+# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
+# of using the various options.
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE=libblkid
+
+# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
+#DOC_MODULE_VERSION=2
+
+# The top-level SGML file. You can change this if you want to.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting the functions and macros.
+# e.g. DOC_SOURCE_DIR=../../../gtk
+DOC_SOURCE_DIR=../src
+
+# Extra options to pass to gtkdoc-scangobj. Not normally needed.
+SCANGOBJ_OPTIONS=
+
+# Extra options to supply to gtkdoc-scan.
+# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
+SCAN_OPTIONS=--deprecated-guards="BLKID_DISABLE_DEPRECATED"
+
+# Extra options to supply to gtkdoc-mkdb.
+# e.g. MKDB_OPTIONS=--sgml-mode --output-format=xml
+MKDB_OPTIONS=--sgml-mode --output-format=xml --name-space blkid
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkhtml
+MKHTML_OPTIONS=
+
+# Extra options to supply to gtkdoc-fixref. Not normally needed.
+# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
+FIXXREF_OPTIONS=
+
+# Used for dependencies. The docs will be rebuilt if any of these change.
+# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
+# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
+HFILE_GLOB=$(top_builddir)/libblkid/src/blkid.h
+CFILE_GLOB=$(top_srcdir)/libblkid/src/*.c
+
+# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
+# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
+EXTRA_HFILES=
+
+# Header files to ignore when scanning. Use base file name, no paths
+# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h
+IGNORE_HFILES=blkidP.h list.h partitions.h superblocks.h topology.h aix.h dos.h
+
+# Images to copy into HTML directory.
+# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
+HTML_IMAGES=
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+content_files = $(builddir)/version.xml $(srcdir)/libblkid-config.xml
+
+# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
+# These files must be listed here *and* in content_files
+# e.g. expand_content_files=running.sgml
+expand_content_files=
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
+# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
+GTKDOC_CFLAGS=
+GTKDOC_LIBS=
+
+# This includes the standard gtk-doc make rules, copied by gtkdocize.
+include $(top_srcdir)/config/gtk-doc.make
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST += version.xml.in $(srcdir)/libblkid-config.xml
+
+# Files not to distribute
+# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
+# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
+DISTCLEANFILES += version.xml
+
diff --git a/docs/libblkid-config.xml b/docs/libblkid-config.xml
new file mode 100644
index 0000000..89fbb7f
--- /dev/null
+++ b/docs/libblkid-config.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+  <!ENTITY version SYSTEM "version.xml">
+]>
+<refentry id="libblkid-config">
+<refmeta>
+<refentrytitle role="top_of_page" id="libblkid-config.top_of_page">Config file</refentrytitle>
+<manvolnum>3</manvolnum>
+<refmiscinfo>LIBBLKID Library</refmiscinfo>
+</refmeta>
+
+<refnamediv>
+<refname>Config file</refname>
+<refpurpose>config file to control paths and basic library behavior</refpurpose>
+</refnamediv>
+
+<refsect1 id="libblkid-config.description" role="desc">
+<title role="desc.title">Description</title>
+<para>
+The standard location of the
+/etc/blkid.conf config file can be overridden by the environment variable
+BLKID_CONF.  The following options control the libblkid library:
+</para>
+
+<variablelist role="params">
+ <varlistentry>
+  <term>SEND_UEVENT=<parameter>yes|not</parameter></term>
+  <listitem><simpara>
+   Sends uevent when /dev/disk/by-{label,uuid}/
+   symlink does not match with LABEL or UUID on the device. Default is "yes".
+  </simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+  <term>CACHE_FILE=<parameter>path</parameter></term>
+  <listitem><simpara>
+   Overrides the standard location of the cache file. This
+   setting can be overridden by the environment variable BLKID_FILE. Default is
+   /etc/blkid.tab.
+  </simpara></listitem>
+ </varlistentry>
+ <varlistentry>
+  <term>EVALUATE=<parameter>method</parameter></term>
+  <listitem><simpara>
+   Defines LABEL and UUID evaluation method(s). Currently,
+   the libblkid library supports "udev" and "scan" methods. More than one methods
+   may be specified in a comma separated list. Default is "udev,scan". The "udev"
+   method uses udev /dev/disk/by-* symlinks and the "scan" method scans all
+   block devices from the /proc/partitions file.
+  </simpara></listitem>
+ </varlistentry>
+</variablelist>
+
+</refsect1>
+
+</refentry>
diff --git a/docs/libblkid-docs.xml b/docs/libblkid-docs.xml
new file mode 100644
index 0000000..da5c1ad
--- /dev/null
+++ b/docs/libblkid-docs.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
+[
+  <!ENTITY version SYSTEM "version.xml">
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude">
+  <bookinfo>
+    <title>libblkid Reference Manual</title>
+    <releaseinfo>for libblkid version &version;</releaseinfo>
+    <copyright>
+      <year>2009-2012</year>
+      <holder>Karel Zak &lt;kzak@redhat.com&gt;</holder>
+    </copyright>
+  </bookinfo>
+
+  <part id="gtk">
+    <title>libblkid Overview</title>
+    <partintro>
+    <para>
+The libblkid library is used to identify block devices (disks) as to their
+content (e.g.  filesystem type, partitions) as well as extracting additional
+information such as filesystem labels/volume names, partitions, unique
+identifiers/serial numbers, etc.  A common use is to allow use of LABEL= and
+UUID= tags instead of hard-coding specific block device names into
+configuration files.
+    </para>
+    <para>
+The libblkid librray
+was written by Andreas Dilger for the ext2 filesystem utilties, with input
+from Ted Ts'o.  The library was subsequently heavily modified by Ted Ts'o.
+    </para>
+    <para>
+The low-level probing code, topology and partitions support was written
+by Karel Zak. Currently, the library is mainatned by Karel Zak.
+    </para>
+    <para>
+The library is part of the util-linux package since version 2.15 and is
+available from ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+    </para>
+  </partintro>
+  <xi:include href="xml/libblkid-config.xml"/>
+ </part>
+
+  <part>
+    <title>High-level</title>
+    <xi:include href="xml/evaluate.xml"/>
+    <xi:include href="xml/cache.xml"/>
+    <xi:include href="xml/search.xml"/>
+  </part>
+  <part>
+    <title>Low-level</title>
+    <xi:include href="xml/lowprobe.xml"/>
+    <xi:include href="xml/lowprobe-tags.xml"/>
+    <xi:include href="xml/superblocks.xml"/>
+    <xi:include href="xml/partitions.xml"/>
+    <xi:include href="xml/topology.xml"/>
+  </part>
+  <part>
+    <title>Common utils</title>
+    <xi:include href="xml/encode.xml"/>
+    <xi:include href="xml/misc.xml"/>
+  </part>
+
+  <index id="api-index-full">
+    <title>API Index</title>
+    <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+  </index>
+</book>
diff --git a/docs/libblkid-sections.txt b/docs/libblkid-sections.txt
new file mode 100644
index 0000000..511fc30
--- /dev/null
+++ b/docs/libblkid-sections.txt
@@ -0,0 +1,192 @@
+<SECTION>
+<FILE>evaluate</FILE>
+blkid_evaluate_tag
+blkid_evaluate_spec
+</SECTION>
+
+<SECTION>
+<FILE>cache</FILE>
+blkid_cache
+blkid_gc_cache
+blkid_get_cache
+blkid_put_cache
+blkid_probe_all
+blkid_probe_all_removable
+blkid_probe_all_new
+blkid_verify
+</SECTION>
+
+<SECTION>
+<FILE>search</FILE>
+blkid_dev
+blkid_dev_devname
+blkid_dev_has_tag
+blkid_dev_iterate
+blkid_dev_iterate_begin
+blkid_dev_iterate_end
+blkid_dev_next
+blkid_dev_set_search
+blkid_find_dev_with_tag
+blkid_get_dev
+blkid_get_devname
+blkid_get_tag_value
+blkid_tag_iterate
+blkid_tag_iterate_begin
+blkid_tag_iterate_end
+blkid_tag_next
+</SECTION>
+
+<SECTION>
+<FILE>lowprobe</FILE>
+blkid_probe
+blkid_free_probe
+blkid_new_probe
+blkid_new_probe_from_filename
+blkid_probe_get_devno
+blkid_probe_get_fd
+blkid_probe_get_sectorsize
+blkid_probe_get_sectors
+blkid_probe_get_size
+blkid_probe_get_offset
+blkid_probe_get_wholedisk_devno
+blkid_probe_set_device
+blkid_probe_is_wholedisk
+blkid_reset_probe
+</SECTION>
+
+<SECTION>
+<FILE>lowprobe-tags</FILE>
+blkid_do_fullprobe
+blkid_do_wipe
+blkid_do_probe
+blkid_do_safeprobe
+<SUBSECTION>
+blkid_probe_get_value
+blkid_probe_has_value
+blkid_probe_lookup_value
+blkid_probe_numof_values
+</SECTION>
+
+<SECTION>
+<FILE>partitions</FILE>
+blkid_partlist
+blkid_partition
+blkid_parttable
+blkid_probe_enable_partitions
+blkid_probe_set_partitions_flags
+blkid_probe_filter_partitions_type
+blkid_probe_invert_partitions_filter
+blkid_probe_reset_partitions_filter
+<SUBSECTION>
+blkid_known_pttype
+<SUBSECTION>
+blkid_partition_get_name
+blkid_partition_get_flags
+blkid_partition_get_partno
+blkid_partition_get_size
+blkid_partition_get_start
+blkid_partition_get_table
+blkid_partition_get_type
+blkid_partition_get_type_string
+blkid_partition_get_uuid
+blkid_partition_is_extended
+blkid_partition_is_logical
+blkid_partition_is_primary
+<SUBSECTION>
+blkid_partlist_get_partition
+blkid_partlist_numof_partitions
+blkid_partlist_devno_to_partition
+blkid_partlist_get_table
+<SUBSECTION>
+blkid_parttable_get_offset
+blkid_parttable_get_parent
+blkid_parttable_get_type
+<SUBSECTION>
+blkid_probe_get_partitions
+</SECTION>
+
+<SECTION>
+<FILE>superblocks</FILE>
+blkid_probe_enable_superblocks
+<SUBSECTION>
+blkid_known_fstype
+blkid_superblocks_get_name
+<SUBSECTION>
+blkid_probe_filter_superblocks_type
+blkid_probe_filter_superblocks_usage
+blkid_probe_invert_superblocks_filter
+blkid_probe_reset_superblocks_filter
+blkid_probe_set_superblocks_flags
+<SUBSECTION>
+blkid_probe_reset_filter
+blkid_probe_filter_types
+blkid_probe_filter_usage
+blkid_probe_invert_filter
+blkid_probe_set_request
+</SECTION>
+
+<SECTION>
+<FILE>topology</FILE>
+blkid_topology
+blkid_probe_enable_topology
+<SUBSECTION>
+blkid_probe_get_topology
+blkid_topology_get_alignment_offset
+blkid_topology_get_logical_sector_size
+blkid_topology_get_minimum_io_size
+blkid_topology_get_optimal_io_size
+blkid_topology_get_physical_sector_size
+</SECTION>
+
+<SECTION>
+<FILE>encode</FILE>
+blkid_encode_string
+blkid_safe_string
+</SECTION>
+
+<SECTION>
+<FILE>misc</FILE>
+blkid_loff_t
+blkid_devno_to_devname
+blkid_devno_to_wholedisk
+blkid_get_dev_size
+blkid_get_library_version
+blkid_parse_tag_string
+blkid_parse_version_string
+blkid_send_uevent
+BLKID_VERSION
+BLKID_DATE
+BLKID_FLTR_NOTIN
+BLKID_FLTR_ONLYIN
+BLKID_DEV_CREATE
+BLKID_DEV_FIND
+BLKID_DEV_NORMAL
+BLKID_DEV_VERIFY
+BLKID_PARTS_ENTRY_DETAILS
+BLKID_PARTS_FORCE_GPT
+BLKID_PARTS_MAGIC
+BLKID_PROBREQ_LABEL
+BLKID_PROBREQ_LABELRAW
+BLKID_PROBREQ_SECTYPE
+BLKID_PROBREQ_TYPE
+BLKID_PROBREQ_USAGE
+BLKID_PROBREQ_UUID
+BLKID_PROBREQ_UUIDRAW
+BLKID_PROBREQ_VERSION
+BLKID_SUBLKS_DEFAULT
+BLKID_SUBLKS_LABEL
+BLKID_SUBLKS_LABELRAW
+BLKID_SUBLKS_MAGIC
+BLKID_SUBLKS_SECTYPE
+BLKID_SUBLKS_TYPE
+BLKID_SUBLKS_USAGE
+BLKID_SUBLKS_UUID
+BLKID_SUBLKS_UUIDRAW
+BLKID_SUBLKS_VERSION
+BLKID_USAGE_CRYPTO
+BLKID_USAGE_FILESYSTEM
+BLKID_USAGE_OTHER
+BLKID_USAGE_RAID
+</SECTION>
+
+
diff --git a/docs/version.xml.in b/docs/version.xml.in
new file mode 100644
index 0000000..d78bda9
--- /dev/null
+++ b/docs/version.xml.in
@@ -0,0 +1 @@
+@VERSION@
diff --git a/libblkid.3 b/libblkid.3
new file mode 100644
index 0000000..ff84e7d
--- /dev/null
+++ b/libblkid.3
@@ -0,0 +1,80 @@
+.\" Copyright 2001 Andreas Dilger (adilger@turbolinux.com)
+.\"
+.\" This man page was created for libblkid.so.1.0 from e2fsprogs-1.24.
+.\"
+.\" This file may be copied under the terms of the GNU Lesser General Public
+.\" License.
+.\"
+.\" Created  Wed Sep 14 12:02:12 2001, Andreas Dilger
+.TH LIBBLKID 3 "May 2009" "util-linux" "Programmer's Manual"
+.SH NAME
+libblkid \- block device identification library
+.SH SYNOPSIS
+.B #include <blkid.h>
+.sp
+.B cc
+.I file.c
+.B \-lblkid
+.SH DESCRIPTION
+The
+.B libblkid
+library is used to identify block devices (disks) as to their content (e.g.
+filesystem type) as well as extracting additional information such as
+filesystem labels/volume names, unique identifiers/serial numbers.
+A common use is to allow use of LABEL= and UUID= tags instead of hard-coding
+specific block device names into configuration files.
+.P
+The low-level part of the library also allows to extract information about
+partitions and block device topology.
+.P
+The high-level part of the library keeps information about block devices in a
+cache file and is verified to still be valid before being returned to the user
+(if the user has read permission on the raw block device, otherwise not).
+The cache file also allows unprivileged users (normally anyone other
+than root, or those not in the "disk" group) to locate devices by label/id.
+The standard location of the cache file can be overridden by the
+environment variable BLKID_FILE.
+.P
+In situations where one is getting information about a single known device, it
+does not impact performance whether the cache is used or not (unless you are
+not able to read the block device directly). 
+.P
+The high-level part of the library supports two methods to evaluate LABEL/UUID.
+It reads information directly from a block device or read information from
+/dev/disk/by-* udev symlinks. The udev is preferred method by default.
+.P
+If you are dealing with
+multiple devices, use of the cache is highly recommended (even if empty) as
+devices will be scanned at most one time and the on-disk cache will be
+updated if possible.
+.P
+In some cases (modular kernels), block devices are not even visible until
+after they are accessed the first time, so it is critical that there is
+some way to locate these devices without enumerating only visible devices,
+so the use of the cache file is
+.B required
+in this situation.
+.SH CONFIGURATION FILE
+The standard location of the
+.I /etc/blkid.conf
+config file can be overridden by the environment variable BLKID_CONF. For more
+details about the config file see
+.BR blkid (8)
+man page.
+.SH AUTHOR
+.B libblkid
+was written by Andreas Dilger for the ext2 filesystem utilties, with input
+from Ted Ts'o.  The library was subsequently heavily modified by Ted Ts'o.
+
+The low-level probing code was rewritten by Karel Zak.
+.SH AVAILABILITY
+.B libblkid
+is part of the util-linux package since version 2.15 and is available from
+ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+.SH COPYING
+.B libblkid
+is available under the terms of the GNU Library General Public License (LGPL),
+version 2 (or at your discretion any later version).
+.SH "SEE ALSO"
+.BR blkid (8)
+.BR findfs (8)
diff --git a/samples/.gitignore b/samples/.gitignore
new file mode 100644
index 0000000..409e5cf
--- /dev/null
+++ b/samples/.gitignore
@@ -0,0 +1,4 @@
+topology
+partitions
+mkfs
+superblocks
diff --git a/samples/Makemodule.am b/samples/Makemodule.am
new file mode 100644
index 0000000..0ffbf14
--- /dev/null
+++ b/samples/Makemodule.am
@@ -0,0 +1,22 @@
+
+check_PROGRAMS += \
+	sample-mkfs \
+	sample-partitions \
+	sample-superblocks \
+	sample-topology
+
+sample_mkfs_SOURCES = libblkid/samples/mkfs.c
+sample_mkfs_LDADD = libblkid.la
+sample_mkfs_CFLAGS = -I$(ul_libblkid_incdir)
+
+sample_partitions_SOURCES = libblkid/samples/partitions.c
+sample_partitions_LDADD = libblkid.la
+sample_partitions_CFLAGS = -I$(ul_libblkid_incdir)
+
+sample_superblocks_SOURCES = libblkid/samples/superblocks.c
+sample_superblocks_LDADD = libblkid.la
+sample_superblocks_CFLAGS = -I$(ul_libblkid_incdir)
+
+sample_topology_SOURCES = libblkid/samples/topology.c
+sample_topology_LDADD = libblkid.la
+sample_topology_CFLAGS = -I$(ul_libblkid_incdir)
diff --git a/samples/mkfs.c b/samples/mkfs.c
new file mode 100644
index 0000000..5c3ebe7
--- /dev/null
+++ b/samples/mkfs.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <blkid.h>
+
+#include "c.h"
+
+int main(int argc, char *argv[])
+{
+	int rc;
+	char *devname;
+	blkid_probe pr;
+	blkid_topology tp;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <device>  "
+			"-- checks based on libblkid for mkfs-like programs.\n",
+			program_invocation_short_name);
+		return EXIT_FAILURE;
+	}
+
+	devname = argv[1];
+	pr = blkid_new_probe_from_filename(devname);
+	if (!pr)
+		err(EXIT_FAILURE, "%s: faild to create a new libblkid probe",
+				devname);
+
+	/*
+	 * check Filesystems / Partitions overwrite
+	 */
+
+	/* enable partitions probing (superblocks are enabled by default) */
+	blkid_probe_enable_partitions(pr, TRUE);
+
+	rc = blkid_do_fullprobe(pr);
+	if (rc == -1)
+		errx(EXIT_FAILURE, "%s: blkid_do_fullprobe() failed", devname);
+	else if (rc == 0) {
+		const char *type;
+
+		if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL))
+			errx(EXIT_FAILURE, "%s: appears to contain an existing "
+					"%s superblock", devname, type);
+
+		if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL))
+			errx(EXIT_FAILURE, "%s: appears to contain an partition "
+					"table (%s)", devname, type);
+	}
+
+	/*
+	 * get topology details
+	 */
+	tp = blkid_probe_get_topology(pr);
+	if (!tp)
+		errx(EXIT_FAILURE, "%s: failed to read topology", devname);
+
+
+	/* ... your mkfs.<type> code or so ...
+
+	off = blkid_topology_get_alignment_offset(tp);
+
+	 */
+
+	blkid_free_probe(pr);
+
+	return EXIT_SUCCESS;
+}
diff --git a/samples/partitions.c b/samples/partitions.c
new file mode 100644
index 0000000..3b52736
--- /dev/null
+++ b/samples/partitions.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <blkid.h>
+#include "c.h"
+
+int main(int argc, char *argv[])
+{
+	int i, nparts;
+	char *devname;
+	blkid_probe pr;
+	blkid_partlist ls;
+	blkid_parttable root_tab;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <device|file>  "
+				"-- prints partitions\n",
+				program_invocation_short_name);
+		return EXIT_FAILURE;
+	}
+
+	devname = argv[1];
+	pr = blkid_new_probe_from_filename(devname);
+	if (!pr)
+		err(EXIT_FAILURE, "%s: faild to create a new libblkid probe",
+				devname);
+	/* Binary interface */
+	ls = blkid_probe_get_partitions(pr);
+	if (!ls)
+		errx(EXIT_FAILURE, "%s: failed to read partitions\n", devname);
+
+	/*
+	 * Print info about the primary (root) partition table
+	 */
+	root_tab = blkid_partlist_get_table(ls);
+	if (!root_tab)
+		errx(EXIT_FAILURE, "%s: does not contains any "
+				 "known partition table\n", devname);
+
+	printf("size: %jd, sector size: %u, PT: %s, offset: %jd\n---\n",
+		blkid_probe_get_size(pr),
+		blkid_probe_get_sectorsize(pr),
+		blkid_parttable_get_type(root_tab),
+		blkid_parttable_get_offset(root_tab));
+
+	/*
+	 * List partitions
+	 */
+	nparts = blkid_partlist_numof_partitions(ls);
+	if (!nparts)
+		goto done;
+
+	for (i = 0; i < nparts; i++) {
+		const char *p;
+		blkid_partition par = blkid_partlist_get_partition(ls, i);
+		blkid_parttable tab = blkid_partition_get_table(par);
+
+		printf("#%d: %10llu %10llu  0x%x",
+			blkid_partition_get_partno(par),
+			(unsigned long long) blkid_partition_get_start(par),
+			(unsigned long long) blkid_partition_get_size(par),
+			blkid_partition_get_type(par));
+
+		if (root_tab != tab)
+			/* subpartition (BSD, Minix, ...) */
+			printf(" (%s)", blkid_parttable_get_type(tab));
+
+		p = blkid_partition_get_name(par);
+		if (p)
+			printf(" name='%s'", p);
+		p = blkid_partition_get_uuid(par);
+		if (p)
+			printf(" uuid='%s'", p);
+		p = blkid_partition_get_type_string(par);
+		if (p)
+			printf(" type='%s'", p);
+
+		putc('\n', stdout);
+	}
+
+done:
+	blkid_free_probe(pr);
+	return EXIT_SUCCESS;
+}
diff --git a/samples/superblocks.c b/samples/superblocks.c
new file mode 100644
index 0000000..20e39c9
--- /dev/null
+++ b/samples/superblocks.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <blkid.h>
+
+#include "c.h"
+
+int main(int argc, char *argv[])
+{
+	int rc;
+	char *devname;
+	blkid_probe pr;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <device>  "
+				"-- prints superblocks details about the device\n",
+				program_invocation_short_name);
+		return EXIT_FAILURE;
+	}
+
+	devname = argv[1];
+	pr = blkid_new_probe_from_filename(devname);
+	if (!pr)
+		err(EXIT_FAILURE, "%s: faild to create a new libblkid probe",
+				devname);
+
+	/* enable topology probing */
+	blkid_probe_enable_superblocks(pr, TRUE);
+
+	/* set all flags */
+	blkid_probe_set_superblocks_flags(pr,
+			BLKID_SUBLKS_LABEL | BLKID_SUBLKS_LABELRAW |
+			BLKID_SUBLKS_UUID | BLKID_SUBLKS_UUIDRAW |
+			BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE |
+			BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION |
+			BLKID_SUBLKS_MAGIC);
+
+	rc = blkid_do_safeprobe(pr);
+	if (rc == -1)
+		errx(EXIT_FAILURE, "%s: blkid_do_safeprobe() failed", devname);
+	else if (rc == 1)
+		warnx("%s: cannot gather information about superblocks", devname);
+	else {
+		int i, nvals = blkid_probe_numof_values(pr);
+
+		for (i = 0; i < nvals; i++) {
+			const char *name, *data;
+
+			blkid_probe_get_value(pr, i, &name, &data, NULL);
+			printf("\t%s = %s\n", name, data);
+		}
+	}
+
+	blkid_free_probe(pr);
+	return EXIT_SUCCESS;
+}
diff --git a/samples/topology.c b/samples/topology.c
new file mode 100644
index 0000000..de1c3a5
--- /dev/null
+++ b/samples/topology.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <blkid.h>
+
+#include "c.h"
+
+int main(int argc, char *argv[])
+{
+	int rc;
+	char *devname;
+	blkid_probe pr;
+	blkid_topology tp;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <device>  "
+				"-- prints topology details about the device\n",
+				program_invocation_short_name);
+		return EXIT_FAILURE;
+	}
+
+	devname = argv[1];
+	pr = blkid_new_probe_from_filename(devname);
+	if (!pr)
+		err(EXIT_FAILURE, "%s: faild to create a new libblkid probe",
+				devname);
+	/*
+	 * Binary interface
+	 */
+	tp = blkid_probe_get_topology(pr);
+	if (tp) {
+		printf("----- binary interface:\n");
+		printf("\talignment offset     : %lu\n",
+				blkid_topology_get_alignment_offset(tp));
+		printf("\tminimum io size      : %lu\n",
+				blkid_topology_get_minimum_io_size(tp));
+		printf("\toptimal io size      : %lu\n",
+				blkid_topology_get_optimal_io_size(tp));
+		printf("\tlogical sector size  : %lu\n",
+				blkid_topology_get_logical_sector_size(tp));
+		printf("\tphysical sector size : %lu\n",
+				blkid_topology_get_physical_sector_size(tp));
+	}
+
+	/*
+	 * NAME=value interface
+	 */
+
+	/* enable topology probing */
+	blkid_probe_enable_topology(pr, TRUE);
+
+	/* disable superblocks probing (enabled by default) */
+	blkid_probe_enable_superblocks(pr, FALSE);
+
+	rc = blkid_do_fullprobe(pr);
+	if (rc == -1)
+		errx(EXIT_FAILURE, "%s: blkid_do_fullprobe() failed", devname);
+	else if (rc == 1)
+		warnx("%s: missing topology information", devname);
+	else {
+		int i, nvals = blkid_probe_numof_values(pr);
+
+		printf("----- NAME=value interface (values: %d):\n", nvals);
+
+		for (i = 0; i < nvals; i++) {
+			const char *name, *data;
+
+			blkid_probe_get_value(pr, i, &name, &data, NULL);
+			printf("\t%s = %s\n", name, data);
+		}
+	}
+
+	blkid_free_probe(pr);
+	return EXIT_SUCCESS;
+}
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..af34f58
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1 @@
+blkid.h
diff --git a/src/Makemodule.am b/src/Makemodule.am
new file mode 100644
index 0000000..de60458
--- /dev/null
+++ b/src/Makemodule.am
@@ -0,0 +1,233 @@
+
+# blkid.h is generated, so it's store in builddir!
+blkidincdir = $(includedir)/blkid
+nodist_blkidinc_HEADERS = $(top_builddir)/libblkid/src/blkid.h
+
+usrlib_exec_LTLIBRARIES += libblkid.la
+libblkid_la_SOURCES = \
+	include/list.h \
+	\
+	libblkid/src/blkidP.h \
+	libblkid/src/cache.c \
+	libblkid/src/config.c \
+	libblkid/src/dev.c \
+	libblkid/src/devname.c \
+	libblkid/src/devno.c \
+	libblkid/src/encode.c \
+	libblkid/src/evaluate.c \
+	libblkid/src/getsize.c \
+	libblkid/src/llseek.c \
+	libblkid/src/probe.c \
+	libblkid/src/read.c \
+	libblkid/src/resolve.c \
+	libblkid/src/save.c \
+	libblkid/src/superblocks/superblocks.h \
+	libblkid/src/tag.c \
+	libblkid/src/verify.c \
+	libblkid/src/version.c \
+	$(blkidinc_HEADERS) \
+	\
+	libblkid/src/partitions/aix.c \
+	libblkid/src/partitions/aix.h \
+	libblkid/src/partitions/blkid_parttypes.h \
+	libblkid/src/partitions/bsd.c \
+	libblkid/src/partitions/dos.c \
+	libblkid/src/partitions/dos.h \
+	libblkid/src/partitions/gpt.c \
+	libblkid/src/partitions/mac.c \
+	libblkid/src/partitions/minix.c \
+	libblkid/src/partitions/partitions.c \
+	libblkid/src/partitions/partitions.h \
+	libblkid/src/partitions/sgi.c \
+	libblkid/src/partitions/solaris_x86.c \
+	libblkid/src/partitions/sun.c \
+	libblkid/src/partitions/ultrix.c \
+	libblkid/src/partitions/unixware.c \
+	\
+	libblkid/src/superblocks/adaptec_raid.c \
+	libblkid/src/superblocks/befs.c \
+	libblkid/src/superblocks/bfs.c \
+	libblkid/src/superblocks/btrfs.c \
+	libblkid/src/superblocks/cramfs.c \
+	libblkid/src/superblocks/ddf_raid.c \
+	libblkid/src/superblocks/drbd.c \
+	libblkid/src/superblocks/drbdproxy_datalog.c \
+	libblkid/src/superblocks/exfat.c \
+	libblkid/src/superblocks/ext.c \
+	libblkid/src/superblocks/gfs.c \
+	libblkid/src/superblocks/hfs.c \
+	libblkid/src/superblocks/highpoint_raid.c \
+	libblkid/src/superblocks/hpfs.c \
+	libblkid/src/superblocks/iso9660.c \
+	libblkid/src/superblocks/isw_raid.c \
+	libblkid/src/superblocks/jfs.c \
+	libblkid/src/superblocks/jmicron_raid.c \
+	libblkid/src/superblocks/linux_raid.c \
+	libblkid/src/superblocks/lsi_raid.c \
+	libblkid/src/superblocks/luks.c \
+	libblkid/src/superblocks/lvm.c \
+	libblkid/src/superblocks/minix.c \
+	libblkid/src/superblocks/netware.c \
+	libblkid/src/superblocks/nilfs.c \
+	libblkid/src/superblocks/ntfs.c \
+	libblkid/src/superblocks/nvidia_raid.c \
+	libblkid/src/superblocks/ocfs.c \
+	libblkid/src/superblocks/promise_raid.c \
+	libblkid/src/superblocks/reiserfs.c \
+	libblkid/src/superblocks/romfs.c \
+	libblkid/src/superblocks/silicon_raid.c \
+	libblkid/src/superblocks/squashfs.c \
+	libblkid/src/superblocks/superblocks.c \
+	libblkid/src/superblocks/superblocks.h \
+	libblkid/src/superblocks/swap.c \
+	libblkid/src/superblocks/sysv.c \
+	libblkid/src/superblocks/ubifs.c \
+	libblkid/src/superblocks/udf.c \
+	libblkid/src/superblocks/ufs.c \
+	libblkid/src/superblocks/vfat.c \
+	libblkid/src/superblocks/via_raid.c \
+	libblkid/src/superblocks/vmfs.c \
+	libblkid/src/superblocks/vxfs.c \
+	libblkid/src/superblocks/xfs.c \
+	libblkid/src/superblocks/zfs.c \
+	\
+	libblkid/src/topology/topology.c \
+	libblkid/src/topology/topology.h
+
+if LINUX
+libblkid_la_SOURCES += \
+	libblkid/src/topology/dm.c \
+	libblkid/src/topology/evms.c \
+	libblkid/src/topology/ioctl.c \
+	libblkid/src/topology/lvm.c \
+	libblkid/src/topology/md.c \
+	libblkid/src/topology/sysfs.c
+endif
+
+nodist_libblkid_la_SOURCES = libblkid/src/blkid.h
+libblkid_la_LIBADD = libcommon.la
+
+
+libblkid_la_CFLAGS = \
+	-I$(ul_libblkid_incdir) \
+	-I$(top_srcdir)/libblkid/src
+
+if BUILD_LIBUUID
+libblkid_la_LIBADD += libuuid.la
+libblkid_la_CFLAGS += -I$(ul_libuuid_incdir)
+endif
+
+
+libblkid_la_DEPENDENCIES = \
+	$(libblkid_la_LIBADD) \
+	libblkid/src/blkid.sym \
+	libblkid/src/blkid.h.in
+
+libblkid_la_LDFLAGS = \
+	-Wl,--version-script=$(top_srcdir)/libblkid/src/blkid.sym \
+	-version-info $(LIBBLKID_VERSION_INFO)
+
+EXTRA_DIST += \
+	libblkid/src/blkid.sym \
+	libblkid/src/blkid.h.in
+
+if BUILD_LIBBLKID_TESTS
+check_PROGRAMS += \
+	test_blkid_cache \
+	test_blkid_config \
+	test_blkid_dev \
+	test_blkid_devname \
+	test_blkid_devno \
+	test_blkid_evaluate \
+	test_blkid_read \
+	test_blkid_resolve \
+	test_blkid_save \
+	test_blkid_tag \
+	test_blkid_verify
+
+blkid_tests_cflags  = -DTEST_PROGRAM $(libblkid_la_CFLAGS)
+blkid_tests_ldflags =
+blkid_tests_ldadd   = libblkid.la
+
+if BUILD_LIBUUID
+blkid_tests_ldflags += libuuid.la
+blkid_tests_ldadd   += $(UUID_LIBS)
+endif
+
+blkid_tests_ldflags += -static
+
+test_blkid_cache_SOURCES = libblkid/src/cache.c
+test_blkid_cache_CFLAGS = $(blkid_tests_cflags)
+test_blkid_cache_LDFLAGS = $(blkid_tests_ldflags)
+test_blkid_cache_LDADD = $(blkid_tests_ldadd)
+
+test_blkid_config_SOURCES = libblkid/src/config.c
+test_blkid_config_CFLAGS = $(blkid_tests_cflags)
+test_blkid_config_LDFLAGS = $(blkid_tests_ldflags)
+test_blkid_config_LDADD = $(blkid_tests_ldadd)
+
+test_blkid_dev_SOURCES = libblkid/src/dev.c
+test_blkid_dev_CFLAGS = $(blkid_tests_cflags)
+test_blkid_dev_LDFLAGS = $(blkid_tests_ldflags)
+test_blkid_dev_LDADD = $(blkid_tests_ldadd)
+
+test_blkid_devname_SOURCES = libblkid/src/devname.c
+test_blkid_devname_CFLAGS = $(blkid_tests_cflags)
+test_blkid_devname_LDFLAGS = $(blkid_tests_ldflags)
+test_blkid_devname_LDADD = $(blkid_tests_ldadd)
+
+test_blkid_devno_SOURCES = libblkid/src/devno.c
+test_blkid_devno_CFLAGS = $(blkid_tests_cflags)
+test_blkid_devno_LDFLAGS = $(blkid_tests_ldflags)
+test_blkid_devno_LDADD = $(blkid_tests_ldadd)
+
+test_blkid_evaluate_SOURCES = libblkid/src/evaluate.c
+test_blkid_evaluate_CFLAGS = $(blkid_tests_cflags)
+test_blkid_evaluate_LDFLAGS = $(blkid_tests_ldflags)
+test_blkid_evaluate_LDADD = $(blkid_tests_ldadd)
+
+test_blkid_read_SOURCES = libblkid/src/read.c
+test_blkid_read_CFLAGS = $(blkid_tests_cflags)
+test_blkid_read_LDFLAGS = $(blkid_tests_ldflags)
+test_blkid_read_LDADD = $(blkid_tests_ldadd)
+
+test_blkid_resolve_SOURCES = libblkid/src/resolve.c
+test_blkid_resolve_CFLAGS = $(blkid_tests_cflags)
+test_blkid_resolve_LDFLAGS = $(blkid_tests_ldflags)
+test_blkid_resolve_LDADD = $(blkid_tests_ldadd)
+
+test_blkid_save_SOURCES = libblkid/src/save.c
+test_blkid_save_CFLAGS = $(blkid_tests_cflags)
+test_blkid_save_LDFLAGS = $(blkid_tests_ldflags)
+test_blkid_save_LDADD = $(blkid_tests_ldadd)
+
+test_blkid_tag_SOURCES = libblkid/src/tag.c
+test_blkid_tag_CFLAGS = $(blkid_tests_cflags)
+test_blkid_tag_LDFLAGS = $(blkid_tests_ldflags)
+test_blkid_tag_LDADD = $(blkid_tests_ldadd)
+
+test_blkid_verify_SOURCES = libblkid/src/verify.c
+test_blkid_verify_CFLAGS = $(blkid_tests_cflags)
+test_blkid_verify_LDFLAGS = $(blkid_tests_ldflags)
+test_blkid_verify_LDADD = $(blkid_tests_ldadd)
+
+endif # BUILD_LIBBLKID_TESTS
+
+
+# move lib from $(usrlib_execdir) to $(libdir) if needed
+install-exec-hook-libblkid:
+	if test "$(usrlib_execdir)" != "$(libdir)"; then \
+		mkdir -p $(DESTDIR)$(libdir); \
+		mv $(DESTDIR)$(usrlib_execdir)/libblkid.so.* $(DESTDIR)$(libdir); \
+		so_img_name=$$(readlink $(DESTDIR)$(usrlib_execdir)/libblkid.so); \
+		so_img_rel_target=$$(echo $(usrlib_execdir) | sed 's,\(^/\|\)[^/][^/]*,..,g'); \
+		(cd $(DESTDIR)$(usrlib_execdir) && \
+			rm -f libblkid.so && \
+			$(LN_S) $$so_img_rel_target$(libdir)/$$so_img_name libblkid.so); \
+	fi
+
+uninstall-hook-libblkid:
+	rm -f $(DESTDIR)$(libdir)/libblkid.so*
+
+INSTALL_EXEC_HOOKS += install-exec-hook-libblkid
+UNINSTALL_HOOKS += uninstall-hook-libblkid
diff --git a/src/blkid.h.in b/src/blkid.h.in
new file mode 100644
index 0000000..8f6d336
--- /dev/null
+++ b/src/blkid.h.in
@@ -0,0 +1,351 @@
+/*
+ * blkid.h - Interface for libblkid, a library to identify block devices
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _BLKID_BLKID_H
+#define _BLKID_BLKID_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BLKID_VERSION   "@LIBBLKID_VERSION@"
+#define BLKID_DATE      "@LIBBLKID_DATE@"
+
+/**
+ * blkid_dev:
+ *
+ * The device object keeps information about one device
+ */
+typedef struct blkid_struct_dev *blkid_dev;
+
+/**
+ * blkid_cache:
+ *
+ * information about all system devices
+ */
+typedef struct blkid_struct_cache *blkid_cache;
+
+/**
+ * blkid_probe:
+ *
+ * low-level probing setting
+ */
+typedef struct blkid_struct_probe *blkid_probe;
+
+/**
+ * blkid_topology:
+ *
+ * device topology information
+ */
+typedef struct blkid_struct_topology *blkid_topology;
+
+/**
+ * blkid_partlist
+ *
+ * list of all detected partitions and partitions tables
+ */
+typedef struct blkid_struct_partlist *blkid_partlist;
+
+/**
+ * blkid_partition:
+ *
+ * information about a partition
+ */
+typedef struct blkid_struct_partition *blkid_partition;
+
+/**
+ * blkid_parttable:
+ *
+ * information about a partition table
+ */
+typedef struct blkid_struct_parttable *blkid_parttable;
+
+/**
+ * blkid_loff_t:
+ *
+ * 64-bit signed number for offsets and sizes
+ */
+typedef int64_t blkid_loff_t;
+
+/**
+ * blkid_tag_iterate:
+ *
+ * tags iterator for high-level (blkid_cache) API
+ */
+typedef struct blkid_struct_tag_iterate *blkid_tag_iterate;
+
+/**
+ * blkid_dev_iterate:
+ *
+ * devices iterator for high-level (blkid_cache) API
+ */
+typedef struct blkid_struct_dev_iterate *blkid_dev_iterate;
+
+/*
+ * Flags for blkid_get_dev
+ *
+ * BLKID_DEV_CREATE	Create an empty device structure if not found
+ *			in the cache.
+ * BLKID_DEV_VERIFY	Make sure the device structure corresponds
+ *			with reality.
+ * BLKID_DEV_FIND	Just look up a device entry, and return NULL
+ *			if it is not found.
+ * BLKID_DEV_NORMAL	Get a valid device structure, either from the
+ *			cache or by probing the device.
+ */
+#define BLKID_DEV_FIND		0x0000
+#define BLKID_DEV_CREATE	0x0001
+#define BLKID_DEV_VERIFY	0x0002
+#define BLKID_DEV_NORMAL	(BLKID_DEV_CREATE | BLKID_DEV_VERIFY)
+
+/* cache.c */
+extern void blkid_put_cache(blkid_cache cache);
+extern int blkid_get_cache(blkid_cache *cache, const char *filename);
+extern void blkid_gc_cache(blkid_cache cache);
+
+/* dev.c */
+extern const char *blkid_dev_devname(blkid_dev dev);
+
+extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache);
+extern int blkid_dev_set_search(blkid_dev_iterate iter,
+				char *search_type, char *search_value);
+extern int blkid_dev_next(blkid_dev_iterate iterate, blkid_dev *dev);
+extern void blkid_dev_iterate_end(blkid_dev_iterate iterate);
+
+/* devno.c */
+extern char *blkid_devno_to_devname(dev_t devno);
+extern int blkid_devno_to_wholedisk(dev_t dev, char *diskname,
+                        size_t len, dev_t *diskdevno);
+
+/* devname.c */
+extern int blkid_probe_all(blkid_cache cache);
+extern int blkid_probe_all_new(blkid_cache cache);
+extern int blkid_probe_all_removable(blkid_cache cache);
+extern blkid_dev blkid_get_dev(blkid_cache cache, const char *devname,
+			       int flags);
+
+/* getsize.c */
+extern blkid_loff_t blkid_get_dev_size(int fd);
+
+/* verify.c */
+extern blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev);
+
+/* read.c */
+
+/* resolve.c */
+extern char *blkid_get_tag_value(blkid_cache cache, const char *tagname,
+				       const char *devname);
+extern char *blkid_get_devname(blkid_cache cache, const char *token,
+			       const char *value);
+
+/* tag.c */
+extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev);
+extern int blkid_tag_next(blkid_tag_iterate iterate,
+			      const char **type, const char **value);
+extern void blkid_tag_iterate_end(blkid_tag_iterate iterate);
+extern int blkid_dev_has_tag(blkid_dev dev, const char *type,
+			     const char *value);
+extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
+					 const char *type,
+					 const char *value);
+extern int blkid_parse_tag_string(const char *token, char **ret_type,
+				  char **ret_val);
+
+/* version.c */
+extern int blkid_parse_version_string(const char *ver_string);
+extern int blkid_get_library_version(const char **ver_string,
+				     const char **date_string);
+
+/* encode.c */
+extern int blkid_encode_string(const char *str, char *str_enc, size_t len);
+extern int blkid_safe_string(const char *str, char *str_safe, size_t len);
+
+/* evaluate.c */
+extern int blkid_send_uevent(const char *devname, const char *action);
+extern char *blkid_evaluate_tag(const char *token, const char *value,
+				blkid_cache *cache);
+extern char *blkid_evaluate_spec(const char *spec, blkid_cache *cache);
+
+/* probe.c */
+extern blkid_probe blkid_new_probe(void);
+extern blkid_probe blkid_new_probe_from_filename(const char *filename);
+extern void blkid_free_probe(blkid_probe pr);
+extern void blkid_reset_probe(blkid_probe pr);
+
+extern int blkid_probe_set_device(blkid_probe pr, int fd,
+	                blkid_loff_t off, blkid_loff_t size);
+
+extern dev_t blkid_probe_get_devno(blkid_probe pr);
+extern dev_t blkid_probe_get_wholedisk_devno(blkid_probe pr);
+extern int blkid_probe_is_wholedisk(blkid_probe pr);
+
+extern blkid_loff_t blkid_probe_get_size(blkid_probe pr);
+extern blkid_loff_t blkid_probe_get_offset(blkid_probe pr);
+extern unsigned int blkid_probe_get_sectorsize(blkid_probe pr);
+extern blkid_loff_t blkid_probe_get_sectors(blkid_probe pr);
+
+extern int blkid_probe_get_fd(blkid_probe pr);
+
+/*
+ * superblocks probing
+ */
+extern int blkid_known_fstype(const char *fstype);
+extern int blkid_superblocks_get_name(size_t idx, const char **name, int *usage);
+
+extern int blkid_probe_enable_superblocks(blkid_probe pr, int enable);
+
+#define BLKID_SUBLKS_LABEL	(1 << 1) /* read LABEL from superblock */
+#define BLKID_SUBLKS_LABELRAW	(1 << 2) /* read and define LABEL_RAW result value*/
+#define BLKID_SUBLKS_UUID	(1 << 3) /* read UUID from superblock */
+#define BLKID_SUBLKS_UUIDRAW	(1 << 4) /* read and define UUID_RAW result value */
+#define BLKID_SUBLKS_TYPE	(1 << 5) /* define TYPE result value */
+#define BLKID_SUBLKS_SECTYPE	(1 << 6) /* define compatible fs type (second type) */
+#define BLKID_SUBLKS_USAGE	(1 << 7) /* define USAGE result value */
+#define BLKID_SUBLKS_VERSION	(1 << 8) /* read FS type from superblock */
+#define BLKID_SUBLKS_MAGIC	(1 << 9) /* define SBMAGIC and SBMAGIC_OFFSET */
+
+#define BLKID_SUBLKS_DEFAULT	(BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | \
+				 BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE)
+
+extern int blkid_probe_set_superblocks_flags(blkid_probe pr, int flags);
+
+extern int blkid_probe_reset_superblocks_filter(blkid_probe pr);
+extern int blkid_probe_invert_superblocks_filter(blkid_probe pr);
+
+/**
+ * BLKID_FLTR_NOTIN
+ */
+#define BLKID_FLTR_NOTIN		1
+/**
+ * BLKID_FLTR_ONLYIN
+ */
+#define BLKID_FLTR_ONLYIN		2
+extern int blkid_probe_filter_superblocks_type(blkid_probe pr, int flag, char *names[]);
+
+#define BLKID_USAGE_FILESYSTEM		(1 << 1)
+#define BLKID_USAGE_RAID		(1 << 2)
+#define BLKID_USAGE_CRYPTO		(1 << 3)
+#define BLKID_USAGE_OTHER		(1 << 4)
+extern int blkid_probe_filter_superblocks_usage(blkid_probe pr, int flag, int usage);
+
+/*
+ * topology probing
+ */
+extern int blkid_probe_enable_topology(blkid_probe pr, int enable);
+
+/* binary interface */
+extern blkid_topology blkid_probe_get_topology(blkid_probe pr);
+
+extern unsigned long blkid_topology_get_alignment_offset(blkid_topology tp);
+extern unsigned long blkid_topology_get_minimum_io_size(blkid_topology tp);
+extern unsigned long blkid_topology_get_optimal_io_size(blkid_topology tp);
+extern unsigned long blkid_topology_get_logical_sector_size(blkid_topology tp);
+extern unsigned long blkid_topology_get_physical_sector_size(blkid_topology tp);
+
+/*
+ * partitions probing
+ */
+extern int blkid_known_pttype(const char *pttype);
+extern int blkid_probe_enable_partitions(blkid_probe pr, int enable);
+
+extern int blkid_probe_reset_partitions_filter(blkid_probe pr);
+extern int blkid_probe_invert_partitions_filter(blkid_probe pr);
+extern int blkid_probe_filter_partitions_type(blkid_probe pr, int flag, char *names[]);
+
+
+/* partitions probing flags */
+#define BLKID_PARTS_FORCE_GPT		(1 << 1)
+#define BLKID_PARTS_ENTRY_DETAILS	(1 << 2)
+#define BLKID_PARTS_MAGIC		(1 << 3)
+extern int blkid_probe_set_partitions_flags(blkid_probe pr, int flags);
+
+/* binary interface */
+extern blkid_partlist blkid_probe_get_partitions(blkid_probe pr);
+
+extern int blkid_partlist_numof_partitions(blkid_partlist ls);
+extern blkid_parttable blkid_partlist_get_table(blkid_partlist ls);
+extern blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n);
+extern blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno);
+
+extern blkid_parttable blkid_partition_get_table(blkid_partition par);
+extern const char *blkid_partition_get_name(blkid_partition par);
+extern const char *blkid_partition_get_uuid(blkid_partition par);
+extern int blkid_partition_get_partno(blkid_partition par);
+extern blkid_loff_t blkid_partition_get_start(blkid_partition par);
+extern blkid_loff_t blkid_partition_get_size(blkid_partition par);
+extern int blkid_partition_get_type(blkid_partition par);
+extern const char *blkid_partition_get_type_string(blkid_partition par);
+extern unsigned long long blkid_partition_get_flags(blkid_partition par);
+extern int blkid_partition_is_logical(blkid_partition par);
+extern int blkid_partition_is_extended(blkid_partition par);
+extern int blkid_partition_is_primary(blkid_partition par);
+
+extern const char *blkid_parttable_get_type(blkid_parttable tab);
+extern blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab);
+extern blkid_partition blkid_parttable_get_parent(blkid_parttable tab);
+
+/*
+ * NAME=value low-level interface
+ */
+extern int blkid_do_probe(blkid_probe pr);
+extern int blkid_do_safeprobe(blkid_probe pr);
+extern int blkid_do_fullprobe(blkid_probe pr);
+
+extern int blkid_probe_numof_values(blkid_probe pr);
+extern int blkid_probe_get_value(blkid_probe pr, int num, const char **name,
+                        const char **data, size_t *len);
+extern int blkid_probe_lookup_value(blkid_probe pr, const char *name,
+                        const char **data, size_t *len);
+extern int blkid_probe_has_value(blkid_probe pr, const char *name);
+
+extern int blkid_do_wipe(blkid_probe pr, int dryrun);
+
+/*
+ * Deprecated functions/macros
+ */
+#ifndef BLKID_DISABLE_DEPRECATED
+
+#define BLKID_PROBREQ_LABEL     BLKID_SUBLKS_LABEL
+#define BLKID_PROBREQ_LABELRAW  BLKID_SUBLKS_LABELRAW
+#define BLKID_PROBREQ_UUID      BLKID_SUBLKS_UUID
+#define BLKID_PROBREQ_UUIDRAW   BLKID_SUBLKS_UUIDRAW
+#define BLKID_PROBREQ_TYPE      BLKID_SUBLKS_TYPE
+#define BLKID_PROBREQ_SECTYPE   BLKID_SUBLKS_SECTYPE
+#define BLKID_PROBREQ_USAGE     BLKID_SUBLKS_USAGE
+#define BLKID_PROBREQ_VERSION   BLKID_SUBLKS_VERSION
+
+extern int blkid_probe_set_request(blkid_probe pr, int flags);
+extern int blkid_probe_filter_usage(blkid_probe pr, int flag, int usage);
+extern int blkid_probe_filter_types(blkid_probe pr, int flag, char *names[]);
+extern int blkid_probe_invert_filter(blkid_probe pr);
+extern int blkid_probe_reset_filter(blkid_probe pr);
+
+#endif /* BLKID_DISABLE_DEPRECATED */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BLKID_BLKID_H */
diff --git a/src/blkid.sym b/src/blkid.sym
new file mode 100644
index 0000000..62f981f
--- /dev/null
+++ b/src/blkid.sym
@@ -0,0 +1,148 @@
+/*
+ * The symbol versioning ensures that a new application requiring symbol foo()
+ * can't run with old libblkid.so not providing foo() - the global SONAME
+ * version info can't enforce this since we never change the SONAME.
+ *
+ * The original libblkid from e2fsprogs (<=1.41.4) does not to use
+ * symbol versioning -- all the original symbols are in BLKID_1.0 now.
+ */
+BLKID_1.0 {
+global:
+	blkid_dev_devname;
+	blkid_dev_has_tag;
+	blkid_dev_iterate_begin;
+	blkid_dev_iterate_end;
+	blkid_dev_next;
+	blkid_devno_to_devname;
+	blkid_dev_set_search;
+	blkid_find_dev_with_tag;
+	blkid_gc_cache;
+	blkid_get_cache;
+	blkid_get_dev;
+	blkid_get_devname;
+	blkid_get_dev_size;
+	blkid_get_library_version;
+	blkid_get_tag_value;
+	blkid_known_fstype;
+	blkid_parse_tag_string;
+	blkid_parse_version_string;
+	blkid_probe_all;
+	blkid_probe_all_new;
+	blkid_put_cache;
+	blkid_tag_iterate_begin;
+	blkid_tag_iterate_end;
+	blkid_tag_next;
+	blkid_verify;
+local:
+	*;
+};
+
+
+/*
+ * symbols since util-linux 2.15
+ */
+BLKID_2.15 {
+global:
+	blkid_do_probe;
+	blkid_do_safeprobe;
+	blkid_encode_string;
+	blkid_evaluate_tag;
+	blkid_free_probe;
+	blkid_new_probe;
+	blkid_probe_filter_types;
+	blkid_probe_filter_usage;
+	blkid_probe_get_value;
+	blkid_probe_has_value;
+	blkid_probe_invert_filter;
+	blkid_probe_lookup_value;
+	blkid_probe_numof_values;
+	blkid_probe_reset_filter;
+	blkid_probe_set_device;
+	blkid_probe_set_request;
+	blkid_reset_probe;
+	blkid_safe_string;
+	blkid_send_uevent;
+} BLKID_1.0;
+
+/*
+ * symbols since util-linux 2.17
+ */
+BLKID_2.17 {
+global:
+	blkid_devno_to_wholedisk;
+	blkid_do_fullprobe;
+	blkid_known_pttype;
+	blkid_new_probe_from_filename;
+	blkid_partition_get_name;
+	blkid_partition_get_partno;
+	blkid_partition_get_size;
+	blkid_partition_get_start;
+	blkid_partition_get_table;
+	blkid_partition_get_type;
+	blkid_partition_get_type_string;
+	blkid_partition_get_uuid;
+	blkid_partition_is_extended;
+	blkid_partition_is_logical;
+	blkid_partition_is_primary;
+	blkid_partlist_get_partition;
+	blkid_partlist_numof_partitions;
+	blkid_parttable_get_offset;
+	blkid_parttable_get_parent;
+	blkid_parttable_get_type;
+	blkid_probe_enable_partitions;
+	blkid_probe_enable_superblocks;
+	blkid_probe_enable_topology;
+	blkid_probe_filter_partitions_type;
+	blkid_probe_filter_superblocks_type;
+	blkid_probe_filter_superblocks_usage;
+	blkid_probe_get_devno;
+	blkid_probe_get_partitions;
+	blkid_probe_get_sectorsize;
+	blkid_probe_get_sectors;
+	blkid_probe_get_size;
+	blkid_probe_get_topology;
+	blkid_probe_invert_partitions_filter;
+	blkid_probe_invert_superblocks_filter;
+	blkid_probe_reset_partitions_filter;
+	blkid_probe_reset_superblocks_filter;
+	blkid_probe_set_partitions_flags;
+	blkid_probe_set_superblocks_flags;
+	blkid_topology_get_alignment_offset;
+	blkid_topology_get_logical_sector_size;
+	blkid_topology_get_minimum_io_size;
+	blkid_topology_get_optimal_io_size;
+	blkid_topology_get_physical_sector_size;
+} BLKID_2.15;
+
+/*
+ * symbols since util-linux 2.18
+ */
+BLKID_2.18 {
+global:
+	blkid_partition_get_flags;
+	blkid_partlist_devno_to_partition;
+	blkid_partlist_get_table;
+	blkid_probe_all_removable;
+	blkid_probe_get_fd;
+	blkid_probe_get_offset;
+	blkid_probe_get_wholedisk_devno;
+	blkid_probe_is_wholedisk;
+} BLKID_2.17;
+
+/*
+ * symbols since util-linux 2.20
+ */
+BLKID_2.20 {
+global:
+	blkid_evaluate_spec;
+	blkid_superblocks_get_name;
+} BLKID_2.18;
+
+/*
+ * symbols since util-linux 2.21
+ */
+BLKID_2.21 {
+global:
+	blkid_do_wipe;
+} BLKID_2.20;
+
diff --git a/src/blkidP.h b/src/blkidP.h
new file mode 100644
index 0000000..92f2539
--- /dev/null
+++ b/src/blkidP.h
@@ -0,0 +1,481 @@
+/*
+ * blkidP.h - Internal interfaces for libblkid
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifndef _BLKID_BLKIDP_H
+#define _BLKID_BLKIDP_H
+
+/* support debug output if LIBBLKID_DEBUG env. variable is set */
+#define CONFIG_BLKID_DEBUG 1
+
+/* Always confirm that /dev/disk-by symlinks match with LABEL/UUID on device */
+/* #define CONFIG_BLKID_VERIFY_UDEV 1 */
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+#include "c.h"
+#include "bitops.h"	/* $(top_srcdir)/include/ */
+#include "blkdev.h"
+
+#include "blkid.h"
+#include "list.h"
+
+/*
+ * This describes the attributes of a specific device.
+ * We can traverse all of the tags by bid_tags (linking to the tag bit_names).
+ * The bid_label and bid_uuid fields are shortcuts to the LABEL and UUID tag
+ * values, if they exist.
+ */
+struct blkid_struct_dev
+{
+	struct list_head	bid_devs;	/* All devices in the cache */
+	struct list_head	bid_tags;	/* All tags for this device */
+	blkid_cache		bid_cache;	/* Dev belongs to this cache */
+	char			*bid_name;	/* Device inode pathname */
+	char			*bid_type;	/* Preferred device TYPE */
+	int			bid_pri;	/* Device priority */
+	dev_t			bid_devno;	/* Device major/minor number */
+	time_t			bid_time;	/* Last update time of device */
+	suseconds_t		bid_utime;	/* Last update time (microseconds) */
+	unsigned int		bid_flags;	/* Device status bitflags */
+	char			*bid_label;	/* Shortcut to device LABEL */
+	char			*bid_uuid;	/* Shortcut to binary UUID */
+};
+
+#define BLKID_BID_FL_VERIFIED	0x0001	/* Device data validated from disk */
+#define BLKID_BID_FL_INVALID	0x0004	/* Device is invalid */
+#define BLKID_BID_FL_REMOVABLE	0x0008	/* Device added by blkid_probe_all_removable() */
+
+/*
+ * Each tag defines a NAME=value pair for a particular device.  The tags
+ * are linked via bit_names for a single device, so that traversing the
+ * names list will get you a list of all tags associated with a device.
+ * They are also linked via bit_values for all devices, so one can easily
+ * search all tags with a given NAME for a specific value.
+ */
+struct blkid_struct_tag
+{
+	struct list_head	bit_tags;	/* All tags for this device */
+	struct list_head	bit_names;	/* All tags with given NAME */
+	char			*bit_name;	/* NAME of tag (shared) */
+	char			*bit_val;	/* value of tag */
+	blkid_dev		bit_dev;	/* pointer to device */
+};
+typedef struct blkid_struct_tag *blkid_tag;
+
+/*
+ * Chain IDs
+ */
+enum {
+	BLKID_CHAIN_SUBLKS,	/* FS/RAID superblocks (enabled by default) */
+	BLKID_CHAIN_TOPLGY,	/* Block device topology */
+	BLKID_CHAIN_PARTS,	/* Partition tables */
+
+	BLKID_NCHAINS		/* number of chains */
+};
+
+struct blkid_chain {
+	const struct blkid_chaindrv *driver;	/* chain driver */
+
+	int		enabled;	/* boolean */
+	int		flags;		/* BLKID_<chain>_* */
+	int		binary;		/* boolean */
+	int		idx;		/* index of the current prober (or -1) */
+	unsigned long	*fltr;		/* filter or NULL */
+	void		*data;		/* private chain data or NULL */
+};
+
+/*
+ * Chain driver
+ */
+struct blkid_chaindrv {
+	const size_t	id;		/* BLKID_CHAIN_* */
+	const char	*name;		/* name of chain (for debug purpose) */
+	const int	dflt_flags;	/* default chain flags */
+	const int	dflt_enabled;	/* default enabled boolean */
+	int		has_fltr;	/* boolean */
+
+	const struct blkid_idinfo **idinfos; /* description of probing functions */
+	const size_t	nidinfos;	/* number of idinfos */
+
+	/* driver operations */
+	int		(*probe)(blkid_probe, struct blkid_chain *);
+	int		(*safeprobe)(blkid_probe, struct blkid_chain *);
+	void		(*free_data)(blkid_probe, void *);
+};
+
+/*
+ * Low-level probe result
+ */
+#define BLKID_PROBVAL_BUFSIZ	64
+
+#define BLKID_NVALS_SUBLKS	14
+#define BLKID_NVALS_TOPLGY	5
+#define BLKID_NVALS_PARTS	13
+
+/* Max number of all values in probing result */
+#define BLKID_NVALS             (BLKID_NVALS_SUBLKS + \
+				 BLKID_NVALS_TOPLGY + \
+				 BLKID_NVALS_PARTS)
+
+struct blkid_prval
+{
+	const char	*name;			/* value name */
+	unsigned char	data[BLKID_PROBVAL_BUFSIZ]; /* value data */
+	size_t		len;			/* length of value data */
+
+	struct blkid_chain	*chain;		/* owner */
+};
+
+/*
+ * Filesystem / Raid magic strings
+ */
+struct blkid_idmag
+{
+	const char	*magic;		/* magic string */
+	unsigned int	len;		/* length of magic */
+
+	long		kboff;		/* kilobyte offset of superblock */
+	unsigned int	sboff;		/* byte offset within superblock */
+};
+
+/*
+ * Filesystem / Raid description
+ */
+struct blkid_idinfo
+{
+	const char	*name;		/* fs, raid or partition table name */
+	int		usage;		/* BLKID_USAGE_* flag */
+	int		flags;		/* BLKID_IDINFO_* flags */
+	int		minsz;		/* minimal device size */
+
+					/* probe function */
+	int		(*probefunc)(blkid_probe pr, const struct blkid_idmag *mag);
+
+	struct blkid_idmag	magics[];	/* NULL or array with magic strings */
+};
+
+#define BLKID_NONE_MAGIC	{{ NULL }}
+
+/*
+ * tolerant FS - can share the same device with more filesystems (e.g. typical
+ * on CD-ROMs). We need this flag to detect ambivalent results (e.g. valid fat
+ * and valid linux swap on the same device).
+ */
+#define BLKID_IDINFO_TOLERANT	(1 << 1)
+
+struct blkid_bufinfo {
+	unsigned char		*data;
+	blkid_loff_t		off;
+	blkid_loff_t		len;
+	struct list_head	bufs;	/* list of buffers */
+};
+
+/*
+ * Low-level probing control struct
+ */
+struct blkid_struct_probe
+{
+	int			fd;		/* device file descriptor */
+	blkid_loff_t		off;		/* begin of data on the device */
+	blkid_loff_t		size;		/* end of data on the device */
+
+	dev_t			devno;		/* device number (st.st_rdev) */
+	dev_t			disk_devno;	/* devno of the whole-disk or 0 */
+	unsigned int		blkssz;		/* sector size (BLKSSZGET ioctl) */
+	mode_t			mode;		/* struct stat.sb_mode */
+
+	int			flags;		/* private libray flags */
+	int			prob_flags;	/* always zeroized by blkid_do_*() */
+
+	blkid_loff_t		wipe_off;	/* begin of the wiped area */
+	blkid_loff_t		wipe_size;	/* size of the wiped area */
+	struct blkid_chain	*wipe_chain;	/* superblock, partition, ... */
+
+	struct list_head	buffers;	/* list of buffers */
+
+	struct blkid_chain	chains[BLKID_NCHAINS];	/* array of chains */
+	struct blkid_chain	*cur_chain;		/* current chain */
+
+	struct blkid_prval	vals[BLKID_NVALS];	/* results */
+	int			nvals;		/* number of assigned vals */
+
+	struct blkid_struct_probe *parent;	/* for clones */
+	struct blkid_struct_probe *disk_probe;	/* whole-disk probing */
+};
+
+/* private flags library flags */
+#define BLKID_FL_PRIVATE_FD	(1 << 1)	/* see blkid_new_probe_from_filename() */
+#define BLKID_FL_TINY_DEV	(1 << 2)	/* <= 1.47MiB (floppy or so) */
+#define BLKID_FL_CDROM_DEV	(1 << 3)	/* is a CD/DVD drive */
+
+/* private per-probing flags */
+#define BLKID_PROBE_FL_IGNORE_PT (1 << 1)	/* ignore partition table */
+
+extern blkid_probe blkid_clone_probe(blkid_probe parent);
+extern blkid_probe blkid_probe_get_wholedisk_probe(blkid_probe pr);
+
+/*
+ * Evaluation methods (for blkid_eval_* API)
+ */
+enum {
+	BLKID_EVAL_UDEV = 0,
+	BLKID_EVAL_SCAN,
+
+	__BLKID_EVAL_LAST
+};
+
+/*
+ * Library config options
+ */
+struct blkid_config {
+	int eval[__BLKID_EVAL_LAST];	/* array with EVALUATION=<udev,cache> options */
+	int nevals;			/* number of elems in eval array */
+	int uevent;			/* SEND_UEVENT=<yes|not> option */
+	char *cachefile;		/* CACHE_FILE=<path> option */
+};
+
+extern struct blkid_config *blkid_read_config(const char *filename);
+extern void blkid_free_config(struct blkid_config *conf);
+
+/*
+ * Minimum number of seconds between device probes, even when reading
+ * from the cache.  This is to avoid re-probing all devices which were
+ * just probed by another program that does not share the cache.
+ */
+#define BLKID_PROBE_MIN		2
+
+/*
+ * Time in seconds an entry remains verified in the in-memory cache
+ * before being reverified (in case of long-running processes that
+ * keep a cache in memory and continue to use it for a long time).
+ */
+#define BLKID_PROBE_INTERVAL	200
+
+/* This describes an entire blkid cache file and probed devices.
+ * We can traverse all of the found devices via bic_list.
+ * We can traverse all of the tag types by bic_tags, which hold empty tags
+ * for each tag type.  Those tags can be used as list_heads for iterating
+ * through all devices with a specific tag type (e.g. LABEL).
+ */
+struct blkid_struct_cache
+{
+	struct list_head	bic_devs;	/* List head of all devices */
+	struct list_head	bic_tags;	/* List head of all tag types */
+	time_t			bic_time;	/* Last probe time */
+	time_t			bic_ftime;	/* Mod time of the cachefile */
+	unsigned int		bic_flags;	/* Status flags of the cache */
+	char			*bic_filename;	/* filename of cache */
+	blkid_probe		probe;		/* low-level probing stuff */
+};
+
+#define BLKID_BIC_FL_PROBED	0x0002	/* We probed /proc/partition devices */
+#define BLKID_BIC_FL_CHANGED	0x0004	/* Cache has changed from disk */
+
+extern char *blkid_strdup(const char *s);
+extern char *blkid_strndup(const char *s, const int length);
+extern char *blkid_strconcat(const char *a, const char *b, const char *c);
+
+/* config file */
+#define BLKID_CONFIG_FILE	"/etc/blkid.conf"
+
+/* cache file on systemds with /run */
+#define BLKID_RUNTIME_TOPDIR	"/run"
+#define BLKID_RUNTIME_DIR	BLKID_RUNTIME_TOPDIR "/blkid"
+#define BLKID_CACHE_FILE	BLKID_RUNTIME_DIR "/blkid.tab"
+
+/* old systems */
+#define BLKID_CACHE_FILE_OLD	"/etc/blkid.tab"
+
+#define BLKID_ERR_IO	 5
+#define BLKID_ERR_PROC	 9
+#define BLKID_ERR_MEM	12
+#define BLKID_ERR_CACHE	14
+#define BLKID_ERR_DEV	19
+#define BLKID_ERR_PARAM	22
+#define BLKID_ERR_BIG	27
+
+/*
+ * Priority settings for different types of devices
+ */
+#define BLKID_PRI_UBI	50
+#define BLKID_PRI_DM	40
+#define BLKID_PRI_EVMS	30
+#define BLKID_PRI_LVM	20
+#define BLKID_PRI_MD	10
+
+#if defined(TEST_PROGRAM) && !defined(CONFIG_BLKID_DEBUG)
+#define CONFIG_BLKID_DEBUG
+#endif
+
+#define DEBUG_CACHE	0x0001
+#define DEBUG_DUMP	0x0002
+#define DEBUG_DEV	0x0004
+#define DEBUG_DEVNAME	0x0008
+#define DEBUG_DEVNO	0x0010
+#define DEBUG_PROBE	0x0020
+#define DEBUG_READ	0x0040
+#define DEBUG_RESOLVE	0x0080
+#define DEBUG_SAVE	0x0100
+#define DEBUG_TAG	0x0200
+#define DEBUG_LOWPROBE	0x0400
+#define DEBUG_CONFIG	0x0800
+#define DEBUG_EVALUATE	0x1000
+#define DEBUG_INIT	0x8000
+#define DEBUG_ALL	0xFFFF
+
+#ifdef CONFIG_BLKID_DEBUG
+extern int blkid_debug_mask;
+extern void blkid_init_debug(int mask);
+extern void blkid_debug_dump_dev(blkid_dev dev);
+extern void blkid_debug_dump_tag(blkid_tag tag);
+
+#define DBG(m,x)	do { if ((m) & blkid_debug_mask) x; } while (0)
+
+#else /* !CONFIG_BLKID_DEBUG */
+#define DBG(m,x)
+#define blkid_init_debug(x)
+#endif /* CONFIG_BLKID_DEBUG */
+
+/* devno.c */
+struct dir_list {
+	char	*name;
+	struct dir_list *next;
+};
+extern void blkid__scan_dir(char *, dev_t, struct dir_list **, char **);
+extern int blkid_driver_has_major(const char *drvname, int major);
+
+/* lseek.c */
+extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence);
+
+/* read.c */
+extern void blkid_read_cache(blkid_cache cache);
+
+/* save.c */
+extern int blkid_flush_cache(blkid_cache cache);
+
+/* cache */
+extern char *blkid_safe_getenv(const char *arg);
+extern char *blkid_get_cache_filename(struct blkid_config *conf);
+
+/*
+ * Functions to create and find a specific tag type: tag.c
+ */
+extern void blkid_free_tag(blkid_tag tag);
+extern blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type);
+extern int blkid_set_tag(blkid_dev dev, const char *name,
+			 const char *value, const int vlength);
+
+/*
+ * Functions to create and find a specific tag type: dev.c
+ */
+extern blkid_dev blkid_new_dev(void);
+extern void blkid_free_dev(blkid_dev dev);
+
+/* probe.c */
+extern int blkid_probe_is_tiny(blkid_probe pr);
+extern int blkid_probe_is_cdrom(blkid_probe pr);
+extern unsigned char *blkid_probe_get_buffer(blkid_probe pr,
+                                blkid_loff_t off, blkid_loff_t len);
+
+extern unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector);
+
+extern int blkid_probe_get_dimension(blkid_probe pr,
+	                blkid_loff_t *off, blkid_loff_t *size);
+
+extern int blkid_probe_set_dimension(blkid_probe pr,
+	                blkid_loff_t off, blkid_loff_t size);
+
+extern int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
+			blkid_loff_t *offset, const struct blkid_idmag **res);
+
+/* returns superblok according to 'struct blkid_idmag' */
+#define blkid_probe_get_sb(_pr, _mag, type) \
+			((type *) blkid_probe_get_buffer((_pr),\
+					(_mag)->kboff << 10, sizeof(type)))
+
+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);
+extern struct blkid_prval *blkid_probe_assign_value(blkid_probe pr, const char *name);
+extern int blkid_probe_reset_last_value(blkid_probe pr);
+extern void blkid_probe_append_vals(blkid_probe pr, struct blkid_prval *vals, int nvals);
+
+extern struct blkid_chain *blkid_probe_get_chain(blkid_probe pr);
+
+extern struct blkid_prval *__blkid_probe_get_value(blkid_probe pr, int num);
+extern struct blkid_prval *__blkid_probe_lookup_value(blkid_probe pr, const char *name);
+
+extern unsigned long *blkid_probe_get_filter(blkid_probe pr, int chain, int create);
+extern int __blkid_probe_invert_filter(blkid_probe pr, int chain);
+extern int __blkid_probe_reset_filter(blkid_probe pr, int chain);
+extern int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[]);
+
+extern void *blkid_probe_get_binary_data(blkid_probe pr, struct blkid_chain *chn);
+
+extern int blkid_probe_set_value(blkid_probe pr, const char *name,
+                unsigned char *data, size_t len);
+extern int blkid_probe_vsprintf_value(blkid_probe pr, const char *name,
+                const char *fmt, va_list ap);
+
+extern int blkid_probe_sprintf_value(blkid_probe pr, const char *name,
+                const char *fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4)));
+
+extern int blkid_probe_set_magic(blkid_probe pr, blkid_loff_t offset,
+		size_t len, unsigned char *magic);
+
+extern void blkid_unparse_uuid(const unsigned char *uuid, char *str, size_t len);
+extern size_t blkid_rtrim_whitespace(unsigned char *str);
+
+extern void blkid_probe_set_wiper(blkid_probe pr, blkid_loff_t off,
+				  blkid_loff_t size);
+extern int blkid_probe_is_wiped(blkid_probe pr, struct blkid_chain **chn,
+		                blkid_loff_t off, blkid_loff_t size);
+extern void blkid_probe_use_wiper(blkid_probe pr, blkid_loff_t off, blkid_loff_t size);
+
+/* filter bitmap macros */
+#define blkid_bmp_wordsize		(8 * sizeof(unsigned long))
+#define blkid_bmp_idx_bit(item)		(1UL << ((item) % blkid_bmp_wordsize))
+#define blkid_bmp_idx_byte(item)	((item) / blkid_bmp_wordsize)
+
+#define blkid_bmp_set_item(bmp, item)	\
+		((bmp)[ blkid_bmp_idx_byte(item) ] |= blkid_bmp_idx_bit(item))
+
+#define blkid_bmp_unset_item(bmp, item)	\
+		((bmp)[ blkid_bmp_idx_byte(item) ] &= ~blkid_bmp_idx_bit(item))
+
+#define blkid_bmp_get_item(bmp, item)	\
+		((bmp)[ blkid_bmp_idx_byte(item) ] & blkid_bmp_idx_bit(item))
+
+#define blkid_bmp_nwords(max_items) \
+		(((max_items) + blkid_bmp_wordsize) / blkid_bmp_wordsize)
+
+#define blkid_bmp_nbytes(max_items) \
+		(blkid_bmp_nwords(max_items) * sizeof(unsigned long))
+
+/* encode.c */
+extern size_t blkid_encode_to_utf8(int enc, unsigned char *dest, size_t len,
+			const unsigned char *src, size_t count);
+
+#define BLKID_ENC_UTF16BE	0
+#define BLKID_ENC_UTF16LE	1
+
+#endif /* _BLKID_BLKIDP_H */
diff --git a/src/cache.c b/src/cache.c
new file mode 100644
index 0000000..a60495a
--- /dev/null
+++ b/src/cache.c
@@ -0,0 +1,272 @@
+/*
+ * cache.c - allocation/initialization/free routines for cache
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include "blkidP.h"
+#include "env.h"
+
+int blkid_debug_mask = 0;
+
+/**
+ * SECTION:cache
+ * @title: Cache
+ * @short_description: basic routines to work with libblkid cache
+ *
+ * Block device information is normally kept in a cache file blkid.tab and is
+ * verified to still be valid before being returned to the user (if the user has
+ * read permission on the raw block device, otherwise not).  The cache file also
+ * allows unprivileged users (normally anyone other than root, or those not in the
+ * "disk" group) to locate devices by label/id.  The standard location of the
+ * cache file can be overridden by the environment variable BLKID_FILE.
+ *
+ * In situations where one is getting information about a single known device, it
+ * does not impact performance whether the cache is used or not (unless you are
+ * not able to read the block device directly).  If you are dealing with multiple
+ * devices, use of the cache is highly recommended (even if empty) as devices will
+ * be scanned at most one time and the on-disk cache will be updated if possible.
+ * There is rarely a reason not to use the cache.
+ *
+ * In some cases (modular kernels), block devices are not even visible until after
+ * they are accessed the first time, so it is critical that there is some way to
+ * locate these devices without enumerating only visible devices, so the use of
+ * the cache file is required in this situation.
+ */
+
+#if 0 /* ifdef CONFIG_BLKID_DEBUG */
+static blkid_debug_dump_cache(int mask, blkid_cache cache)
+{
+	struct list_head *p;
+
+	if (!cache) {
+		printf("cache: NULL\n");
+		return;
+	}
+
+	printf("cache: time = %lu\n", cache->bic_time);
+	printf("cache: flags = 0x%08X\n", cache->bic_flags);
+
+	list_for_each(p, &cache->bic_devs) {
+		blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
+		blkid_debug_dump_dev(dev);
+	}
+}
+#endif
+
+#ifdef CONFIG_BLKID_DEBUG
+void blkid_init_debug(int mask)
+{
+	if (blkid_debug_mask & DEBUG_INIT)
+		return;
+
+	if (!mask)
+	{
+		char *dstr = getenv("LIBBLKID_DEBUG");
+
+		if (!dstr)
+			dstr = getenv("BLKID_DEBUG");	/* for backward compatibility */
+		if (dstr)
+			blkid_debug_mask = strtoul(dstr, 0, 0);
+	} else
+		blkid_debug_mask = mask;
+
+	if (blkid_debug_mask)
+		printf("libblkid: debug mask set to 0x%04x.\n", blkid_debug_mask);
+
+	blkid_debug_mask |= DEBUG_INIT;
+}
+#endif
+
+static const char *get_default_cache_filename(void)
+{
+	struct stat st;
+
+	if (stat(BLKID_RUNTIME_TOPDIR, &st) == 0 && S_ISDIR(st.st_mode))
+		return BLKID_CACHE_FILE;	/* cache in /run */
+
+	return BLKID_CACHE_FILE_OLD;	/* cache in /etc */
+}
+
+/* returns allocated path to cache */
+char *blkid_get_cache_filename(struct blkid_config *conf)
+{
+	char *filename;
+
+	filename = safe_getenv("BLKID_FILE");
+	if (filename)
+		filename = blkid_strdup(filename);
+	else if (conf)
+		filename = blkid_strdup(conf->cachefile);
+	else {
+		struct blkid_config *c = blkid_read_config(NULL);
+		if (!c)
+			filename = blkid_strdup(get_default_cache_filename());
+		else {
+			filename = c->cachefile;  /* already allocated */
+			c->cachefile = NULL;
+			blkid_free_config(c);
+		}
+	}
+	return filename;
+}
+
+/**
+ * blkid_get_cache:
+ * @cache: pointer to return cache handler
+ * @filename: path to the cache file or NULL for the default path
+ *
+ * Allocates and initialize librray cache handler.
+ *
+ * Returns: 0 on success or number less than zero in case of error.
+ */
+int blkid_get_cache(blkid_cache *ret_cache, const char *filename)
+{
+	blkid_cache cache;
+
+	blkid_init_debug(0);
+
+	DBG(DEBUG_CACHE, printf("creating blkid cache (using %s)\n",
+				filename ? filename : "default cache"));
+
+	if (!(cache = (blkid_cache) calloc(1, sizeof(struct blkid_struct_cache))))
+		return -BLKID_ERR_MEM;
+
+	INIT_LIST_HEAD(&cache->bic_devs);
+	INIT_LIST_HEAD(&cache->bic_tags);
+
+	if (filename && !*filename)
+		filename = NULL;
+	if (filename)
+		cache->bic_filename = blkid_strdup(filename);
+	else
+		cache->bic_filename = blkid_get_cache_filename(NULL);
+
+	blkid_read_cache(cache);
+	*ret_cache = cache;
+	return 0;
+}
+
+/**
+ * blkid_put_cache:
+ * @cache: cache handler
+ *
+ * Saves changes to cache file.
+ */
+void blkid_put_cache(blkid_cache cache)
+{
+	if (!cache)
+		return;
+
+	(void) blkid_flush_cache(cache);
+
+	DBG(DEBUG_CACHE, printf("freeing cache struct\n"));
+
+	/* DBG(DEBUG_CACHE, blkid_debug_dump_cache(cache)); */
+
+	while (!list_empty(&cache->bic_devs)) {
+		blkid_dev dev = list_entry(cache->bic_devs.next,
+					   struct blkid_struct_dev,
+					    bid_devs);
+		blkid_free_dev(dev);
+	}
+
+	while (!list_empty(&cache->bic_tags)) {
+		blkid_tag tag = list_entry(cache->bic_tags.next,
+					   struct blkid_struct_tag,
+					   bit_tags);
+
+		while (!list_empty(&tag->bit_names)) {
+			blkid_tag bad = list_entry(tag->bit_names.next,
+						   struct blkid_struct_tag,
+						   bit_names);
+
+			DBG(DEBUG_CACHE, printf("warning: unfreed tag %s=%s\n",
+						bad->bit_name, bad->bit_val));
+			blkid_free_tag(bad);
+		}
+		blkid_free_tag(tag);
+	}
+
+	blkid_free_probe(cache->probe);
+
+	free(cache->bic_filename);
+	free(cache);
+}
+
+/**
+ * blkid_gc_cache:
+ * @cache: cache handler
+ *
+ * Removes garbage (non-existing devices) from the cache.
+ */
+void blkid_gc_cache(blkid_cache cache)
+{
+	struct list_head *p, *pnext;
+	struct stat st;
+
+	if (!cache)
+		return;
+
+	list_for_each_safe(p, pnext, &cache->bic_devs) {
+		blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
+		if (stat(dev->bid_name, &st) < 0) {
+			DBG(DEBUG_CACHE,
+			    printf("freeing %s\n", dev->bid_name));
+			blkid_free_dev(dev);
+			cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+		} else {
+			DBG(DEBUG_CACHE,
+			    printf("Device %s exists\n", dev->bid_name));
+		}
+	}
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_init_debug(DEBUG_ALL);
+
+	if ((argc > 2)) {
+		fprintf(stderr, "Usage: %s [filename] \n", argv[0]);
+		exit(1);
+	}
+
+	if ((ret = blkid_get_cache(&cache, argv[1])) < 0) {
+		fprintf(stderr, "error %d parsing cache file %s\n", ret,
+			argv[1] ? argv[1] : blkid_get_cache_filename(NULL));
+		exit(1);
+	}
+	if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+	if ((ret = blkid_probe_all(cache)) < 0)
+		fprintf(stderr, "error probing devices\n");
+
+	blkid_put_cache(cache);
+
+	return ret;
+}
+#endif
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 0000000..110251a
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,201 @@
+/*
+ * config.c - blkid.conf routines
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdint.h>
+#include <stdarg.h>
+
+#include "blkidP.h"
+#include "env.h"
+
+static int parse_evaluate(struct blkid_config *conf, char *s)
+{
+	while(s && *s) {
+		char *sep;
+
+		if (conf->nevals >= __BLKID_EVAL_LAST)
+			goto err;
+		sep = strchr(s, ',');
+		if (sep)
+			*sep = '\0';
+		if (strcmp(s, "udev") == 0)
+			conf->eval[conf->nevals] = BLKID_EVAL_UDEV;
+		else if (strcmp(s, "scan") == 0)
+			conf->eval[conf->nevals] = BLKID_EVAL_SCAN;
+		else
+			goto err;
+		conf->nevals++;
+		if (sep)
+			s = sep + 1;
+		else
+			break;
+	}
+	return 0;
+err:
+	DBG(DEBUG_CONFIG, printf(
+		"config file: unknown evaluation method '%s'.\n", s));
+	return -1;
+}
+
+static int parse_next(FILE *fd, struct blkid_config *conf)
+{
+	char buf[BUFSIZ];
+	char *s;
+
+	/* read the next non-blank non-comment line */
+	do {
+		if (fgets (buf, sizeof(buf), fd) == NULL)
+			return feof(fd) ? 0 : -1;
+		s = strchr (buf, '\n');
+		if (!s) {
+			/* Missing final newline?  Otherwise extremely */
+			/* long line - assume file was corrupted */
+			if (feof(fd))
+				s = strchr (buf, '\0');
+			else {
+				DBG(DEBUG_CONFIG, fprintf(stderr,
+					"libblkid: config file: missing newline at line '%s'.\n",
+					buf));
+				return -1;
+			}
+		}
+		*s = '\0';
+		if (--s >= buf && *s == '\r')
+			*s = '\0';
+
+		s = buf;
+		while (*s == ' ' || *s == '\t')		/* skip space */
+			s++;
+
+	} while (*s == '\0' || *s == '#');
+
+	if (!strncmp(s, "SEND_UEVENT=", 12)) {
+		s += 13;
+		if (*s && !strcasecmp(s, "yes"))
+			conf->uevent = TRUE;
+		else if (*s)
+			conf->uevent = FALSE;
+	} else if (!strncmp(s, "CACHE_FILE=", 11)) {
+		s += 11;
+		if (*s)
+			conf->cachefile = blkid_strdup(s);
+	} else if (!strncmp(s, "EVALUATE=", 9)) {
+		s += 9;
+		if (*s && parse_evaluate(conf, s) == -1)
+			return -1;
+	} else {
+		DBG(DEBUG_CONFIG, printf(
+			"config file: unknown option '%s'.\n", s));
+		return -1;
+	}
+	return 0;
+}
+
+/* return real config data or built-in default */
+struct blkid_config *blkid_read_config(const char *filename)
+{
+	struct blkid_config *conf;
+	FILE *f;
+
+	if (!filename)
+		filename = safe_getenv("BLKID_CONF");
+	if (!filename)
+		filename = BLKID_CONFIG_FILE;
+
+	conf = (struct blkid_config *) calloc(1, sizeof(*conf));
+	if (!conf)
+		return NULL;
+	conf->uevent = -1;
+
+	DBG(DEBUG_CONFIG, fprintf(stderr,
+		"reading config file: %s.\n", filename));
+
+	f = fopen(filename, "r");
+	if (!f) {
+		DBG(DEBUG_CONFIG, fprintf(stderr,
+			"%s: does not exist, using built-in default\n", filename));
+		goto dflt;
+	}
+	while (!feof(f)) {
+		if (parse_next(f, conf)) {
+			DBG(DEBUG_CONFIG, fprintf(stderr,
+				"%s: parse error\n", filename));
+			goto err;
+		}
+	}
+dflt:
+	if (!conf->nevals) {
+		conf->eval[0] = BLKID_EVAL_UDEV;
+		conf->eval[1] = BLKID_EVAL_SCAN;
+		conf->nevals = 2;
+	}
+	if (!conf->cachefile)
+		conf->cachefile = blkid_strdup(BLKID_CACHE_FILE);
+	if (conf->uevent == -1)
+		conf->uevent = TRUE;
+	if (f)
+		fclose(f);
+	return conf;
+err:
+	free(conf);
+	fclose(f);
+	return NULL;
+}
+
+void blkid_free_config(struct blkid_config *conf)
+{
+	if (!conf)
+		return;
+	free(conf->cachefile);
+	free(conf);
+}
+
+#ifdef TEST_PROGRAM
+/*
+ * usage: tst_config [<filename>]
+ */
+int main(int argc, char *argv[])
+{
+	int i;
+	struct blkid_config *conf;
+	char *filename = NULL;
+
+	blkid_init_debug(DEBUG_ALL);
+
+	if (argc == 2)
+		filename = argv[1];
+
+	conf = blkid_read_config(filename);
+	if (!conf)
+		return EXIT_FAILURE;
+
+	printf("EVALUATE:    ");
+	for (i = 0; i < conf->nevals; i++)
+		printf("%s ", conf->eval[i] == BLKID_EVAL_UDEV ? "udev" : "scan");
+	printf("\n");
+
+	printf("SEND UEVENT: %s\n", conf->uevent ? "TRUE" : "FALSE");
+	printf("CACHE_FILE:  %s\n", conf->cachefile);
+
+	blkid_free_config(conf);
+	return EXIT_SUCCESS;
+}
+#endif
diff --git a/src/dev.c b/src/dev.c
new file mode 100644
index 0000000..58ee6f8
--- /dev/null
+++ b/src/dev.c
@@ -0,0 +1,272 @@
+/*
+ * dev.c - allocation/initialization/free routines for dev
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "blkidP.h"
+
+/*
+ * NOTE: reference manual is not structured as code. The following section is a generic
+ * section for all high-level cache search+iterate routines.
+ */
+
+/**
+ * SECTION:search
+ * @title: Search and iterate
+ * @short_description: search devices and iterate over devices in the cache.
+ *
+ * Note that high-level probing API provides information about superblocks
+ * (filesystems/raids) only.  For partitions and topology is necessary to use
+ * the low-level API.
+ */
+
+blkid_dev blkid_new_dev(void)
+{
+	blkid_dev dev;
+
+	if (!(dev = (blkid_dev) calloc(1, sizeof(struct blkid_struct_dev))))
+		return NULL;
+
+	INIT_LIST_HEAD(&dev->bid_devs);
+	INIT_LIST_HEAD(&dev->bid_tags);
+
+	return dev;
+}
+
+void blkid_free_dev(blkid_dev dev)
+{
+	if (!dev)
+		return;
+
+	DBG(DEBUG_DEV,
+	    printf("  freeing dev %s (%s)\n", dev->bid_name, dev->bid_type ?
+		   dev->bid_type : "(null)"));
+	DBG(DEBUG_DEV, blkid_debug_dump_dev(dev));
+
+	list_del(&dev->bid_devs);
+	while (!list_empty(&dev->bid_tags)) {
+		blkid_tag tag = list_entry(dev->bid_tags.next,
+					   struct blkid_struct_tag,
+					   bit_tags);
+		blkid_free_tag(tag);
+	}
+	free(dev->bid_name);
+	free(dev);
+}
+
+/*
+ * Given a blkid device, return its name
+ */
+extern const char *blkid_dev_devname(blkid_dev dev)
+{
+	return dev->bid_name;
+}
+
+#ifdef CONFIG_BLKID_DEBUG
+void blkid_debug_dump_dev(blkid_dev dev)
+{
+	struct list_head *p;
+
+	if (!dev) {
+		printf("  dev: NULL\n");
+		return;
+	}
+
+	printf("  dev: name = %s\n", dev->bid_name);
+	printf("  dev: DEVNO=\"0x%0llx\"\n", (long long)dev->bid_devno);
+	printf("  dev: TIME=\"%ld.%ld\"\n", (long)dev->bid_time, (long)dev->bid_utime);
+	printf("  dev: PRI=\"%d\"\n", dev->bid_pri);
+	printf("  dev: flags = 0x%08X\n", dev->bid_flags);
+
+	list_for_each(p, &dev->bid_tags) {
+		blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+		if (tag)
+			printf("    tag: %s=\"%s\"\n", tag->bit_name,
+			       tag->bit_val);
+		else
+			printf("    tag: NULL\n");
+	}
+	printf("\n");
+}
+#endif
+
+/*
+ * dev iteration routines for the public libblkid interface.
+ *
+ * These routines do not expose the list.h implementation, which are a
+ * contamination of the namespace, and which force us to reveal far, far
+ * too much of our internal implemenation.  I'm not convinced I want
+ * to keep list.h in the long term, anyway.  It's fine for kernel
+ * programming, but performance is not the #1 priority for this
+ * library, and I really don't like the tradeoff of type-safety for
+ * performance for this application.  [tytso:20030125.2007EST]
+ */
+
+/*
+ * This series of functions iterate over all devices in a blkid cache
+ */
+#define DEV_ITERATE_MAGIC	0x01a5284c
+
+struct blkid_struct_dev_iterate {
+	int			magic;
+	blkid_cache		cache;
+	char			*search_type;
+	char			*search_value;
+	struct list_head	*p;
+};
+
+extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache)
+{
+	blkid_dev_iterate	iter;
+
+	iter = malloc(sizeof(struct blkid_struct_dev_iterate));
+	if (iter) {
+		iter->magic = DEV_ITERATE_MAGIC;
+		iter->cache = cache;
+		iter->p	= cache->bic_devs.next;
+		iter->search_type = 0;
+		iter->search_value = 0;
+	}
+	return (iter);
+}
+
+extern int blkid_dev_set_search(blkid_dev_iterate iter,
+				 char *search_type, char *search_value)
+{
+	char *new_type, *new_value;
+
+	if (!iter || iter->magic != DEV_ITERATE_MAGIC || !search_type ||
+	    !search_value)
+		return -1;
+	new_type = malloc(strlen(search_type)+1);
+	new_value = malloc(strlen(search_value)+1);
+	if (!new_type || !new_value) {
+		free(new_type);
+		free(new_value);
+		return -1;
+	}
+	strcpy(new_type, search_type);
+	strcpy(new_value, search_value);
+	free(iter->search_type);
+	free(iter->search_value);
+	iter->search_type = new_type;
+	iter->search_value = new_value;
+	return 0;
+}
+
+/*
+ * Return 0 on success, -1 on error
+ */
+extern int blkid_dev_next(blkid_dev_iterate iter,
+			  blkid_dev *ret_dev)
+{
+	blkid_dev		dev;
+
+	*ret_dev = 0;
+	if (!iter || iter->magic != DEV_ITERATE_MAGIC)
+		return -1;
+	while (iter->p != &iter->cache->bic_devs) {
+		dev = list_entry(iter->p, struct blkid_struct_dev, bid_devs);
+		iter->p = iter->p->next;
+		if (iter->search_type &&
+		    !blkid_dev_has_tag(dev, iter->search_type,
+				       iter->search_value))
+			continue;
+		*ret_dev = dev;
+		return 0;
+	}
+	return -1;
+}
+
+extern void blkid_dev_iterate_end(blkid_dev_iterate iter)
+{
+	if (!iter || iter->magic != DEV_ITERATE_MAGIC)
+		return;
+	iter->magic = 0;
+	free(iter->search_type);
+	free(iter->search_value);
+	free(iter);
+}
+
+#ifdef TEST_PROGRAM
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+void __attribute__((__noreturn__)) usage(char *prog)
+{
+	fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask]\n", prog);
+	fprintf(stderr, "\tList all devices and exit\n");
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	blkid_dev_iterate	iter;
+	blkid_cache 		cache = NULL;
+	blkid_dev		dev;
+	int			c, ret;
+	char			*tmp;
+	char			*file = NULL;
+	char			*search_type = NULL;
+	char			*search_value = NULL;
+
+	while ((c = getopt (argc, argv, "m:f:")) != EOF)
+		switch (c) {
+		case 'f':
+			file = optarg;
+			break;
+		case 'm':
+		{
+			int mask = strtoul (optarg, &tmp, 0);
+			if (*tmp) {
+				fprintf(stderr, "Invalid debug mask: %s\n",
+					optarg);
+				exit(1);
+			}
+			blkid_init_debug(mask);
+			break;
+		}
+		case '?':
+			usage(argv[0]);
+		}
+	if (argc >= optind+2) {
+		search_type = argv[optind];
+		search_value = argv[optind+1];
+		optind += 2;
+	}
+	if (argc != optind)
+		usage(argv[0]);
+
+	if ((ret = blkid_get_cache(&cache, file)) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+
+	iter = blkid_dev_iterate_begin(cache);
+	if (search_type)
+		blkid_dev_set_search(iter, search_type, search_value);
+	while (blkid_dev_next(iter, &dev) == 0) {
+		printf("Device: %s\n", blkid_dev_devname(dev));
+	}
+	blkid_dev_iterate_end(iter);
+
+
+	blkid_put_cache(cache);
+	return (0);
+}
+#endif
diff --git a/src/devname.c b/src/devname.c
new file mode 100644
index 0000000..17a9e50
--- /dev/null
+++ b/src/devname.c
@@ -0,0 +1,676 @@
+/*
+ * devname.c - get a dev by its device inode name
+ *
+ * Copyright (C) Andries Brouwer
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#define _GNU_SOURCE 1
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <ctype.h>
+#include <fcntl.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <dirent.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <time.h>
+
+#include "blkidP.h"
+
+#include "canonicalize.h"		/* $(top_srcdir)/include */
+#include "pathnames.h"
+#include "sysfs.h"
+#include "at.h"
+
+/*
+ * Find a dev struct in the cache by device name, if available.
+ *
+ * If there is no entry with the specified device name, and the create
+ * flag is set, then create an empty device entry.
+ */
+blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
+{
+	blkid_dev dev = NULL, tmp;
+	struct list_head *p, *pnext;
+
+	if (!cache || !devname)
+		return NULL;
+
+	list_for_each(p, &cache->bic_devs) {
+		tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
+		if (strcmp(tmp->bid_name, devname))
+			continue;
+
+		DBG(DEBUG_DEVNAME,
+		    printf("found devname %s in cache\n", tmp->bid_name));
+		dev = tmp;
+		break;
+	}
+
+	if (!dev && (flags & BLKID_DEV_CREATE)) {
+		if (access(devname, F_OK) < 0)
+			return NULL;
+		dev = blkid_new_dev();
+		if (!dev)
+			return NULL;
+		dev->bid_time = INT_MIN;
+		dev->bid_name = blkid_strdup(devname);
+		dev->bid_cache = cache;
+		list_add_tail(&dev->bid_devs, &cache->bic_devs);
+		cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+	}
+
+	if (flags & BLKID_DEV_VERIFY) {
+		dev = blkid_verify(cache, dev);
+		if (!dev || !(dev->bid_flags & BLKID_BID_FL_VERIFIED))
+			return dev;
+		/*
+		 * If the device is verified, then search the blkid
+		 * cache for any entries that match on the type, uuid,
+		 * and label, and verify them; if a cache entry can
+		 * not be verified, then it's stale and so we remove
+		 * it.
+		 */
+		list_for_each_safe(p, pnext, &cache->bic_devs) {
+			blkid_dev dev2 = list_entry(p, struct blkid_struct_dev, bid_devs);
+			if (dev2->bid_flags & BLKID_BID_FL_VERIFIED)
+				continue;
+			if (!dev->bid_type || !dev2->bid_type ||
+			    strcmp(dev->bid_type, dev2->bid_type))
+				continue;
+			if (dev->bid_label && dev2->bid_label &&
+			    strcmp(dev->bid_label, dev2->bid_label))
+				continue;
+			if (dev->bid_uuid && dev2->bid_uuid &&
+			    strcmp(dev->bid_uuid, dev2->bid_uuid))
+				continue;
+			if ((dev->bid_label && !dev2->bid_label) ||
+			    (!dev->bid_label && dev2->bid_label) ||
+			    (dev->bid_uuid && !dev2->bid_uuid) ||
+			    (!dev->bid_uuid && dev2->bid_uuid))
+				continue;
+			dev2 = blkid_verify(cache, dev2);
+			if (dev2 && !(dev2->bid_flags & BLKID_BID_FL_VERIFIED))
+				blkid_free_dev(dev2);
+		}
+	}
+	return dev;
+}
+
+/* Directories where we will try to search for device names */
+static const char *dirlist[] = { "/dev", "/devfs", "/devices", NULL };
+
+static int is_dm_leaf(const char *devname)
+{
+	struct dirent	*de, *d_de;
+	DIR		*dir, *d_dir;
+	char		path[256];
+	int		ret = 1;
+
+	if ((dir = opendir("/sys/block")) == NULL)
+		return 0;
+	while ((de = readdir(dir)) != NULL) {
+		if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") ||
+		    !strcmp(de->d_name, devname) ||
+		    strncmp(de->d_name, "dm-", 3) ||
+		    strlen(de->d_name) > sizeof(path)-32)
+			continue;
+		sprintf(path, "/sys/block/%s/slaves", de->d_name);
+		if ((d_dir = opendir(path)) == NULL)
+			continue;
+		while ((d_de = readdir(d_dir)) != NULL) {
+			if (!strcmp(d_de->d_name, devname)) {
+				ret = 0;
+				break;
+			}
+		}
+		closedir(d_dir);
+		if (!ret)
+			break;
+	}
+	closedir(dir);
+	return ret;
+}
+
+/*
+ * Probe a single block device to add to the device cache.
+ */
+static void probe_one(blkid_cache cache, const char *ptname,
+		      dev_t devno, int pri, int only_if_new, int removable)
+{
+	blkid_dev dev = NULL;
+	struct list_head *p, *pnext;
+	const char **dir;
+	char *devname = NULL;
+
+	/* See if we already have this device number in the cache. */
+	list_for_each_safe(p, pnext, &cache->bic_devs) {
+		blkid_dev tmp = list_entry(p, struct blkid_struct_dev,
+					   bid_devs);
+		if (tmp->bid_devno == devno) {
+			if (only_if_new && !access(tmp->bid_name, F_OK))
+				return;
+			dev = blkid_verify(cache, tmp);
+			if (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED))
+				break;
+			dev = 0;
+		}
+	}
+	if (dev && dev->bid_devno == devno)
+		goto set_pri;
+
+	/* Try to translate private device-mapper dm-<N> names
+	 * to standard /dev/mapper/<name>.
+	 */
+	if (!strncmp(ptname, "dm-", 3) && isdigit(ptname[3])) {
+		devname = canonicalize_dm_name(ptname);
+		if (!devname)
+			blkid__scan_dir("/dev/mapper", devno, 0, &devname);
+		if (devname)
+			goto get_dev;
+	}
+
+	/*
+	 * Take a quick look at /dev/ptname for the device number.  We check
+	 * all of the likely device directories.  If we don't find it, or if
+	 * the stat information doesn't check out, use blkid_devno_to_devname()
+	 * to find it via an exhaustive search for the device major/minor.
+	 */
+	for (dir = dirlist; *dir; dir++) {
+		struct stat st;
+		char device[256];
+
+		sprintf(device, "%s/%s", *dir, ptname);
+		if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) &&
+		    dev->bid_devno == devno)
+			goto set_pri;
+
+		if (stat(device, &st) == 0 &&
+		    (S_ISBLK(st.st_mode) ||
+		     (S_ISCHR(st.st_mode) && !strncmp(ptname, "ubi", 3))) &&
+		    st.st_rdev == devno) {
+			devname = blkid_strdup(device);
+			goto get_dev;
+		}
+	}
+	/* Do a short-cut scan of /dev/mapper first */
+	if (!devname)
+		blkid__scan_dir("/dev/mapper", devno, 0, &devname);
+	if (!devname) {
+		devname = blkid_devno_to_devname(devno);
+		if (!devname)
+			return;
+	}
+
+get_dev:
+	dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL);
+	free(devname);
+
+set_pri:
+	if (dev) {
+		if (pri)
+			dev->bid_pri = pri;
+		else if (!strncmp(dev->bid_name, "/dev/mapper/", 11)) {
+			dev->bid_pri = BLKID_PRI_DM;
+			if (is_dm_leaf(ptname))
+				dev->bid_pri += 5;
+		} else if (!strncmp(ptname, "md", 2))
+			dev->bid_pri = BLKID_PRI_MD;
+		if (removable)
+			dev->bid_flags |= BLKID_BID_FL_REMOVABLE;
+	}
+	return;
+}
+
+#define PROC_PARTITIONS "/proc/partitions"
+#define VG_DIR		"/proc/lvm/VGs"
+
+/*
+ * This function initializes the UUID cache with devices from the LVM
+ * proc hierarchy.  We currently depend on the names of the LVM
+ * hierarchy giving us the device structure in /dev.  (XXX is this a
+ * safe thing to do?)
+ */
+#ifdef VG_DIR
+static dev_t lvm_get_devno(const char *lvm_device)
+{
+	FILE *lvf;
+	char buf[1024];
+	int ma, mi;
+	dev_t ret = 0;
+
+	DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device));
+	if ((lvf = fopen(lvm_device, "r")) == NULL) {
+		DBG(DEBUG_DEVNAME, printf("%s: (%d) %m\n", lvm_device, errno));
+		return 0;
+	}
+
+	while (fgets(buf, sizeof(buf), lvf)) {
+		if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) {
+			ret = makedev(ma, mi);
+			break;
+		}
+	}
+	fclose(lvf);
+
+	return ret;
+}
+
+static void lvm_probe_all(blkid_cache cache, int only_if_new)
+{
+	DIR		*vg_list;
+	struct dirent	*vg_iter;
+	int		vg_len = strlen(VG_DIR);
+	dev_t		dev;
+
+	if ((vg_list = opendir(VG_DIR)) == NULL)
+		return;
+
+	DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR));
+
+	while ((vg_iter = readdir(vg_list)) != NULL) {
+		DIR		*lv_list;
+		char		*vdirname;
+		char		*vg_name;
+		struct dirent	*lv_iter;
+
+		vg_name = vg_iter->d_name;
+		if (!strcmp(vg_name, ".") || !strcmp(vg_name, ".."))
+			continue;
+		vdirname = malloc(vg_len + strlen(vg_name) + 8);
+		if (!vdirname)
+			goto exit;
+		sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name);
+
+		lv_list = opendir(vdirname);
+		free(vdirname);
+		if (lv_list == NULL)
+			continue;
+
+		while ((lv_iter = readdir(lv_list)) != NULL) {
+			char		*lv_name, *lvm_device;
+
+			lv_name = lv_iter->d_name;
+			if (!strcmp(lv_name, ".") || !strcmp(lv_name, ".."))
+				continue;
+
+			lvm_device = malloc(vg_len + strlen(vg_name) +
+					    strlen(lv_name) + 8);
+			if (!lvm_device) {
+				closedir(lv_list);
+				goto exit;
+			}
+			sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
+				lv_name);
+			dev = lvm_get_devno(lvm_device);
+			sprintf(lvm_device, "%s/%s", vg_name, lv_name);
+			DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n",
+						  lvm_device,
+						  (unsigned int) dev));
+			probe_one(cache, lvm_device, dev, BLKID_PRI_LVM,
+				  only_if_new, 0);
+			free(lvm_device);
+		}
+		closedir(lv_list);
+	}
+exit:
+	closedir(vg_list);
+}
+#endif
+
+#define PROC_EVMS_VOLUMES "/proc/evms/volumes"
+
+static int
+evms_probe_all(blkid_cache cache, int only_if_new)
+{
+	char line[100];
+	int ma, mi, sz, num = 0;
+	FILE *procpt;
+	char device[110];
+
+	procpt = fopen(PROC_EVMS_VOLUMES, "r");
+	if (!procpt)
+		return 0;
+	while (fgets(line, sizeof(line), procpt)) {
+		if (sscanf (line, " %d %d %d %*s %*s %[^\n ]",
+			    &ma, &mi, &sz, device) != 4)
+			continue;
+
+		DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n",
+					  device, ma, mi));
+
+		probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS,
+			  only_if_new, 0);
+		num++;
+	}
+	fclose(procpt);
+	return num;
+}
+
+static void
+ubi_probe_all(blkid_cache cache, int only_if_new)
+{
+	const char **dirname;
+
+	for (dirname = dirlist; *dirname; dirname++) {
+		DBG(DEBUG_DEVNAME, printf("probing UBI volumes under %s\n",
+					  *dirname));
+
+		DIR		*dir;
+		struct dirent	*iter;
+
+		dir = opendir(*dirname);
+		if (dir == NULL)
+			continue ;
+
+		while ((iter = readdir(dir)) != NULL) {
+			char		*name;
+			struct stat	st;
+			dev_t		dev;
+
+			name = iter->d_name;
+#ifdef _DIRENT_HAVE_D_TYPE
+			if (iter->d_type != DT_UNKNOWN &&
+			    iter->d_type != DT_CHR && iter->d_type != DT_LNK)
+				continue;
+#endif
+			if (!strcmp(name, ".") || !strcmp(name, "..") ||
+			    !strstr(name, "ubi"))
+				continue;
+			if (!strcmp(name, "ubi_ctrl"))
+				continue;
+			if (fstat_at(dirfd(dir), *dirname, name, &st, 0))
+				continue;
+
+			dev = st.st_rdev;
+
+			if (!S_ISCHR(st.st_mode) || !minor(dev))
+				continue;
+			DBG(DEBUG_DEVNAME, printf("UBI vol %s/%s: devno 0x%04X\n",
+				  *dirname, name, (int) dev));
+			probe_one(cache, name, dev, BLKID_PRI_UBI, only_if_new, 0);
+		}
+		closedir(dir);
+	}
+}
+
+/*
+ * Read the device data for all available block devices in the system.
+ */
+static int probe_all(blkid_cache cache, int only_if_new)
+{
+	FILE *proc;
+	char line[1024];
+	char ptname0[128 + 1], ptname1[128 + 1], *ptname = 0;
+	char *ptnames[2];
+	dev_t devs[2];
+	int ma, mi;
+	unsigned long long sz;
+	int lens[2] = { 0, 0 };
+	int which = 0, last = 0;
+	struct list_head *p, *pnext;
+
+	ptnames[0] = ptname0;
+	ptnames[1] = ptname1;
+
+	if (!cache)
+		return -BLKID_ERR_PARAM;
+
+	if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
+	    time(0) - cache->bic_time < BLKID_PROBE_INTERVAL)
+		return 0;
+
+	blkid_read_cache(cache);
+	evms_probe_all(cache, only_if_new);
+#ifdef VG_DIR
+	lvm_probe_all(cache, only_if_new);
+#endif
+	ubi_probe_all(cache, only_if_new);
+
+	proc = fopen(PROC_PARTITIONS, "r");
+	if (!proc)
+		return -BLKID_ERR_PROC;
+
+	while (fgets(line, sizeof(line), proc)) {
+		last = which;
+		which ^= 1;
+		ptname = ptnames[which];
+
+		if (sscanf(line, " %d %d %llu %128[^\n ]",
+			   &ma, &mi, &sz, ptname) != 4)
+			continue;
+		devs[which] = makedev(ma, mi);
+
+		DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname));
+
+		/* Skip whole disk devs unless they have no partitions.
+		 * If base name of device has changed, also
+		 * check previous dev to see if it didn't have a partn.
+		 * heuristic: partition name ends in a digit, & partition
+		 * names contain whole device name as substring.
+		 *
+		 * Skip extended partitions.
+		 * heuristic: size is 1
+		 *
+		 * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
+		 */
+
+		lens[which] = strlen(ptname);
+
+		/* ends in a digit, clearly a partition, so check */
+		if (isdigit(ptname[lens[which] - 1])) {
+			DBG(DEBUG_DEVNAME,
+			    printf("partition dev %s, devno 0x%04X\n",
+				   ptname, (unsigned int) devs[which]));
+
+			if (sz > 1)
+				probe_one(cache, ptname, devs[which], 0,
+					  only_if_new, 0);
+			lens[which] = 0;	/* mark as checked */
+		}
+
+		/*
+		 * If last was a whole disk and we just found a partition
+		 * on it, remove the whole-disk dev from the cache if
+		 * it exists.
+		 */
+		if (lens[last] && !strncmp(ptnames[last], ptname, lens[last])) {
+			list_for_each_safe(p, pnext, &cache->bic_devs) {
+				blkid_dev tmp;
+
+				/* find blkid dev for the whole-disk devno */
+				tmp = list_entry(p, struct blkid_struct_dev,
+						 bid_devs);
+				if (tmp->bid_devno == devs[last]) {
+					DBG(DEBUG_DEVNAME,
+						printf("freeing %s\n",
+						       tmp->bid_name));
+					blkid_free_dev(tmp);
+					cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+					break;
+				}
+			}
+			lens[last] = 0;
+		}
+		/*
+		 * If last was not checked because it looked like a whole-disk
+		 * dev, and the device's base name has changed,
+		 * check last as well.
+		 */
+		if (lens[last] && strncmp(ptnames[last], ptname, lens[last])) {
+			DBG(DEBUG_DEVNAME,
+			    printf("whole dev %s, devno 0x%04X\n",
+				   ptnames[last], (unsigned int) devs[last]));
+			probe_one(cache, ptnames[last], devs[last], 0,
+				  only_if_new, 0);
+			lens[last] = 0;
+		}
+	}
+
+	/* Handle the last device if it wasn't partitioned */
+	if (lens[which])
+		probe_one(cache, ptname, devs[which], 0, only_if_new, 0);
+
+	fclose(proc);
+	blkid_flush_cache(cache);
+	return 0;
+}
+
+/* Don't use it by default -- it's pretty slow (because cdroms, floppy, ...)
+ */
+static int probe_all_removable(blkid_cache cache)
+{
+	DIR *dir;
+	struct dirent *d;
+
+	if (!cache)
+		return -BLKID_ERR_PARAM;
+
+	dir = opendir(_PATH_SYS_BLOCK);
+	if (!dir)
+		return -BLKID_ERR_PROC;
+
+	while((d = readdir(dir))) {
+		struct sysfs_cxt sysfs = UL_SYSFSCXT_EMPTY;
+		int removable = 0;
+		dev_t devno;
+
+#ifdef _DIRENT_HAVE_D_TYPE
+		if (d->d_type != DT_UNKNOWN && d->d_type != DT_LNK)
+			continue;
+#endif
+		if (d->d_name[0] == '.' &&
+		    ((d->d_name[1] == 0) ||
+		     ((d->d_name[1] == '.') && (d->d_name[2] == 0))))
+			continue;
+
+		devno = sysfs_devname_to_devno(d->d_name, NULL);
+		if (!devno)
+			continue;
+
+		if (sysfs_init(&sysfs, devno, NULL) == 0) {
+			sysfs_read_int(&sysfs, "removable", &removable);
+			sysfs_deinit(&sysfs);
+		}
+
+		if (removable)
+			probe_one(cache, d->d_name, devno, 0, 0, 1);
+	}
+
+	closedir(dir);
+	return 0;
+}
+
+
+/**
+ * blkid_probe_all:
+ * @cache: cache handler
+ *
+ * Probes all block devices.
+ *
+ * Returns: 0 on success, or number less than zero in case of error.
+ */
+int blkid_probe_all(blkid_cache cache)
+{
+	int ret;
+
+	DBG(DEBUG_PROBE, printf("Begin blkid_probe_all()\n"));
+	ret = probe_all(cache, 0);
+	cache->bic_time = time(0);
+	cache->bic_flags |= BLKID_BIC_FL_PROBED;
+	DBG(DEBUG_PROBE, printf("End blkid_probe_all()\n"));
+	return ret;
+}
+
+/**
+ * blkid_probe_all_new:
+ * @cache: cache handler
+ *
+ * Probes all new block devices.
+ *
+ * Returns: 0 on success, or number less than zero in case of error.
+ */
+int blkid_probe_all_new(blkid_cache cache)
+{
+	int ret;
+
+	DBG(DEBUG_PROBE, printf("Begin blkid_probe_all_new()\n"));
+	ret = probe_all(cache, 1);
+	DBG(DEBUG_PROBE, printf("End blkid_probe_all_new()\n"));
+	return ret;
+}
+
+/**
+ * blkid_probe_all_removable:
+ * @cache: cache handler
+ *
+ * The libblkid probing is based on devices from /proc/partitions by default.
+ * This file usually does not contain removable devices (e.g. CDROMs) and this kind
+ * of devices are invisible for libblkid.
+ *
+ * This function adds removable block devices to @cache (probing is based on
+ * information from the /sys directory). Don't forget that removable devices
+ * (floppies, CDROMs, ...) could be pretty slow. It's very bad idea to call
+ * this function by default.
+ *
+ * Note that devices which were detected by this function won't be written to
+ * blkid.tab cache file.
+ *
+ * Returns: 0 on success, or number less than zero in case of error.
+ */
+int blkid_probe_all_removable(blkid_cache cache)
+{
+	int ret;
+
+	DBG(DEBUG_PROBE, printf("Begin blkid_probe_all_removable()\n"));
+	ret = probe_all_removable(cache);
+	DBG(DEBUG_PROBE, printf("End blkid_probe_all_removable()\n"));
+	return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_init_debug(DEBUG_ALL);
+	if (argc != 1) {
+		fprintf(stderr, "Usage: %s\n"
+			"Probe all devices and exit\n", argv[0]);
+		exit(1);
+	}
+	if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+	if (blkid_probe_all(cache) < 0)
+		printf("%s: error probing devices\n", argv[0]);
+
+	if (blkid_probe_all_removable(cache) < 0)
+		printf("%s: error probing removable devices\n", argv[0]);
+
+	blkid_put_cache(cache);
+	return (0);
+}
+#endif
diff --git a/src/devno.c b/src/devno.c
new file mode 100644
index 0000000..3024bee
--- /dev/null
+++ b/src/devno.c
@@ -0,0 +1,399 @@
+/*
+ * devno.c - find a particular device by its device number (major/minor)
+ *
+ * Copyright (C) 2000, 2001, 2003 Theodore Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <dirent.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#include <fcntl.h>
+#include <inttypes.h>
+
+#include "blkidP.h"
+#include "pathnames.h"
+#include "at.h"
+#include "sysfs.h"
+
+char *blkid_strndup(const char *s, int length)
+{
+	char *ret;
+
+	if (!s)
+		return NULL;
+
+	if (!length)
+		length = strlen(s);
+
+	ret = malloc(length + 1);
+	if (ret) {
+		strncpy(ret, s, length);
+		ret[length] = '\0';
+	}
+	return ret;
+}
+
+char *blkid_strdup(const char *s)
+{
+	return blkid_strndup(s, 0);
+}
+
+char *blkid_strconcat(const char *a, const char *b, const char *c)
+{
+	char *res, *p;
+	size_t len, al, bl, cl;
+
+	al = a ? strlen(a) : 0;
+	bl = b ? strlen(b) : 0;
+	cl = c ? strlen(c) : 0;
+
+	len = al + bl + cl;
+	if (!len)
+		return NULL;
+	p = res = malloc(len + 1);
+	if (!res)
+		return NULL;
+	if (al) {
+		memcpy(p, a, al);
+		p += al;
+	}
+	if (bl) {
+		memcpy(p, b, bl);
+		p += bl;
+	}
+	if (cl) {
+		memcpy(p, c, cl);
+		p += cl;
+	}
+	*p = '\0';
+	return res;
+}
+
+/*
+ * This function adds an entry to the directory list
+ */
+static void add_to_dirlist(const char *dir, const char *subdir,
+				struct dir_list **list)
+{
+	struct dir_list *dp;
+
+	dp = malloc(sizeof(struct dir_list));
+	if (!dp)
+		return;
+	dp->name = subdir ? blkid_strconcat(dir, "/", subdir) :
+			    blkid_strdup(dir);
+	if (!dp->name) {
+		free(dp);
+		return;
+	}
+	dp->next = *list;
+	*list = dp;
+}
+
+/*
+ * This function frees a directory list
+ */
+static void free_dirlist(struct dir_list **list)
+{
+	struct dir_list *dp, *next;
+
+	for (dp = *list; dp; dp = next) {
+		next = dp->next;
+		free(dp->name);
+		free(dp);
+	}
+	*list = NULL;
+}
+
+void blkid__scan_dir(char *dirname, dev_t devno, struct dir_list **list,
+		     char **devname)
+{
+	DIR	*dir;
+	struct dirent *dp;
+	struct stat st;
+
+	if ((dir = opendir(dirname)) == NULL)
+		return;
+
+	while ((dp = readdir(dir)) != 0) {
+#ifdef _DIRENT_HAVE_D_TYPE
+		if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_BLK &&
+		    dp->d_type != DT_LNK && dp->d_type != DT_DIR)
+			continue;
+#endif
+		if (dp->d_name[0] == '.' &&
+		    ((dp->d_name[1] == 0) ||
+		     ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
+			continue;
+
+		if (fstat_at(dirfd(dir), dirname, dp->d_name, &st, 0))
+			continue;
+
+		if (S_ISBLK(st.st_mode) && st.st_rdev == devno) {
+			*devname = blkid_strconcat(dirname, "/", dp->d_name);
+			DBG(DEBUG_DEVNO,
+			    printf("found 0x%llx at %s\n", (long long)devno,
+				   *devname));
+			break;
+		}
+
+		if (!list || !S_ISDIR(st.st_mode))
+			continue;
+
+		/* add subdirectory (but not symlink) to the list */
+#ifdef _DIRENT_HAVE_D_TYPE
+		if (dp->d_type == DT_LNK)
+			continue;
+		if (dp->d_type == DT_UNKNOWN)
+#endif
+		{
+			if (fstat_at(dirfd(dir), dirname, dp->d_name, &st, 1) ||
+			    !S_ISDIR(st.st_mode))
+				continue;	/* symlink or lstat() failed */
+		}
+
+		if (*dp->d_name == '.' || (
+#ifdef _DIRENT_HAVE_D_TYPE
+		    dp->d_type == DT_DIR &&
+#endif
+		    strcmp(dp->d_name, "shm") == 0))
+			/* ignore /dev/.{udev,mount,mdadm} and /dev/shm */
+			continue;
+
+		add_to_dirlist(dirname, dp->d_name, list);
+	}
+	closedir(dir);
+	return;
+}
+
+/* Directories where we will try to search for device numbers */
+static const char *devdirs[] = { "/devices", "/devfs", "/dev", NULL };
+
+/**
+ * SECTION: misc
+ * @title: Miscellaneous utils
+ * @short_description: mix of various utils for low-level and high-level API
+ */
+
+
+
+static char *scandev_devno_to_devpath(dev_t devno)
+{
+	struct dir_list *list = NULL, *new_list = NULL;
+	char *devname = NULL;
+	const char **dir;
+
+	/*
+	 * Add the starting directories to search in reverse order of
+	 * importance, since we are using a stack...
+	 */
+	for (dir = devdirs; *dir; dir++)
+		add_to_dirlist(*dir, NULL, &list);
+
+	while (list) {
+		struct dir_list *current = list;
+
+		list = list->next;
+		DBG(DEBUG_DEVNO, printf("directory %s\n", current->name));
+		blkid__scan_dir(current->name, devno, &new_list, &devname);
+		free(current->name);
+		free(current);
+		if (devname)
+			break;
+		/*
+		 * If we're done checking at this level, descend to
+		 * the next level of subdirectories. (breadth-first)
+		 */
+		if (list == NULL) {
+			list = new_list;
+			new_list = NULL;
+		}
+	}
+	free_dirlist(&list);
+	free_dirlist(&new_list);
+
+	return devname;
+}
+
+/**
+ * blkid_devno_to_devname:
+ * @devno: device number
+ *
+ * This function finds the pathname to a block device with a given
+ * device number.
+ *
+ * Returns: a pointer to allocated memory to the pathname on success,
+ * and NULL on failure.
+ */
+char *blkid_devno_to_devname(dev_t devno)
+{
+	char *path = NULL;
+	char buf[PATH_MAX];
+
+	path = sysfs_devno_to_devpath(devno, buf, sizeof(buf));
+	if (path)
+		path = strdup(path);
+	if (!path)
+		path = scandev_devno_to_devpath(devno);
+
+	if (!path) {
+		DBG(DEBUG_DEVNO,
+		    printf("blkid: couldn't find devno 0x%04lx\n",
+			   (unsigned long) devno));
+	} else {
+		DBG(DEBUG_DEVNO,
+		    printf("found devno 0x%04llx as %s\n", (long long)devno, path));
+	}
+
+	return path;
+}
+
+
+/**
+ * blkid_devno_to_wholedisk:
+ * @dev: device number
+ * @diskname: buffer to return diskname (or NULL)
+ * @len: diskname buffer size (or 0)
+ * @diskdevno: pointer to returns devno of entire disk (or NULL)
+ *
+ * This function uses sysfs to convert the @devno device number to the *name*
+ * of the whole disk. The function DOES NOT return full device name. The @dev
+ * argument could be partition or whole disk -- both is converted.
+ *
+ * For example: sda1, 0x0801 --> sda, 0x0800
+ *
+ * For conversion to the full disk *path* use blkid_devno_to_devname(), for
+ * example:
+ *
+ * <informalexample>
+ *  <programlisting>
+ *
+ *	dev_t dev = 0x0801, disk;		// sda1 = 8:1
+ *	char *diskpath, diskname[32];
+ *
+ *	blkid_devno_to_wholedisk(dev, diskname, sizeof(diskname), &disk);
+ *	diskpath = blkid_devno_to_devname(disk);
+ *
+ *	// print "0x0801: sda, /dev/sda, 8:0
+ *	printf("0x%x: %s, %s, %d:%d\n",
+ *		dev, diskname, diskpath, major(disk), minor(disk));
+ *
+ *	free(diskpath);
+ *
+ *  </programlisting>
+ * </informalexample>
+ *
+ * Returns: 0 on success or -1 in case of error.
+ */
+int blkid_devno_to_wholedisk(dev_t dev, char *diskname,
+			size_t len, dev_t *diskdevno)
+{
+	return sysfs_devno_to_wholedisk( dev, diskname, len, diskdevno);
+}
+
+/*
+ * Returns 1 if the @major number is associated with @drvname.
+ */
+int blkid_driver_has_major(const char *drvname, int major)
+{
+	FILE *f;
+	char buf[128];
+	int match = 0;
+
+	f = fopen(_PATH_PROC_DEVICES, "r");
+	if (!f)
+		return 0;
+
+	while (fgets(buf, sizeof(buf), f)) {	/* skip to block dev section */
+		if (strncmp("Block devices:\n", buf, sizeof(buf)) == 0)
+			break;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		int maj;
+		char name[64 + 1];
+
+		if (sscanf(buf, "%d %64[^\n ]", &maj, name) != 2)
+			continue;
+
+		if (maj == major && strcmp(name, drvname) == 0) {
+			match = 1;
+			break;
+		}
+	}
+
+	fclose(f);
+
+	DBG(DEBUG_DEVNO, printf("major %d %s associated with '%s' driver\n",
+			major, match ? "is" : "is NOT", drvname));
+	return match;
+}
+
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+	char	*devname, *tmp;
+	char	diskname[PATH_MAX];
+	int	major, minor;
+	dev_t	devno, disk_devno;
+	const char *errmsg = "Couldn't parse %s: %s\n";
+
+	blkid_init_debug(DEBUG_ALL);
+	if ((argc != 2) && (argc != 3)) {
+		fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n"
+			"Resolve a device number to a device name\n",
+			argv[0], argv[0]);
+		exit(1);
+	}
+	if (argc == 2) {
+		devno = strtoul(argv[1], &tmp, 0);
+		if (*tmp) {
+			fprintf(stderr, errmsg, "device number", argv[1]);
+			exit(1);
+		}
+	} else {
+		major = strtoul(argv[1], &tmp, 0);
+		if (*tmp) {
+			fprintf(stderr, errmsg, "major number", argv[1]);
+			exit(1);
+		}
+		minor = strtoul(argv[2], &tmp, 0);
+		if (*tmp) {
+			fprintf(stderr, errmsg, "minor number", argv[2]);
+			exit(1);
+		}
+		devno = makedev(major, minor);
+	}
+	printf("Looking for device 0x%04llx\n", (long long)devno);
+	devname = blkid_devno_to_devname(devno);
+	free(devname);
+
+	printf("Looking for whole-device for 0x%04llx\n", (long long)devno);
+	blkid_devno_to_wholedisk(devno, diskname, sizeof(diskname), &disk_devno);
+
+	return 0;
+}
+#endif
diff --git a/src/encode.c b/src/encode.c
new file mode 100644
index 0000000..9a0570e
--- /dev/null
+++ b/src/encode.c
@@ -0,0 +1,338 @@
+
+/*
+ * encode.c - string conversion routines (mostly for compatibility with
+ *            udev/volume_id)
+ *
+ * Copyright (C) 2008 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "blkidP.h"
+
+#define UDEV_ALLOWED_CHARS_INPUT               "/ $%?,"
+
+/**
+ * SECTION: encode
+ * @title: Encoding utils
+ * @short_description: encode strings to safe udev-compatible formats
+ *
+ */
+
+/* count of characters used to encode one unicode char */
+static int utf8_encoded_expected_len(const char *str)
+{
+	unsigned char c = (unsigned char)str[0];
+
+	if (c < 0x80)
+		return 1;
+	if ((c & 0xe0) == 0xc0)
+		return 2;
+	if ((c & 0xf0) == 0xe0)
+		return 3;
+	if ((c & 0xf8) == 0xf0)
+		return 4;
+	if ((c & 0xfc) == 0xf8)
+		return 5;
+	if ((c & 0xfe) == 0xfc)
+		return 6;
+	return 0;
+}
+
+/* decode one unicode char */
+static int utf8_encoded_to_unichar(const char *str)
+{
+	int unichar;
+	int len;
+	int i;
+
+	len = utf8_encoded_expected_len(str);
+	switch (len) {
+	case 1:
+		return (int)str[0];
+	case 2:
+		unichar = str[0] & 0x1f;
+		break;
+	case 3:
+		unichar = (int)str[0] & 0x0f;
+		break;
+	case 4:
+		unichar = (int)str[0] & 0x07;
+		break;
+	case 5:
+		unichar = (int)str[0] & 0x03;
+		break;
+	case 6:
+		unichar = (int)str[0] & 0x01;
+		break;
+	default:
+		return -1;
+	}
+
+	for (i = 1; i < len; i++) {
+		if (((int)str[i] & 0xc0) != 0x80)
+			return -1;
+		unichar <<= 6;
+		unichar |= (int)str[i] & 0x3f;
+	}
+
+	return unichar;
+}
+
+/* expected size used to encode one unicode char */
+static int utf8_unichar_to_encoded_len(int unichar)
+{
+	if (unichar < 0x80)
+		return 1;
+	if (unichar < 0x800)
+		return 2;
+	if (unichar < 0x10000)
+		return 3;
+	if (unichar < 0x200000)
+		return 4;
+	if (unichar < 0x4000000)
+		return 5;
+	return 6;
+}
+
+/* check if unicode char has a valid numeric range */
+static int utf8_unichar_valid_range(int unichar)
+{
+	if (unichar > 0x10ffff)
+		return 0;
+	if ((unichar & 0xfffff800) == 0xd800)
+		return 0;
+	if ((unichar > 0xfdcf) && (unichar < 0xfdf0))
+		return 0;
+	if ((unichar & 0xffff) == 0xffff)
+		return 0;
+	return 1;
+}
+
+/* validate one encoded unicode char and return its length */
+static int utf8_encoded_valid_unichar(const char *str)
+{
+	int len;
+	int unichar;
+	int i;
+
+	len = utf8_encoded_expected_len(str);
+	if (len == 0)
+		return -1;
+
+	/* ascii is valid */
+	if (len == 1)
+		return 1;
+
+	/* check if expected encoded chars are available */
+	for (i = 0; i < len; i++)
+		if ((str[i] & 0x80) != 0x80)
+			return -1;
+
+	unichar = utf8_encoded_to_unichar(str);
+
+	/* check if encoded length matches encoded value */
+	if (utf8_unichar_to_encoded_len(unichar) != len)
+		return -1;
+
+	/* check if value has valid range */
+	if (!utf8_unichar_valid_range(unichar))
+		return -1;
+
+	return len;
+}
+
+static int replace_whitespace(const char *str, char *to, size_t len)
+{
+	size_t i, j;
+
+	/* strip trailing whitespace */
+	len = strnlen(str, len);
+	while (len && isspace(str[len-1]))
+		len--;
+
+	/* strip leading whitespace */
+	i = 0;
+	while (isspace(str[i]) && (i < len))
+		i++;
+
+	j = 0;
+	while (i < len) {
+		/* substitute multiple whitespace with a single '_' */
+		if (isspace(str[i])) {
+			while (isspace(str[i]))
+				i++;
+			to[j++] = '_';
+		}
+		to[j++] = str[i++];
+	}
+	to[j] = '\0';
+	return 0;
+}
+
+static int is_whitelisted(char c, const char *white)
+{
+	if ((c >= '0' && c <= '9') ||
+	    (c >= 'A' && c <= 'Z') ||
+	    (c >= 'a' && c <= 'z') ||
+	    strchr("#+-.:=@_", c) != NULL ||
+	    (white != NULL && strchr(white, c) != NULL))
+		return 1;
+	return 0;
+}
+
+/* allow chars in whitelist, plain ascii, hex-escaping and valid utf8 */
+static int replace_chars(char *str, const char *white)
+{
+	size_t i = 0;
+	int replaced = 0;
+
+	while (str[i] != '\0') {
+		int len;
+
+		if (is_whitelisted(str[i], white)) {
+			i++;
+			continue;
+		}
+
+		/* accept hex encoding */
+		if (str[i] == '\\' && str[i+1] == 'x') {
+			i += 2;
+			continue;
+		}
+
+		/* accept valid utf8 */
+		len = utf8_encoded_valid_unichar(&str[i]);
+		if (len > 1) {
+			i += len;
+			continue;
+		}
+
+		/* if space is allowed, replace whitespace with ordinary space */
+		if (isspace(str[i]) && white != NULL && strchr(white, ' ') != NULL) {
+			str[i] = ' ';
+			i++;
+			replaced++;
+			continue;
+		}
+
+		/* everything else is replaced with '_' */
+		str[i] = '_';
+		i++;
+		replaced++;
+	}
+	return replaced;
+}
+
+size_t blkid_encode_to_utf8(int enc, unsigned char *dest, size_t len,
+			const unsigned char *src, size_t count)
+{
+	size_t i, j;
+	uint16_t c;
+
+	for (j = i = 0; i + 2 <= count; i += 2) {
+		if (enc == BLKID_ENC_UTF16LE)
+			c = (src[i+1] << 8) | src[i];
+		else /* BLKID_ENC_UTF16BE */
+			c = (src[i] << 8) | src[i+1];
+		if (c == 0) {
+			dest[j] = '\0';
+			break;
+		} else if (c < 0x80) {
+			if (j+1 >= len)
+				break;
+			dest[j++] = (uint8_t) c;
+		} else if (c < 0x800) {
+			if (j+2 >= len)
+				break;
+			dest[j++] = (uint8_t) (0xc0 | (c >> 6));
+			dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
+		} else {
+			if (j+3 >= len)
+				break;
+			dest[j++] = (uint8_t) (0xe0 | (c >> 12));
+			dest[j++] = (uint8_t) (0x80 | ((c >> 6) & 0x3f));
+			dest[j++] = (uint8_t) (0x80 | (c & 0x3f));
+		}
+	}
+	dest[j] = '\0';
+	return j;
+}
+
+/**
+ * blkid_encode_string:
+ * @str: input string to be encoded
+ * @str_enc: output string to store the encoded input string
+ * @len: maximum size of the output string, which may be
+ *       four times as long as the input string
+ *
+ * Encode all potentially unsafe characters of a string to the
+ * corresponding hex value prefixed by '\x'.
+ *
+ * Returns: 0 if the entire string was copied, non-zero otherwise.
+ **/
+int blkid_encode_string(const char *str, char *str_enc, size_t len)
+{
+	size_t i, j;
+
+	if (str == NULL || str_enc == NULL)
+		return -1;
+
+	for (i = 0, j = 0; str[i] != '\0'; i++) {
+		int seqlen;
+
+		seqlen = utf8_encoded_valid_unichar(&str[i]);
+		if (seqlen > 1) {
+			if (len-j < (size_t)seqlen)
+				goto err;
+			memcpy(&str_enc[j], &str[i], seqlen);
+			j += seqlen;
+			i += (seqlen-1);
+		} else if (str[i] == '\\' || !is_whitelisted(str[i], NULL)) {
+			if (len-j < 4)
+				goto err;
+			sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]);
+			j += 4;
+		} else {
+			if (len-j < 1)
+				goto err;
+			str_enc[j] = str[i];
+			j++;
+		}
+		if (j+3 >= len)
+			goto err;
+	}
+	if (len-j < 1)
+		goto err;
+	str_enc[j] = '\0';
+	return 0;
+err:
+	return -1;
+}
+
+/**
+ * blkid_safe_string:
+ * @str: input string
+ * @str_safe: output string
+ * @len: size of output string
+ *
+ * Allows plain ascii, hex-escaping and valid utf8. Replaces all whitespaces
+ * with '_'.
+ *
+ * Returns: 0 on success or -1 in case of error.
+ */
+int blkid_safe_string(const char *str, char *str_safe, size_t len)
+{
+	replace_whitespace(str, str_safe, len);
+	replace_chars(str_safe, UDEV_ALLOWED_CHARS_INPUT);
+	return 0;
+}
diff --git a/src/evaluate.c b/src/evaluate.c
new file mode 100644
index 0000000..5a84b43
--- /dev/null
+++ b/src/evaluate.c
@@ -0,0 +1,328 @@
+/*
+ * evaluate.c - very high-level API to evaluate LABELs or UUIDs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <stdint.h>
+#include <stdarg.h>
+
+#include "pathnames.h"
+#include "canonicalize.h"
+
+#include "blkidP.h"
+
+/**
+ * SECTION:evaluate
+ * @title: Tags and Spec evaluation
+ * @short_description: top-level API for LABEL and UUID evaluation.
+ *
+ * This API provides very simple and portable way how evaluate LABEL and UUID
+ * tags.  The blkid_evaluate_tag() and blkid_evaluate_spec() work on 2.4 and
+ * 2.6 systems and on systems with or without udev. Currently, the libblkid
+ * library supports "udev" and "scan" methods. The "udev" method uses udev
+ * /dev/disk/by-* symlinks and the "scan" method scans all block devices from
+ * the /proc/partitions file. The evaluation could be controlled by the
+ * /etc/blkid.conf config file. The default is to try "udev" and then "scan"
+ * method.
+ *
+ * The blkid_evaluate_tag() also automatically informs udevd when an obsolete
+ * /dev/disk/by-* symlink is detected.
+ *
+ * If you are not sure how translate LABEL or UUID to the device name use this
+ * API.
+ */
+
+#ifdef CONFIG_BLKID_VERIFY_UDEV
+/* returns zero when the device has NAME=value (LABEL/UUID) */
+static int verify_tag(const char *devname, const char *name, const char *value)
+{
+	blkid_probe pr;
+	int fd = -1, rc = -1;
+	size_t len;
+	const char *data;
+	int errsv = 0;
+
+	pr = blkid_new_probe();
+	if (!pr)
+		return -1;
+
+	blkid_probe_enable_superblocks(pr, TRUE);
+	blkid_probe_set_superblocks_flags(pr,
+			BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID);
+
+	blkid_probe_enable_partitions(pr, TRUE);
+	blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
+
+	fd = open(devname, O_RDONLY);
+	if (fd < 0) {
+		errsv = errno;
+		goto done;
+	}
+	if (blkid_probe_set_device(pr, fd, 0, 0))
+		goto done;
+	rc = blkid_do_safeprobe(pr);
+	if (rc)
+		goto done;
+	rc = blkid_probe_lookup_value(pr, name, &data, &len);
+	if (!rc)
+		rc = memcmp(value, data, len);
+done:
+	DBG(DEBUG_EVALUATE, printf("%s: %s verification %s\n",
+			devname, name, rc == 0 ? "PASS" : "FAILED"));
+	if (fd >= 0)
+		close(fd);
+	blkid_free_probe(pr);
+
+	/* for non-root users we use unverified udev links */
+	return errsv == EACCES ? 0 : rc;
+}
+#endif /* CONFIG_BLKID_VERIFY_UDEV*/
+
+/**
+ * blkid_send_uevent:
+ * @devname: absolute path to the device
+ * @action: event string
+ *
+ * Returns: -1 in case of failure, or 0 on success.
+ */
+int blkid_send_uevent(const char *devname, const char *action)
+{
+	char uevent[PATH_MAX];
+	struct stat st;
+	FILE *f;
+	int rc = -1;
+
+	DBG(DEBUG_EVALUATE, printf("%s: uevent '%s' requested\n", devname, action));
+
+	if (!devname || !action)
+		return -1;
+	if (stat(devname, &st) || !S_ISBLK(st.st_mode))
+		return -1;
+
+	snprintf(uevent, sizeof(uevent), "/sys/dev/block/%d:%d/uevent",
+			major(st.st_rdev), minor(st.st_rdev));
+
+	f = fopen(uevent, "w");
+	if (f) {
+		rc = 0;
+		if (fputs(action, f) >= 0)
+			rc = 0;
+		fclose(f);
+	}
+	DBG(DEBUG_EVALUATE, printf("%s: send uevent %s\n",
+			uevent, rc == 0 ? "SUCCES" : "FAILED"));
+	return rc;
+}
+
+static char *evaluate_by_udev(const char *token, const char *value, int uevent)
+{
+	char dev[PATH_MAX];
+	char *path = NULL;
+	size_t len;
+	struct stat st;
+
+	DBG(DEBUG_EVALUATE,
+	    printf("evaluating by udev %s=%s\n", token, value));
+
+	if (!strcmp(token, "UUID"))
+		strcpy(dev, _PATH_DEV_BYUUID "/");
+	else if (!strcmp(token, "LABEL"))
+		strcpy(dev, _PATH_DEV_BYLABEL "/");
+	else if (!strcmp(token, "PARTLABEL"))
+		strcpy(dev, _PATH_DEV_BYPARTLABEL "/");
+	else if (!strcmp(token, "PARTUUID"))
+		strcpy(dev, _PATH_DEV_BYPARTUUID "/");
+	else {
+		DBG(DEBUG_EVALUATE,
+		    printf("unsupported token %s\n", token));
+		return NULL;	/* unsupported tag */
+	}
+
+	len = strlen(dev);
+	if (blkid_encode_string(value, &dev[len], sizeof(dev) - len) != 0)
+		return NULL;
+
+	DBG(DEBUG_EVALUATE,
+	    printf("expected udev link: %s\n", dev));
+
+	if (stat(dev, &st))
+		goto failed;	/* link or device does not exist */
+
+	if (!S_ISBLK(st.st_mode))
+		return NULL;
+
+	path = canonicalize_path(dev);
+	if (!path)
+		return NULL;
+
+#ifdef CONFIG_BLKID_VERIFY_UDEV
+	if (verify_tag(path, token, value))
+		goto failed;
+#endif
+	return path;
+
+failed:
+	DBG(DEBUG_EVALUATE, printf("failed to evaluate by udev\n"));
+
+	if (uevent && path)
+		blkid_send_uevent(path, "change");
+	free(path);
+	return NULL;
+}
+
+static char *evaluate_by_scan(const char *token, const char *value,
+		blkid_cache *cache, struct blkid_config *conf)
+{
+	blkid_cache c = cache ? *cache : NULL;
+	char *res;
+
+	DBG(DEBUG_EVALUATE,
+	    printf("evaluating by blkid scan %s=%s\n", token, value));
+
+	if (!c) {
+		char *cachefile = blkid_get_cache_filename(conf);
+		blkid_get_cache(&c, cachefile);
+		free(cachefile);
+	}
+	if (!c)
+		return NULL;
+
+	res = blkid_get_devname(c, token, value);
+
+	if (cache)
+		*cache = c;
+	else
+		blkid_put_cache(c);
+
+	return res;
+}
+
+/**
+ * blkid_evaluate_tag:
+ * @token: token name (e.g "LABEL" or "UUID") or unparsed tag (e.g. "LABEL=foo")
+ * @value: token data (e.g. "foo")
+ * @cache: pointer to cache (or NULL when you don't want to re-use the cache)
+ *
+ * Returns: allocated string with a device name.
+ */
+char *blkid_evaluate_tag(const char *token, const char *value, blkid_cache *cache)
+{
+	struct blkid_config *conf = NULL;
+	char *t = NULL, *v = NULL;
+	char *ret = NULL;
+	int i;
+
+	if (!token)
+		return NULL;
+
+	if (!cache || !*cache)
+		blkid_init_debug(0);
+
+	DBG(DEBUG_EVALUATE,
+	    printf("evaluating  %s%s%s\n", token, value ? "=" : "",
+		   value ? value : ""));
+
+	if (!value) {
+		if (!strchr(token, '=')) {
+			ret = blkid_strdup(token);
+			goto out;
+		}
+		blkid_parse_tag_string(token, &t, &v);
+		if (!t || !v)
+			goto out;
+		token = t;
+		value = v;
+	}
+
+	conf = blkid_read_config(NULL);
+	if (!conf)
+		goto out;
+
+	for (i = 0; i < conf->nevals; i++) {
+		if (conf->eval[i] == BLKID_EVAL_UDEV)
+			ret = evaluate_by_udev(token, value, conf->uevent);
+		else if (conf->eval[i] == BLKID_EVAL_SCAN)
+			ret = evaluate_by_scan(token, value, cache, conf);
+		if (ret)
+			break;
+	}
+
+	DBG(DEBUG_EVALUATE,
+	    printf("%s=%s evaluated as %s\n", token, value, ret));
+out:
+	blkid_free_config(conf);
+	free(t);
+	free(v);
+	return ret;
+}
+
+/**
+ * blkid_evaluate_spec:
+ * @spec: unparsed tag (e.g. "LABEL=foo") or path (e.g. /dev/dm-0)
+ * @cache: pointer to cache (or NULL when you don't want to re-use the cache)
+ *
+ * All returned paths are canonicalized, device-mapper paths are converted
+ * to the /dev/mapper/name format.
+ *
+ * Returns: allocated string with a device name.
+ */
+char *blkid_evaluate_spec(const char *spec, blkid_cache *cache)
+{
+	char *t = NULL, *v = NULL, *res;
+
+	if (!spec)
+		return NULL;
+
+	if (strchr(spec, '=') &&
+	    blkid_parse_tag_string(spec, &t, &v) != 0)	/* parse error */
+		return NULL;
+
+	if (v)
+		res = blkid_evaluate_tag(t, v, cache);
+	else
+		res = canonicalize_path(spec);
+
+	free(t);
+	free(v);
+	return res;
+}
+
+
+#ifdef TEST_PROGRAM
+int main(int argc, char *argv[])
+{
+	blkid_cache cache = NULL;
+	char *res;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <tag> | <spec>\n", argv[0]);
+		return EXIT_FAILURE;
+	}
+
+	blkid_init_debug(0);
+
+	res = blkid_evaluate_spec(argv[1], &cache);
+	if (res)
+		printf("%s\n", res);
+	if (cache)
+		blkid_put_cache(cache);
+
+	return res ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+#endif
diff --git a/src/getsize.c b/src/getsize.c
new file mode 100644
index 0000000..abe6ebc
--- /dev/null
+++ b/src/getsize.c
@@ -0,0 +1,34 @@
+/*
+ * getsize.c --- get the size of a partition.
+ *
+ * Copyright (C) 1995, 1995 Theodore Ts'o.
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "blkidP.h"
+
+/**
+ * blkid_get_dev_size:
+ * @fd: file descriptor
+ *
+ * Returns: size (in bytes) of the block device or size of the regular file or 0.
+ */
+blkid_loff_t blkid_get_dev_size(int fd)
+{
+	unsigned long long bytes;
+
+	if (blkdev_get_size(fd, &bytes))
+		return 0;
+
+	return bytes;
+}
+
diff --git a/src/llseek.c b/src/llseek.c
new file mode 100644
index 0000000..7fb44c4
--- /dev/null
+++ b/src/llseek.c
@@ -0,0 +1,142 @@
+/*
+ * llseek.c -- stub calling the llseek system call
+ *
+ * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef __MSDOS__
+#include <io.h>
+#endif
+
+#include "blkidP.h"
+
+#ifdef __linux__
+
+#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
+
+#define my_llseek lseek64
+
+#elif defined(HAVE_LLSEEK)
+#include <syscall.h>
+
+#ifndef HAVE_LLSEEK_PROTOTYPE
+extern long long llseek(int fd, long long offset, int origin);
+#endif
+
+#define my_llseek llseek
+
+#else	/* ! HAVE_LLSEEK */
+
+#if SIZEOF_LONG == SIZEOF_LONG_LONG
+
+#define llseek lseek
+
+#else /* SIZEOF_LONG != SIZEOF_LONG_LONG */
+
+#include <linux/unistd.h>
+
+#ifndef __NR__llseek
+#define __NR__llseek            140
+#endif
+
+#ifndef __i386__
+static int _llseek(unsigned int, unsigned long, unsigned long,
+		   blkid_loff_t *, unsigned int);
+
+static _syscall5(int, _llseek, unsigned int, fd, unsigned long, offset_high,
+		 unsigned long, offset_low, blkid_loff_t *, result,
+		 unsigned int, origin)
+#endif
+
+static blkid_loff_t my_llseek(int fd, blkid_loff_t offset, int origin)
+{
+	blkid_loff_t result;
+	int retval;
+
+#ifndef __i386__
+	retval = _llseek(fd, ((unsigned long long) offset) >> 32,
+			 ((unsigned long long)offset) & 0xffffffff,
+			 &result, origin);
+#else
+	retval = syscall(__NR__llseek, fd, ((unsigned long long) offset) >> 32,
+			 ((unsigned long long)offset) & 0xffffffff,
+			 &result, origin);
+#endif
+	return (retval == -1 ? (blkid_loff_t) retval : result);
+}
+
+#endif	/* __alpha__ || __ia64__ */
+
+#endif /* HAVE_LLSEEK */
+
+blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence)
+{
+	blkid_loff_t result;
+	static int do_compat = 0;
+
+	if ((sizeof(off_t) >= sizeof(blkid_loff_t)) ||
+	    (offset < ((blkid_loff_t) 1 << ((sizeof(off_t)*8) -1))))
+		return lseek(fd, (off_t) offset, whence);
+
+	if (do_compat) {
+		errno = EOVERFLOW;
+		return -1;
+	}
+
+	result = my_llseek(fd, offset, whence);
+	if (result == -1 && errno == ENOSYS) {
+		/*
+		 * Just in case this code runs on top of an old kernel
+		 * which does not support the llseek system call
+		 */
+		do_compat++;
+		errno = EOVERFLOW;
+	}
+	return result;
+}
+
+#else /* !linux */
+
+#ifndef EOVERFLOW
+#ifdef EXT2_ET_INVALID_ARGUMENT
+#define EOVERFLOW EXT2_ET_INVALID_ARGUMENT
+#else
+#define EOVERFLOW 112
+#endif
+#endif
+
+blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int origin)
+{
+#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
+	return lseek64 (fd, offset, origin);
+#else
+	if ((sizeof(off_t) < sizeof(blkid_loff_t)) &&
+	    (offset >= ((blkid_loff_t) 1 << ((sizeof(off_t)*8) - 1)))) {
+		errno = EOVERFLOW;
+		return -1;
+	}
+	return lseek(fd, (off_t) offset, origin);
+#endif
+}
+
+#endif	/* linux */
+
+
diff --git a/src/partitions/aix.c b/src/partitions/aix.c
new file mode 100644
index 0000000..de397bf
--- /dev/null
+++ b/src/partitions/aix.c
@@ -0,0 +1,59 @@
+/*
+ * aix partitions
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+#include "aix.h"
+
+static int probe_aix_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	blkid_partlist ls;
+	blkid_parttable tab;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	tab = blkid_partlist_new_parttable(ls, "aix", 0);
+	if (!tab)
+		goto err;
+
+	return 0;
+err:
+	return -1;
+}
+
+/*
+ * We know nothing about AIX on-disk structures. Everything what we know is the
+ * magic number at begin of the disk.
+ *
+ * Note, Linux kernel is tring to be smart and AIX signature is ignored when
+ * there is a valid DOS partitions table. We don't support such behaviour. All
+ * fdisk-like programs has to properly wipe the fist sector. Everything other
+ * is a bug.
+ */
+const struct blkid_idinfo aix_pt_idinfo =
+{
+	.name		= "aix",
+	.probefunc	= probe_aix_pt,
+	.magics		=
+	{
+		{ .magic = BLKID_AIX_MAGIC_STRING, .len = BLKID_AIX_MAGIC_STRLEN },
+		{ NULL }
+	}
+};
+
diff --git a/src/partitions/aix.h b/src/partitions/aix.h
new file mode 100644
index 0000000..f767c5a
--- /dev/null
+++ b/src/partitions/aix.h
@@ -0,0 +1,7 @@
+#ifndef BLKID_PARTITIONS_AIX_H
+#define BLKID_PARTITIONS_AIX_H
+
+#define BLKID_AIX_MAGIC_STRING	"\xC9\xC2\xD4\xC1"
+#define BLKID_AIX_MAGIC_STRLEN	(sizeof(BLKID_AIX_MAGIC_STRING) - 1)
+
+#endif
diff --git a/src/partitions/blkid_parttypes.h b/src/partitions/blkid_parttypes.h
new file mode 100644
index 0000000..b0aad86
--- /dev/null
+++ b/src/partitions/blkid_parttypes.h
@@ -0,0 +1,121 @@
+/*
+ * Partition types
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+/* Note, _L32M means <32M (less), for example FAT16_L32M */
+
+enum {
+	BLKID_EMPTY_PARTITION			= 0x00,
+	BLKID_FAT12_PARTITION			= 0x01,
+	BLKID_XENIX_ROOT_PARTITION		= 0x02,
+	BLKID_XENIX_USR_PARTITION		= 0x03,
+	BLKID_FAT16_LESS32M_PARTITION		= 0x04,
+	BLKID_DOS_EXTENDED_PARTITION		= 0x05,
+	BLKID_FAT16_PARTITION			= 0x06, /* DOS 16-bit >=32M */
+	BLKID_HPFS_NTFS_PARTITION		= 0x07, /* OS/2 IFS, eg, HPFS or NTFS or QNX */
+	BLKID_AIX_PARTITION			= 0x08, /* AIX boot (AIX -- PS/2 port) or SplitDrive */
+	BLKID_AIX_BOOTABLE_PARTITION		= 0x09, /* AIX data or Coherent */
+	BLKID_OS2_BOOTMNGR_PARTITION		= 0x0a, /* OS/2 Boot Manager */
+	BLKID_W95_FAT32_PARTITION		= 0x0b,
+	BLKID_W95_FAT32_LBA_PARTITION		= 0x0c, /* LBA really is `Extended Int 13h' */
+	BLKID_W95_FAT16_LBA_PARTITION		= 0x0e,
+	BLKID_W95_EXTENDED_PARTITION		= 0x0f,
+	BLKID_OPUS_PARTITION			= 0x10,
+	BLKID_HIDDEN_FAT12_PARTITION		= 0x11,
+	BLKID_COMPAQ_DIAGNOSTICS_PARTITION	= 0x12,
+	BLKID_HIDDEN_FAT16_L32M_PARTITION	= 0x14,
+	BLKID_HIDDEN_FAT16_PARTITION		= 0x16,
+	BLKID_HIDDEN_HPFS_NTFS_PARTITION	= 0x17,
+	BLKID_AST_SMARTSLEEP_PARTITION		= 0x18,
+	BLKID_HIDDEN_W95_FAT32_PARTITION	= 0x1b,
+	BLKID_HIDDEN_W95_FAT32LBA_PARTITION	= 0x1c,
+	BLKID_HIDDEN_W95_FAT16LBA_PARTITION	= 0x1e,
+	BLKID_NEC_DOS_PARTITION			= 0x24,
+	BLKID_PLAN9_PARTITION			= 0x39,
+	BLKID_PARTITIONMAGIC_PARTITION		= 0x3c,
+	BLKID_VENIX80286_PARTITION		= 0x40,
+	BLKID_PPC_PREP_BOOT_PARTITION		= 0x41,
+	BLKID_SFS_PARTITION			= 0x42,
+	BLKID_QNX_4X_PARTITION			= 0x4d,
+	BLKID_QNX_4X_2ND_PARTITION		= 0x4e,
+	BLKID_QNX_4X_3RD_PARTITION		= 0x4f,
+	BLKID_DM_PARTITION			= 0x50,
+	BLKID_DM6_AUX1_PARTITION		= 0x51, /* (or Novell) */
+	BLKID_CPM_PARTITION			= 0x52, /* CP/M or Microport SysV/AT */
+	BLKID_DM6_AUX3_PARTITION		= 0x53,
+	BLKID_DM6_PARTITION			= 0x54,
+	BLKID_EZ_DRIVE_PARTITION		= 0x55,
+	BLKID_GOLDEN_BOW_PARTITION		= 0x56,
+	BLKID_PRIAM_EDISK_PARTITION		= 0x5c,
+	BLKID_SPEEDSTOR_PARTITION		= 0x61,
+	BLKID_GNU_HURD_PARTITION		= 0x63, /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
+	BLKID_UNIXWARE_PARTITION		= BLKID_GNU_HURD_PARTITION,
+	BLKID_NETWARE_286_PARTITION		= 0x64,
+	BLKID_NETWARE_386_PARTITION		= 0x65,
+	BLKID_DISKSECURE_MULTIBOOT_PARTITION	= 0x70,
+	BLKID_PC_IX_PARTITION			= 0x75,
+	BLKID_OLD_MINIX_PARTITION		= 0x80, /* Minix 1.4a and earlier */
+	BLKID_MINIX_PARTITION			= 0x81, /* Minix 1.4b and later */
+	BLKID_LINUX_SWAP_PARTITION		= 0x82,
+	BLKID_SOLARIS_X86_PARTITION		= BLKID_LINUX_SWAP_PARTITION,
+	BLKID_LINUX_DATA_PARTITION		= 0x83,
+	BLKID_OS2_HIDDEN_DRIVE_PARTITION	= 0x84,
+	BLKID_LINUX_EXTENDED_PARTITION		= 0x85,
+	BLKID_NTFS_VOL_SET1_PARTITION		= 0x86,
+	BLKID_NTFS_VOL_SET2_PARTITION		= 0x87,
+	BLKID_LINUX_PLAINTEXT_PARTITION		= 0x88,
+	BLKID_LINUX_LVM_PARTITION		= 0x8e,
+	BLKID_AMOEBA_PARTITION			= 0x93,
+	BLKID_AMOEBA_BBT_PARTITION		= 0x94, /* (bad block table) */
+	BLKID_BSD_OS_PARTITION			= 0x9f, /* BSDI */
+	BLKID_THINKPAD_HIBERNATION_PARTITION	= 0xa0,
+	BLKID_FREEBSD_PARTITION			= 0xa5, /* various BSD flavours */
+	BLKID_OPENBSD_PARTITION			= 0xa6,
+	BLKID_NEXTSTEP_PARTITION		= 0xa7,
+	BLKID_DARWIN_UFS_PARTITION		= 0xa8,
+	BLKID_NETBSD_PARTITION			= 0xa9,
+	BLKID_DARWIN_BOOT_PARTITION		= 0xab,
+	BLKID_HFS_HFS_PARTITION			= 0xaf,
+	BLKID_BSDI_FS_PARTITION			= 0xb7,
+	BLKID_BSDI_SWAP_PARTITION		= 0xb8,
+	BLKID_BOOTWIZARD_HIDDEN_PARTITION	= 0xbb,
+	BLKID_SOLARIS_BOOT_PARTITION		= 0xbe,
+	BLKID_SOLARIS_PARTITION			= 0xbf,
+	BLKID_DRDOS_FAT12_PARTITION		= 0xc1,
+	BLKID_DRDOS_FAT16_L32M_PARTITION	= 0xc4,
+	BLKID_DRDOS_FAT16_PARTITION		= 0xc6,
+	BLKID_SYRINX_PARTITION			= 0xc7,
+	BLKID_NONFS_DATA_PARTITION		= 0xda,
+	BLKID_CPM_CTOS_PARTITION		= 0xdb, /* CP/M or Concurrent CP/M or Concurrent DOS or CTOS */
+	BLKID_DELL_UTILITY_PARTITION		= 0xde, /* Dell PowerEdge Server utilities */
+	BLKID_BOOTIT_PARTITION			= 0xdf, /* BootIt EMBRM */
+	BLKID_DOS_ACCESS_PARTITION		= 0xe1, /* DOS access or SpeedStor 12-bit FAT extended partition */
+	BLKID_DOS_RO_PARTITION			= 0xe3, /* DOS R/O or SpeedStor */
+	BLKID_SPEEDSTOR_EXTENDED_PARTITION	= 0xe4, /* SpeedStor 16-bit FAT extended partition < 1024 cyl. */
+	BLKID_BEOS_FS_PARTITION			= 0xeb,
+	BLKID_GPT_PARTITION			= 0xee, /* Intel EFI GUID Partition Table */
+	BLKID_EFI_SYSTEM_PARTITION		= 0xef, /* Intel EFI System Partition */
+	BLKID_LINUX_PARISC_BOOT_PARTITION	= 0xf0, /* Linux/PA-RISC boot loader */
+	BLKID_SPEEDSTOR1_PARTITION		= 0xf1,
+	BLKID_SPEEDSTOR2_PARTITION		= 0xf4, /* SpeedStor large partition */
+	BLKID_DOS_SECONDARY_PARTITION		= 0xf2, /* DOS 3.3+ secondary */
+	BLKID_VMWARE_VMFS_PARTITION		= 0xfb,
+	BLKID_VMWARE_VMKCORE_PARTITION		= 0xfc, /* VMware kernel dump partition */
+	BLKID_LINUX_RAID_PARTITION		= 0xfd, /* New (2.2.x) raid partition with autodetect using persistent superblock */
+	BLKID_LANSTEP_PARTITION			= 0xfe, /* SpeedStor >1024 cyl. or LANstep */
+	BLKID_XENIX_BBT_PARTITION		= 0xff, /* Xenix Bad Block Table */
+};
diff --git a/src/partitions/bsd.c b/src/partitions/bsd.c
new file mode 100644
index 0000000..ee15ad2
--- /dev/null
+++ b/src/partitions/bsd.c
@@ -0,0 +1,243 @@
+/*
+ * BSD/OSF partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Inspired by fdisk, partx, Linux kernel, libparted and openbsd header files.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+
+#define BSD_MAXPARTITIONS	16
+#define BSD_FS_UNUSED		0
+
+struct bsd_disklabel {
+	uint32_t	d_magic;		/* the magic number */
+	int16_t		d_type;			/* drive type */
+	int16_t		d_subtype;		/* controller/d_type specific */
+	char		d_typename[16];		/* type name, e.g. "eagle" */
+	char		d_packname[16];		/* pack identifier */
+
+			/* disk geometry: */
+	uint32_t	d_secsize;		/* # of bytes per sector */
+	uint32_t	d_nsectors;		/* # of data sectors per track */
+	uint32_t	d_ntracks;		/* # of tracks per cylinder */
+	uint32_t	d_ncylinders;		/* # of data cylinders per unit */
+	uint32_t	d_secpercyl;		/* # of data sectors per cylinder */
+	uint32_t	d_secperunit;		/* # of data sectors per unit */
+
+	/*
+	 * Spares (bad sector replacements) below
+	 * are not counted in d_nsectors or d_secpercyl.
+	 * Spare sectors are assumed to be physical sectors
+	 * which occupy space at the end of each track and/or cylinder.
+	 */
+	uint16_t	d_sparespertrack;	/* # of spare sectors per track */
+	uint16_t	d_sparespercyl;		/* # of spare sectors per cylinder */
+
+	/*
+	 * Alternate cylinders include maintenance, replacement,
+	 * configuration description areas, etc.
+	 */
+	uint32_t	d_acylinders;		/* # of alt. cylinders per unit */
+
+			/* hardware characteristics: */
+	/*
+	 * d_interleave, d_trackskew and d_cylskew describe perturbations
+	 * in the media format used to compensate for a slow controller.
+	 * Interleave is physical sector interleave, set up by the formatter
+	 * or controller when formatting.  When interleaving is in use,
+	 * logically adjacent sectors are not physically contiguous,
+	 * but instead are separated by some number of sectors.
+	 * It is specified as the ratio of physical sectors traversed
+	 * per logical sector.  Thus an interleave of 1:1 implies contiguous
+	 * layout, while 2:1 implies that logical sector 0 is separated
+	 * by one sector from logical sector 1.
+	 * d_trackskew is the offset of sector 0 on track N
+	 * relative to sector 0 on track N-1 on the same cylinder.
+	 * Finally, d_cylskew is the offset of sector 0 on cylinder N
+	 * relative to sector 0 on cylinder N-1.
+	 */
+	uint16_t	d_rpm;			/* rotational speed */
+	uint16_t	d_interleave;		/* hardware sector interleave */
+	uint16_t	d_trackskew;		/* sector 0 skew, per track */
+	uint16_t	d_cylskew;		/* sector 0 skew, per cylinder */
+	uint32_t	d_headswitch;		/* head switch time, usec */
+	uint32_t	d_trkseek;		/* track-to-track seek, usec */
+	uint32_t	d_flags;		/* generic flags */
+	uint32_t	d_drivedata[5];		/* drive-type specific information */
+	uint32_t	d_spare[5];		/* reserved for future use */
+	uint32_t	d_magic2;		/* the magic number (again) */
+	uint16_t	d_checksum;		/* xor of data incl. partitions */
+
+			/* filesystem and partition information: */
+	uint16_t	d_npartitions;	        /* number of partitions in following */
+	uint32_t	d_bbsize;	        /* size of boot area at sn0, bytes */
+	uint32_t	d_sbsize;	        /* max size of fs superblock, bytes */
+
+	struct bsd_partition	 {		/* the partition table */
+		uint32_t	p_size;	        /* number of sectors in partition */
+		uint32_t	p_offset;       /* starting sector */
+		uint32_t	p_fsize;        /* filesystem basic fragment size */
+		uint8_t		p_fstype;	/* filesystem type, see below */
+		uint8_t		p_frag;		/* filesystem fragments per block */
+		uint16_t	p_cpg;	        /* filesystem cylinders per group */
+	} __attribute__((packed)) d_partitions[BSD_MAXPARTITIONS];	/* actually may be more */
+} __attribute__((packed));
+
+
+/* Returns 'blkid_idmag' in 512-sectors */
+#define BLKID_MAG_SECTOR(_mag)  (((_mag)->kboff * 2)  + ((_mag)->sboff >> 9))
+
+/* Returns 'blkid_idmag' in bytes */
+#define BLKID_MAG_OFFSET(_mag)  ((_mag)->kboff >> 10) + ((_mag)->sboff)
+
+/* Returns 'blkid_idmag' offset in bytes within the last sector */
+#define BLKID_MAG_LASTOFFSET(_mag) \
+		 (BLKID_MAG_OFFSET(_mag) - (BLKID_MAG_SECTOR(_mag) << 9))
+
+static int probe_bsd_pt(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct bsd_disklabel *l;
+	struct bsd_partition *p;
+	const char *name = "bsd" ;
+	blkid_parttable tab = NULL;
+	blkid_partition parent;
+	blkid_partlist ls;
+	int i, nparts = BSD_MAXPARTITIONS;
+	unsigned char *data;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	data = blkid_probe_get_sector(pr, BLKID_MAG_SECTOR(mag));
+	if (!data)
+		goto nothing;
+
+	l = (struct bsd_disklabel *) data + BLKID_MAG_LASTOFFSET(mag);
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	/* try to determine the real type of BSD system according to
+	 * (parental) primary partition */
+	parent = blkid_partlist_get_parent(ls);
+	if (parent) {
+		switch(blkid_partition_get_type(parent)) {
+		case BLKID_FREEBSD_PARTITION:
+			name = "freebsd";
+			break;
+		case BLKID_NETBSD_PARTITION:
+			name = "netbsd";
+			break;
+		case BLKID_OPENBSD_PARTITION:
+			name = "openbsd";
+			break;
+		default:
+			DBG(DEBUG_LOWPROBE, printf(
+				"WARNING: BSD label detected on unknown (0x%x) "
+				"primary partition\n",
+				blkid_partition_get_type(parent)));
+			break;
+		}
+	}
+
+	tab = blkid_partlist_new_parttable(ls, name, BLKID_MAG_OFFSET(mag));
+	if (!tab)
+		goto err;
+
+	if (le16_to_cpu(l->d_npartitions) < BSD_MAXPARTITIONS)
+		nparts = le16_to_cpu(l->d_npartitions);
+
+	else if (le16_to_cpu(l->d_npartitions) > BSD_MAXPARTITIONS)
+		DBG(DEBUG_LOWPROBE, printf(
+			"WARNING: ignore %d more BSD partitions\n",
+			le16_to_cpu(l->d_npartitions) - BSD_MAXPARTITIONS));
+
+	for (i = 0, p = l->d_partitions; i < nparts; i++, p++) {
+		blkid_partition par;
+		uint32_t start, size;
+
+		/* TODO: in fdisk-mode returns all non-zero (p_size) partitions */
+		if (p->p_fstype == BSD_FS_UNUSED)
+			continue;
+
+		start = le32_to_cpu(p->p_offset);
+		size = le32_to_cpu(p->p_size);
+
+		if (parent && !blkid_is_nested_dimension(parent, start, size)) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"WARNING: BSD partition (%d) overflow "
+				"detected, ignore\n", i));
+			continue;
+		}
+
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_type(par, p->p_fstype);
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+
+/*
+ * All BSD variants use the same magic string (little-endian),
+ * and the same disklabel.
+ *
+ * The difference between {Free,Open,...}BSD is in the (parental)
+ * primary partition type.
+ *
+ * See also: http://en.wikipedia.org/wiki/BSD_disklabel
+ *
+ * The location of BSD disk label is architecture specific and in defined by
+ * LABELSECTOR and LABELOFFSET macros in the disklabel.h file. The location
+ * also depends on BSD variant, FreeBSD uses only one location, NetBSD and
+ * OpenBSD are more creative...
+ *
+ * The basic overview:
+ *
+ * arch                    | LABELSECTOR | LABELOFFSET
+ * ------------------------+-------------+------------
+ * amd64 arm hppa hppa64   |             |
+ * i386, macppc, mvmeppc   |      1      |      0
+ * sgi, aviion, sh, socppc |             |
+ * ------------------------+-------------+------------
+ * alpha luna88k mac68k    |      0      |     64
+ * sparc(OpenBSD) vax      |             |
+ * ------------------------+-------------+------------
+ * spark64 sparc(NetBSD)   |      0      |    128
+ * ------------------------+-------------+------------
+ *
+ * ...and more (see http://fxr.watson.org/fxr/ident?v=NETBSD;i=LABELSECTOR)
+ *
+ */
+const struct blkid_idinfo bsd_pt_idinfo =
+{
+	.name		= "bsd",
+	.probefunc	= probe_bsd_pt,
+	.magics		=
+	{
+		{ .magic = "\x57\x45\x56\x82", .len = 4, .sboff = 512 },
+		{ .magic = "\x57\x45\x56\x82", .len = 4, .sboff = 64 },
+		{ .magic = "\x57\x45\x56\x82", .len = 4, .sboff = 128 },
+		{ NULL }
+	}
+};
+
diff --git a/src/partitions/dos.c b/src/partitions/dos.c
new file mode 100644
index 0000000..22d2625
--- /dev/null
+++ b/src/partitions/dos.c
@@ -0,0 +1,288 @@
+/*
+ * MS-DOS partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Inspired by fdisk, partx, Linux kernel and libparted.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+#include "dos.h"
+#include "aix.h"
+
+/* see superblocks/vfat.c */
+extern int blkid_probe_is_vfat(blkid_probe pr);
+
+static const struct dos_subtypes {
+	unsigned char type;
+	const struct blkid_idinfo *id;
+} dos_nested[] = {
+	{ BLKID_FREEBSD_PARTITION, &bsd_pt_idinfo },
+	{ BLKID_NETBSD_PARTITION, &bsd_pt_idinfo },
+	{ BLKID_OPENBSD_PARTITION, &bsd_pt_idinfo },
+	{ BLKID_UNIXWARE_PARTITION, &unixware_pt_idinfo },
+	{ BLKID_SOLARIS_X86_PARTITION, &solaris_x86_pt_idinfo },
+	{ BLKID_MINIX_PARTITION, &minix_pt_idinfo }
+};
+
+static inline int is_extended(struct dos_partition *p)
+{
+	return (p->sys_type == BLKID_DOS_EXTENDED_PARTITION ||
+		p->sys_type == BLKID_W95_EXTENDED_PARTITION ||
+		p->sys_type == BLKID_LINUX_EXTENDED_PARTITION);
+}
+
+static int parse_dos_extended(blkid_probe pr, blkid_parttable tab,
+		uint32_t ex_start, uint32_t ex_size, int ssf)
+{
+	blkid_partlist ls = blkid_probe_get_partlist(pr);
+	uint32_t cur_start = ex_start, cur_size = ex_size;
+	unsigned char *data;
+	int ct_nodata = 0;	/* count ext.partitions without data partitions */
+	int i;
+
+	while (1) {
+		struct dos_partition *p, *p0;
+		uint32_t start, size;
+
+		if (++ct_nodata > 100)
+			return 0;
+		data = blkid_probe_get_sector(pr, cur_start);
+		if (!data)
+			goto leave;	/* malformed partition? */
+
+		if (!is_valid_mbr_signature(data))
+			goto leave;
+
+		p0 = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET);
+
+		/* Usually, the first entry is the real data partition,
+		 * the 2nd entry is the next extended partition, or empty,
+		 * and the 3rd and 4th entries are unused.
+		 * However, DRDOS sometimes has the extended partition as
+		 * the first entry (when the data partition is empty),
+		 * and OS/2 seems to use all four entries.
+		 * -- Linux kernel fs/partitions/dos.c
+		 *
+		 * See also http://en.wikipedia.org/wiki/Extended_boot_record
+		 */
+
+		/* Parse data partition */
+		for (p = p0, i = 0; i < 4; i++, p++) {
+			uint32_t abs_start;
+			blkid_partition par;
+
+			/* the start is relative to the parental ext.partition */
+			start = dos_partition_start(p) * ssf;
+			size = dos_partition_size(p) * ssf;
+			abs_start = cur_start + start;	/* absolute start */
+
+			if (!size || is_extended(p))
+				continue;
+			if (i >= 2) {
+				/* extra checks to detect real data on
+				 * 3rd and 4th entries */
+				if (start + size > cur_size)
+					continue;
+				if (abs_start < ex_start)
+					continue;
+				if (abs_start + size > ex_start + ex_size)
+					continue;
+			}
+
+			par = blkid_partlist_add_partition(ls, tab, abs_start, size);
+			if (!par)
+				goto err;
+
+			blkid_partition_set_type(par, p->sys_type);
+			blkid_partition_set_flags(par, p->boot_ind);
+			ct_nodata = 0;
+		}
+		/* The first nested ext.partition should be a link to the next
+		 * logical partition. Everything other (recursive ext.partitions)
+		 * is junk.
+		 */
+		for (p = p0, i = 0; i < 4; i++, p++) {
+			start = dos_partition_start(p) * ssf;
+			size = dos_partition_size(p) * ssf;
+
+			if (size && is_extended(p))
+				break;
+		}
+		if (i == 4)
+			goto leave;
+
+		cur_start = ex_start + start;
+		cur_size = size;
+	}
+leave:
+	return 0;
+err:
+	return -1;
+}
+
+static int probe_dos_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	int i;
+	int ssf;
+	blkid_parttable tab = NULL;
+	blkid_partlist ls;
+	struct dos_partition *p0, *p;
+	unsigned char *data;
+	uint32_t start, size;
+
+	data = blkid_probe_get_sector(pr, 0);
+	if (!data)
+		goto nothing;
+
+	/* ignore disks with AIX magic number -- for more details see aix.c */
+	if (memcmp(data, BLKID_AIX_MAGIC_STRING, BLKID_AIX_MAGIC_STRLEN) == 0)
+		goto nothing;
+
+	/*
+	 * Now that the 55aa signature is present, this is probably
+	 * either the boot sector of a FAT filesystem or a DOS-type
+	 * partition table.
+	 */
+	if (blkid_probe_is_vfat(pr)) {
+		DBG(DEBUG_LOWPROBE, printf("probably FAT -- ignore\n"));
+		goto nothing;
+	}
+
+	p0 = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET);
+
+	/*
+	 * Reject PT where boot indicator is not 0 or 0x80.
+	 */
+	for (p = p0, i = 0; i < 4; i++, p++)
+		if (p->boot_ind != 0 && p->boot_ind != 0x80) {
+			DBG(DEBUG_LOWPROBE, printf("missing boot indicator -- ignore\n"));
+			goto nothing;
+		}
+
+	/*
+	 * GPT uses valid MBR
+	 */
+	for (p = p0, i = 0; i < 4; i++, p++) {
+		if (p->sys_type == BLKID_GPT_PARTITION) {
+			DBG(DEBUG_LOWPROBE, printf("probably GPT -- ignore\n"));
+			goto nothing;
+		}
+	}
+
+	blkid_probe_use_wiper(pr, BLKID_MSDOS_PT_OFFSET,
+				  512 - BLKID_MSDOS_PT_OFFSET);
+
+	/*
+	 * Well, all checks pass, it's MS-DOS partiton table
+	 */
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+
+	/* sector size factor (the start and size are in the real sectors, but
+	 * we need to convert all sizes to 512 logical sectors
+	 */
+	ssf = blkid_probe_get_sectorsize(pr) / 512;
+
+	/* allocate a new partition table */
+	tab = blkid_partlist_new_parttable(ls, "dos", BLKID_MSDOS_PT_OFFSET);
+	if (!tab)
+		goto err;
+
+	/* Parse primary partitions */
+	for (p = p0, i = 0; i < 4; i++, p++) {
+		blkid_partition par;
+
+		start = dos_partition_start(p) * ssf;
+		size = dos_partition_size(p) * ssf;
+
+		if (!size) {
+			/* Linux kernel ignores empty partitions, but partno for
+			 * the empty primary partitions is not reused */
+			blkid_partlist_increment_partno(ls);
+			continue;
+		}
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_type(par, p->sys_type);
+		blkid_partition_set_flags(par, p->boot_ind);
+	}
+
+	/* Linux uses partition numbers greater than 4
+	 * for all logical partition and all nested partition tables (bsd, ..)
+	 */
+	blkid_partlist_set_partno(ls, 5);
+
+	/* Parse logical partitions */
+	for (p = p0, i = 0; i < 4; i++, p++) {
+		start = dos_partition_start(p) * ssf;
+		size = dos_partition_size(p) * ssf;
+
+		if (!size)
+			continue;
+		if (is_extended(p) &&
+		    parse_dos_extended(pr, tab, start, size, ssf) == -1)
+			goto err;
+	}
+
+	/* Parse subtypes (nested partitions) on large disks */
+	if (!blkid_probe_is_tiny(pr)) {
+		for (p = p0, i = 0; i < 4; i++, p++) {
+			size_t n;
+
+			if (!dos_partition_size(p) || is_extended(p))
+				continue;
+
+			for (n = 0; n < ARRAY_SIZE(dos_nested); n++) {
+				if (dos_nested[n].type != p->sys_type)
+					continue;
+
+				if (blkid_partitions_do_subprobe(pr,
+						blkid_partlist_get_partition(ls, i),
+						dos_nested[n].id) == -1)
+					goto err;
+				break;
+			}
+		}
+	}
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+
+const struct blkid_idinfo dos_pt_idinfo =
+{
+	.name		= "dos",
+	.probefunc	= probe_dos_pt,
+	.magics		=
+	{
+		/* DOS master boot sector:
+		 *
+		 *     0 | Code Area
+		 *   440 | Optional Disk signature
+		 *   446 | Partition table
+		 *   510 | 0x55
+		 *   511 | 0xAA
+		 */
+		{ .magic = "\x55\xAA", .len = 2, .sboff = 510 },
+		{ NULL }
+	}
+};
+
diff --git a/src/partitions/dos.h b/src/partitions/dos.h
new file mode 100644
index 0000000..130aa01
--- /dev/null
+++ b/src/partitions/dos.h
@@ -0,0 +1,36 @@
+#ifndef BLKID_PARTITIONS_DOS_H
+#define BLKID_PARTITIONS_DOS_H
+
+struct dos_partition {
+	unsigned char boot_ind;		/* 0x80 - active */
+	unsigned char bh, bs, bc;	/* begin CHS */
+	unsigned char sys_type;
+	unsigned char eh, es, ec;	/* end CHS */
+	unsigned char start_sect[4];
+	unsigned char nr_sects[4];
+} __attribute__((packed));
+
+#define BLKID_MSDOS_PT_OFFSET		0x1be
+
+/* assemble badly aligned little endian integer */
+static inline unsigned int assemble4le(unsigned char *p)
+{
+	return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+}
+
+static inline unsigned int dos_partition_start(struct dos_partition *p)
+{
+	return assemble4le(&(p->start_sect[0]));
+}
+
+static inline unsigned int dos_partition_size(struct dos_partition *p)
+{
+	return assemble4le(&(p->nr_sects[0]));
+}
+
+static inline int is_valid_mbr_signature(const unsigned char *mbr)
+{
+	return mbr[510] == 0x55 && mbr[511] == 0xaa ? 1 : 0;
+}
+
+#endif /* BLKID_PARTITIONS_DOS_H */
diff --git a/src/partitions/gpt.c b/src/partitions/gpt.c
new file mode 100644
index 0000000..98e82bf
--- /dev/null
+++ b/src/partitions/gpt.c
@@ -0,0 +1,408 @@
+/*
+ * EFI GPT partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * This code is not copy & past from any other implementation.
+ *
+ * For more information about GPT start your study at:
+ * http://en.wikipedia.org/wiki/GUID_Partition_Table
+ * http://technet.microsoft.com/en-us/library/cc739412(WS.10).aspx
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "partitions.h"
+#include "crc32.h"
+#include "dos.h"
+
+#define GPT_PRIMARY_LBA	1
+
+/* Signature - “EFI PART” */
+#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL
+#define GPT_HEADER_SIGNATURE_STR "EFI PART"
+
+/* basic types */
+typedef uint16_t efi_char16_t;
+
+/* UUID */
+typedef struct {
+	uint32_t time_low;
+	uint16_t time_mid;
+	uint16_t time_hi_and_version;
+	uint8_t clock_seq_hi;
+	uint8_t clock_seq_low;
+	uint8_t node[6];
+} efi_guid_t;
+
+
+#define GPT_UNUSED_ENTRY_GUID \
+	    ((efi_guid_t) { 0x00000000, 0x0000, 0x0000, 0x00, 0x00, \
+	                    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }})
+struct gpt_header {
+	uint64_t	signature;		/* "EFI PART" */
+	uint32_t	revision;
+	uint32_t	header_size;		/* usually 92 bytes */
+	uint32_t	header_crc32;		/* checksum of header with this
+						 * field zeroed during calculation */
+	uint32_t	reserved1;
+
+	uint64_t	my_lba;			/* location of this header copy */
+	uint64_t	alternate_lba;		/* location of the other header copy */
+	uint64_t	first_usable_lba;	/* lirst usable LBA for partitions */
+	uint64_t	last_usable_lba;	/* last usable LBA for partitions */
+
+	efi_guid_t	disk_guid;		/* disk UUID */
+
+	uint64_t	partition_entries_lba;	/* always 2 in primary header copy */
+	uint32_t	num_partition_entries;
+	uint32_t	sizeof_partition_entry;
+	uint32_t	partition_entry_array_crc32;
+
+	/*
+	 * The rest of the block is reserved by UEFI and must be zero. EFI
+	 * standard handles this by:
+	 *
+	 * uint8_t		reserved2[ BLKSSZGET - 92 ];
+	 *
+	 * This definition is useless in practice. It is necessary to read
+	 * whole block from the device rather than sizeof(struct gpt_header)
+	 * only.
+	 */
+} __attribute__ ((packed));
+
+/*** not used
+struct gpt_entry_attributes {
+	uint64_t	required_to_function:1;
+	uint64_t	reserved:47;
+        uint64_t	type_guid_specific:16;
+} __attribute__ ((packed));
+***/
+
+struct gpt_entry {
+	efi_guid_t	partition_type_guid;	/* type UUID */
+	efi_guid_t	unique_partition_guid;	/* partition UUID */
+	uint64_t	starting_lba;
+	uint64_t	ending_lba;
+
+	/*struct gpt_entry_attributes	attributes;*/
+
+	uint64_t	attributes;
+
+	efi_char16_t	partition_name[72 / sizeof(efi_char16_t)]; /* UTF-16LE string*/
+} __attribute__ ((packed));
+
+
+/*
+ * EFI uses crc32 with ~0 seed and xor's with ~0 at the end.
+ */
+static inline uint32_t count_crc32(const unsigned char *buf, size_t len)
+{
+	return (crc32(~0L, buf, len) ^ ~0L);
+}
+
+static inline unsigned char *get_lba_buffer(blkid_probe pr,
+					uint64_t lba, size_t bytes)
+{
+	return blkid_probe_get_buffer(pr,
+			blkid_probe_get_sectorsize(pr) * lba, bytes);
+}
+
+static inline int guidcmp(efi_guid_t left, efi_guid_t right)
+{
+	return memcmp(&left, &right, sizeof (efi_guid_t));
+}
+
+/*
+ * UUID is traditionally 16 byte big-endian array, except Intel EFI
+ * specification where the UUID is a structure of little-endian fields.
+ */
+static void swap_efi_guid(efi_guid_t *uid)
+{
+	uid->time_low = swab32(uid->time_low);
+	uid->time_mid = swab16(uid->time_mid);
+	uid->time_hi_and_version = swab16(uid->time_hi_and_version);
+}
+
+static int last_lba(blkid_probe pr, uint64_t *lba)
+{
+	blkid_loff_t sz = blkid_probe_get_size(pr);
+	unsigned int ssz = blkid_probe_get_sectorsize(pr);
+
+	if (sz < ssz)
+		return -1;
+
+	*lba = (sz / ssz) - 1ULL;
+	return 0;
+}
+
+/*
+ * Protective (legacy) MBR.
+ *
+ * This MBR contains standard DOS partition table with a single partition, type
+ * of 0xEE.  The partition usually encompassing the entire GPT drive - or 2TiB
+ * for large disks.
+ *
+ * Note that Apple uses GPT/MBR hybrid disks, where the DOS partition table is
+ * synchronized with GPT. This synchronization has many restriction of course
+ * (due DOS PT limitations).
+ *
+ * Note that the PMBR detection is optional (enabled by default) and could be
+ * disabled by BLKID_PARTS_FOPCE_GPT flag (see also blkid_paertitions_set_flags()).
+ */
+static int is_pmbr_valid(blkid_probe pr)
+{
+	int flags = blkid_partitions_get_flags(pr);
+	unsigned char *data;
+	struct dos_partition *p;
+	int i;
+
+	if (flags & BLKID_PARTS_FORCE_GPT)
+		goto ok;			/* skip PMBR check */
+
+	data = blkid_probe_get_sector(pr, 0);
+	if (!data)
+		goto failed;
+
+	if (!is_valid_mbr_signature(data))
+		goto failed;
+
+	p = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET);
+
+	for (i = 0; i < 4; i++, p++) {
+		if (p->sys_type == BLKID_GPT_PARTITION)
+			goto ok;
+	}
+failed:
+	return 0;
+ok:
+	return 1;
+}
+
+/*
+ * Reads GPT header to @hdr and returns a pointer to @hdr or NULL in case of
+ * error. The function also returns GPT entries in @ents.
+ *
+ * Note, this function does not allocate any memory. The GPT header has fixed
+ * size so we use stack, and @ents returns memory from libblkid buffer (so the
+ * next blkid_probe_get_buffer() will overwrite this buffer).
+ *
+ * This function checks validity of header and entries array. A corrupted
+ * header is not returned.
+ */
+static struct gpt_header *get_gpt_header(
+				blkid_probe pr, struct gpt_header *hdr,
+				struct gpt_entry **ents, uint64_t lba,
+				uint64_t lastlba)
+{
+	struct gpt_header *h;
+	uint32_t crc, orgcrc;
+	uint64_t lu, fu;
+	size_t esz;
+	uint32_t hsz, ssz;
+
+	ssz = blkid_probe_get_sectorsize(pr);
+
+	/* whole sector is allocated for GPT header */
+	h = (struct gpt_header *) get_lba_buffer(pr, lba, ssz);
+	if (!h)
+		return NULL;
+
+	if (le64_to_cpu(h->signature) != GPT_HEADER_SIGNATURE)
+		return NULL;
+
+	hsz = le32_to_cpu(h->header_size);
+
+	/* EFI: The HeaderSize must be greater than 92 and must be less
+	 *      than or equal to the logical block size.
+	 */
+	if (hsz > ssz || hsz < sizeof(*h))
+		return NULL;
+
+	/* Header has to be verified when header_crc32 is zero */
+	orgcrc = h->header_crc32;
+	h->header_crc32 = 0;
+	crc = count_crc32((unsigned char *) h, hsz);
+	h->header_crc32 = orgcrc;
+
+	if (crc != le32_to_cpu(orgcrc)) {
+		DBG(DEBUG_LOWPROBE, printf("GPT header corrupted\n"));
+		return NULL;
+	}
+
+	/* Valid header has to be at MyLBA */
+	if (le64_to_cpu(h->my_lba) != lba) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"GPT->MyLBA mismatch with real position\n"));
+		return NULL;
+	}
+
+	fu = le64_to_cpu(h->first_usable_lba);
+	lu = le64_to_cpu(h->last_usable_lba);
+
+	/* Check if First and Last usable LBA makes sense */
+	if (lu < fu || fu > lastlba || lu > lastlba) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"GPT->{First,Last}UsableLBA out of range\n"));
+		return NULL;
+	}
+
+	/* The header has to be outside usable range */
+	if (fu < lba && lba < lu) {
+		DBG(DEBUG_LOWPROBE, printf("GPT header is inside usable area\n"));
+		return NULL;
+	}
+
+	/* Size of blocks with GPT entries */
+	esz = le32_to_cpu(h->num_partition_entries) *
+			le32_to_cpu(h->sizeof_partition_entry);
+	if (!esz) {
+		DBG(DEBUG_LOWPROBE, printf("GPT entries undefined\n"));
+		return NULL;
+	}
+
+	/* The header seems valid, save it
+	 * (we don't care about zeros in hdr->reserved2 area) */
+	memcpy(hdr, h, sizeof(*h));
+	h = hdr;
+
+	/* Read GPT entries */
+	*ents = (struct gpt_entry *) get_lba_buffer(pr,
+				le64_to_cpu(h->partition_entries_lba), esz);
+	if (!*ents) {
+		DBG(DEBUG_LOWPROBE, printf("GPT entries unreadable\n"));
+		return NULL;
+	}
+
+	/* Validate entries */
+	crc = count_crc32((unsigned char *) *ents, esz);
+	if (crc != le32_to_cpu(h->partition_entry_array_crc32)) {
+		DBG(DEBUG_LOWPROBE, printf("GPT entries corrupted\n"));
+		return NULL;
+	}
+
+	return h;
+}
+
+static int probe_gpt_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t lastlba = 0, lba;
+	struct gpt_header hdr, *h;
+	struct gpt_entry *e;
+	blkid_parttable tab = NULL;
+	blkid_partlist ls;
+	uint64_t fu, lu;
+	uint32_t ssf, i;
+	efi_guid_t guid;
+
+	if (last_lba(pr, &lastlba))
+		goto nothing;
+
+	if (!is_pmbr_valid(pr))
+		goto nothing;
+
+	h = get_gpt_header(pr, &hdr, &e, (lba = GPT_PRIMARY_LBA), lastlba);
+	if (!h)
+		h = get_gpt_header(pr, &hdr, &e, (lba = lastlba), lastlba);
+
+	if (!h)
+		goto nothing;
+
+	blkid_probe_use_wiper(pr, lba * blkid_probe_get_size(pr), 8);
+
+	if (blkid_probe_set_magic(pr, lba << 9,
+			      sizeof(GPT_HEADER_SIGNATURE_STR) - 1,
+			      (unsigned char *) GPT_HEADER_SIGNATURE_STR))
+		goto err;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	tab = blkid_partlist_new_parttable(ls, "gpt", lba << 9);
+	if (!tab)
+		goto err;
+
+	ssf = blkid_probe_get_sectorsize(pr) / 512;
+
+	fu = le64_to_cpu(h->first_usable_lba);
+	lu = le64_to_cpu(h->last_usable_lba);
+
+	for (i = 0; i < le32_to_cpu(h->num_partition_entries); i++, e++) {
+
+		blkid_partition par;
+		uint64_t start = le64_to_cpu(e->starting_lba);
+		uint64_t size = le64_to_cpu(e->ending_lba) -
+					le64_to_cpu(e->starting_lba) + 1ULL;
+
+		/* 00000000-0000-0000-0000-000000000000 entry */
+		if (!guidcmp(e->partition_type_guid, GPT_UNUSED_ENTRY_GUID)) {
+			blkid_partlist_increment_partno(ls);
+			continue;
+		}
+		/* the partition has to inside usable range */
+		if (start < fu || start + size - 1 > lu) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"GPT entry[%d] overflows usable area - ignore\n",
+				i));
+			blkid_partlist_increment_partno(ls);
+			continue;
+		}
+
+		par = blkid_partlist_add_partition(ls, tab,
+					start * ssf, size * ssf);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_utf8name(par,
+			(unsigned char *) e->partition_name,
+			sizeof(e->partition_name), BLKID_ENC_UTF16LE);
+
+		guid = e->unique_partition_guid;
+		swap_efi_guid(&guid);
+		blkid_partition_set_uuid(par, (const unsigned char *) &guid);
+
+		guid = e->partition_type_guid;
+		swap_efi_guid(&guid);
+		blkid_partition_set_type_uuid(par, (const unsigned char *) &guid);
+
+		blkid_partition_set_flags(par, e->attributes);
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+
+const struct blkid_idinfo gpt_pt_idinfo =
+{
+	.name		= "gpt",
+	.probefunc	= probe_gpt_pt,
+	.minsz		= 1024 * 1440 + 1,	/* ignore floppies */
+
+	/*
+	 * It would be possible to check for DOS signature (0xAA55), but
+	 * unfortunately almost all EFI GPT implemenations allow to optionaly
+	 * skip the legacy MBR. We follows this behavior and MBR is optional.
+	 * See is_valid_pmbr().
+	 *
+	 * It means we have to always call probe_gpt_pt().
+	 */
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/src/partitions/mac.c b/src/partitions/mac.c
new file mode 100644
index 0000000..e18896c
--- /dev/null
+++ b/src/partitions/mac.c
@@ -0,0 +1,183 @@
+/*
+ * mac partitions parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+
+#define MAC_PARTITION_MAGIC		0x504d
+#define MAC_PARTITION_MAGIC_OLD		0x5453
+
+/*
+ * Mac partition entry
+ * http://developer.apple.com/legacy/mac/library/documentation/mac/Devices/Devices-126.html
+ */
+struct mac_partition {
+	uint16_t	signature;	/* expected to be MAC_PARTITION_MAGIC */
+	uint16_t	reserved;	/* reserved */
+	uint32_t	map_count;	/* # blocks in partition map */
+	uint32_t	start_block;	/* absolute starting block # of partition */
+	uint32_t	block_count;	/* number of blocks in partition */
+	char		name[32];	/* partition name */
+	char		type[32];	/* string type description */
+	uint32_t	data_start;	/* rel block # of first data block */
+	uint32_t	data_count;	/* number of data blocks */
+	uint32_t	status;		/* partition status bits */
+	uint32_t	boot_start;	/* first logical block of boot code */
+	uint32_t	boot_size;	/* size of boot code, in bytes */
+	uint32_t	boot_load;	/* boot code load address */
+	uint32_t	boot_load2;	/* reserved */
+	uint32_t	boot_entry;	/* boot code entry point */
+	uint32_t	boot_entry2;	/* reserved */
+	uint32_t	boot_cksum;	/* boot code checksum */
+	char		processor[16];	/* identifies ISA of boot */
+
+	/* there is more stuff after this that we don't need */
+} __attribute__((packed));
+
+/*
+ * Driver descriptor structure, in block 0
+ * http://developer.apple.com/legacy/mac/library/documentation/mac/Devices/Devices-121.html
+ */
+struct mac_driver_desc {
+	uint16_t	signature;	/* expected to be MAC_DRIVER_MAGIC */
+	uint16_t	block_size;	/* block size of the device */
+	uint32_t	block_count;	/* number of blocks on the device */
+
+	/* there is more stuff after this that we don't need */
+} __attribute__((packed));
+
+static inline unsigned char *get_mac_block(
+					blkid_probe pr,
+					uint16_t block_size,
+					uint32_t num)
+{
+	return blkid_probe_get_buffer(pr,
+			(blkid_loff_t) num * block_size, block_size);
+}
+
+static inline int has_part_signature(struct mac_partition *p)
+{
+	return	be16_to_cpu(p->signature) == MAC_PARTITION_MAGIC ||
+		be16_to_cpu(p->signature) == MAC_PARTITION_MAGIC_OLD;
+}
+
+static int probe_mac_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct mac_driver_desc *md;
+	struct mac_partition *p;
+	blkid_parttable tab = NULL;
+	blkid_partlist ls;
+	uint16_t block_size;
+	uint16_t ssf;	/* sector size fragment */
+	uint32_t nblks, i;
+
+
+	/* The driver descriptor record is always located at physical block 0,
+	 * the first block on the disk.
+	 */
+	md = (struct mac_driver_desc *) blkid_probe_get_sector(pr, 0);
+	if (!md)
+		goto nothing;
+
+	block_size = be16_to_cpu(md->block_size);
+
+	/* The partition map always begins at physical block 1,
+	 * the second block on the disk.
+	 */
+	p = (struct mac_partition *) get_mac_block(pr, block_size, 1);
+	if (!p)
+		goto nothing;
+
+	/* check the first partition signature */
+	if (!has_part_signature(p))
+		goto nothing;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	tab = blkid_partlist_new_parttable(ls, "mac", 0);
+	if (!tab)
+		goto err;
+
+	ssf = block_size / 512;
+	nblks = be32_to_cpu(p->map_count);
+
+	for (i = 1; i <= nblks; ++i) {
+		blkid_partition par;
+		uint32_t start;
+		uint32_t size;
+
+		p = (struct mac_partition *) get_mac_block(pr, block_size, i);
+		if (!p)
+			goto nothing;
+		if (!has_part_signature(p))
+			goto nothing;
+
+		if (be32_to_cpu(p->map_count) != nblks) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"mac: inconsisten map_count in partition map, "
+			        "entry[0]: %d, entry[%d]: %d\n",
+				nblks, i - 1,
+				be32_to_cpu(p->map_count)));
+		}
+
+		/*
+		 * note that libparted ignores some mac partitions according to
+		 * the partition name (e.g. "Apple_Free" or "Apple_Void"). We
+		 * follows Linux kernel and all partitions are visible
+		 */
+
+		start = be32_to_cpu(p->start_block) * ssf;
+		size = be32_to_cpu(p->block_count) * ssf;
+
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_name(par, (unsigned char *) p->name,
+						sizeof(p->name));
+
+		blkid_partition_set_type_string(par, (unsigned char *) p->type,
+						sizeof(p->type));
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+/*
+ * Mac disk always begin with "Driver Descriptor Record"
+ * (struct mac_driver_desc) and magic 0x4552.
+ */
+const struct blkid_idinfo mac_pt_idinfo =
+{
+	.name		= "mac",
+	.probefunc	= probe_mac_pt,
+	.magics		=
+	{
+		/* big-endian magic string */
+		{ .magic = "\x45\x52", .len = 2 },
+		{ NULL }
+	}
+};
+
diff --git a/src/partitions/minix.c b/src/partitions/minix.c
new file mode 100644
index 0000000..bd57a6d
--- /dev/null
+++ b/src/partitions/minix.c
@@ -0,0 +1,100 @@
+/*
+ * Minix partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+#include "dos.h"
+#include "minix.h"
+
+static int probe_minix_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct dos_partition *p;
+	blkid_parttable tab = NULL;
+	blkid_partition parent;
+	blkid_partlist ls;
+	unsigned char *data;
+	int i;
+
+	data = blkid_probe_get_sector(pr, 0);
+	if (!data)
+		goto nothing;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	/* Parent is required, because Minix uses the same PT as DOS and
+	 * difference is only in primary partition (parent) type.
+	 */
+	parent = blkid_partlist_get_parent(ls);
+	if (!parent)
+		goto nothing;
+
+	if (blkid_partition_get_type(parent) != BLKID_MINIX_PARTITION)
+		goto nothing;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	p = (struct dos_partition *) (data + BLKID_MSDOS_PT_OFFSET);
+
+	tab = blkid_partlist_new_parttable(ls, "minix", BLKID_MSDOS_PT_OFFSET);
+	if (!tab)
+		goto err;
+
+	for (i = 0; i < MINIX_MAXPARTITIONS; i++, p++) {
+		uint32_t start, size;
+		blkid_partition par;
+
+		if (p->sys_type != BLKID_MINIX_PARTITION)
+			continue;
+
+		start = dos_partition_start(p);
+		size = dos_partition_size(p);
+
+		if (parent && !blkid_is_nested_dimension(parent, start, size)) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"WARNING: minix partition (%d) overflow "
+				"detected, ignore\n", i));
+			continue;
+		}
+
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_type(par, p->sys_type);
+		blkid_partition_set_flags(par, p->boot_ind);
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+/* same as DOS */
+const struct blkid_idinfo minix_pt_idinfo =
+{
+	.name		= "minix",
+	.probefunc	= probe_minix_pt,
+	.magics		=
+	{
+		{ .magic = "\x55\xAA", .len = 2, .sboff = 510 },
+		{ NULL }
+	}
+};
+
diff --git a/src/partitions/partitions.c b/src/partitions/partitions.c
new file mode 100644
index 0000000..c9f7ff6
--- /dev/null
+++ b/src/partitions/partitions.c
@@ -0,0 +1,1355 @@
+/*
+ * partitions - partition tables parsing
+ *
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdarg.h>
+
+#include "partitions.h"
+#include "sysfs.h"
+
+/**
+ * SECTION: partitions
+ * @title: Partitions probing
+ * @short_description: partitions tables detection and parsing
+ *
+ * This chain supports binary and NAME=value interfaces, but complete PT
+ * description is provided by binary interface only. The libblkid prober is
+ * compatible with kernel partition tables parser. The parser does not return
+ * empty (size=0) partitions or special hidden partitions.
+ *
+ * NAME=value interface, supported tags:
+ *
+ * @PTTYPE: partition table type (dos, gpt, etc.).
+ *
+ * @PART_ENTRY_SCHEME: partition table type
+ *
+ * @PART_ENTRY_NAME: partition name (gpt and mac only)
+ *
+ * @PART_ENTRY_UUID: partition UUID (gpt only)
+ *
+ * @PART_ENTRY_TYPE: partition type, 0xNN (e.g 0x82) or type UUID (gpt only) or type string (mac)
+ *
+ * @PART_ENTRY_FLAGS: partition flags (e.g. boot_ind) or  attributes (e.g. gpt attributes)
+ *
+ * @PART_ENTRY_NUMBER: partition number
+ *
+ * @PART_ENTRY_OFFSET: the begin of the partition
+ *
+ * @PART_ENTRY_SIZE: size of the partition
+ *
+ * @PART_ENTRY_DISK: whole-disk maj:min
+ *
+ * Example:
+ *
+ * <informalexample>
+ *  <programlisting>
+ * blkid_probe pr;
+ * const char *ptname;
+ *
+ * pr = blkid_new_probe_from_filename(devname);
+ * if (!pr)
+ *	err("%s: faild to open device", devname);
+ *
+ * blkid_probe_enable_partitions(pr, TRUE);
+ * blkid_do_fullprobe(pr);
+ *
+ * blkid_probe_lookup_value(pr, "PTTYPE", &ptname, NULL);
+ * printf("%s partition type detected\n", pttype);
+ *
+ * blkid_free_probe(pr);
+ *
+ * // don't forget to check return codes in your code!
+ *  </programlisting>
+ * </informalexample>
+ *
+ * Binary interface:
+ *
+ * <informalexample>
+ *  <programlisting>
+ * blkid_probe pr;
+ * blkid_partlist ls;
+ * int nparts, i;
+ *
+ * pr = blkid_new_probe_from_filename(devname);
+ * if (!pr)
+ *	err("%s: faild to open device", devname);
+ *
+ * ls = blkid_probe_get_partitions(pr);
+ * nparts = blkid_partlist_numof_partitions(ls);
+ *
+ * for (i = 0; i < nparts; i++) {
+ *      blkid_partition par = blkid_partlist_get_partition(ls, i);
+ *      printf("#%d: %llu %llu  0x%x",
+ *               blkid_partition_get_partno(par),
+ *               blkid_partition_get_start(par),
+ *               blkid_partition_get_size(par),
+ *               blkid_partition_get_type(par));
+ * }
+ *
+ * blkid_free_probe(pr);
+ *
+ * // don't forget to check return codes in your code!
+ *  </programlisting>
+ * </informalexample>
+ */
+
+/*
+ * Chain driver function
+ */
+static int partitions_probe(blkid_probe pr, struct blkid_chain *chn);
+static void partitions_free_data(blkid_probe pr, void *data);
+
+/*
+ * Partitions chain probing functions
+ */
+static const struct blkid_idinfo *idinfos[] =
+{
+	&aix_pt_idinfo,
+	&sgi_pt_idinfo,
+	&sun_pt_idinfo,
+	&dos_pt_idinfo,
+	&gpt_pt_idinfo,
+	&mac_pt_idinfo,
+	&ultrix_pt_idinfo,
+	&bsd_pt_idinfo,
+	&unixware_pt_idinfo,
+	&solaris_x86_pt_idinfo,
+	&minix_pt_idinfo
+};
+
+/*
+ * Driver definition
+ */
+const struct blkid_chaindrv partitions_drv = {
+	.id           = BLKID_CHAIN_PARTS,
+	.name         = "partitions",
+	.dflt_enabled = FALSE,
+	.idinfos      = idinfos,
+	.nidinfos     = ARRAY_SIZE(idinfos),
+	.has_fltr     = TRUE,
+	.probe        = partitions_probe,
+	.safeprobe    = partitions_probe,
+	.free_data    = partitions_free_data
+};
+
+
+/*
+ * For compatibility with the rest of libblkid API (with the old high-level
+ * API) we use completely opaque typedefs for all structs. Don't forget that
+ * the final blkid_* types are pointers! See blkid.h.
+ *
+ * [Just for the record, I hate typedef for pointers --kzak]
+ */
+
+/* exported as opaque type "blkid_parttable" */
+struct blkid_struct_parttable {
+	const char	*type;		/* partition table type */
+	blkid_loff_t	offset;		/* begin of the partition table */
+	int		nparts;		/* number of partitions */
+	blkid_partition	parent;		/* parent of nested partition table */
+
+	struct list_head t_tabs;	/* all tables */
+};
+
+/* exported as opaque type "blkid_partition" */
+struct blkid_struct_partition {
+	blkid_loff_t	start;		/* begin of the partition */
+	blkid_loff_t	size;		/* size of the partitions */
+
+	int		type;		/* partition type */
+	char		typestr[37];	/* partition type string (GPT and Mac) */
+
+	unsigned long long flags;	/* partition flags / attributes */
+
+	int		partno;		/* partition number */
+	char		uuid[37];	/* UUID (when supported by PT), e.g GPT */
+	unsigned char	name[128];	/* Partition in UTF8 name (when supporte by PT), e.g. Mac */
+
+	blkid_parttable	tab;		/* partition table */
+};
+
+/* exported as opaque type "blkid_partlist" */
+struct blkid_struct_partlist {
+	int		next_partno;	/* next partition number */
+	blkid_partition next_parent;	/* next parent if parsing nested PT */
+
+	int		nparts;		/* number of partitions */
+	int		nparts_max;	/* max.number of partitions */
+	blkid_partition	parts;		/* array of partitions */
+
+	struct list_head l_tabs;	/* list of partition tables */
+};
+
+static int blkid_partitions_probe_partition(blkid_probe pr);
+
+/**
+ * blkid_probe_enable_partitions:
+ * @pr: probe
+ * @enable: TRUE/FALSE
+ *
+ * Enables/disables the partitions probing for non-binary interface.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_enable_partitions(blkid_probe pr, int enable)
+{
+	if (!pr)
+		return -1;
+	pr->chains[BLKID_CHAIN_PARTS].enabled = enable;
+	return 0;
+}
+
+/**
+ * blkid_probe_set_partitions_flags:
+ * @pr: prober
+ * @flags: BLKID_PARTS_* flags
+ *
+ * Sets probing flags to the partitions prober. This function is optional.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_set_partitions_flags(blkid_probe pr, int flags)
+{
+	if (!pr)
+		return -1;
+
+	pr->chains[BLKID_CHAIN_PARTS].flags = flags;
+	return 0;
+}
+
+/**
+ * blkid_probe_reset_partitions_filter:
+ * @pr: prober
+ *
+ * Resets partitions probing filter
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_reset_partitions_filter(blkid_probe pr)
+{
+	return __blkid_probe_reset_filter(pr, BLKID_CHAIN_PARTS);
+}
+
+/**
+ * blkid_probe_invert_partitions_filter:
+ * @pr: prober
+ *
+ * Inverts partitions probing filter
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_invert_partitions_filter(blkid_probe pr)
+{
+	return __blkid_probe_invert_filter(pr, BLKID_CHAIN_PARTS);
+}
+
+/**
+ * blkid_probe_filter_partitions_type:
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @names: NULL terminated array of probing function names (e.g. "vfat").
+ *
+ *  %BLKID_FLTR_NOTIN  - probe for all items which are NOT IN @names
+ *
+ *  %BLKID_FLTR_ONLYIN - probe for items which are IN @names
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_filter_partitions_type(blkid_probe pr, int flag, char *names[])
+{
+	return __blkid_probe_filter_types(pr, BLKID_CHAIN_PARTS, flag, names);
+}
+
+/**
+ * blkid_probe_get_partitions:
+ * @pr: probe
+ *
+ * This is a binary interface for partitions. See also blkid_partlist_*
+ * functions.
+ *
+ * This function is independent on blkid_do_[safe,full]probe() and
+ * blkid_probe_enable_partitions() calls.
+ *
+ * WARNING: the returned object will be overwritten by the next
+ *          blkid_probe_get_partitions() call for the same @pr. If you want to
+ *          use more blkid_partlist objects in the same time you have to create
+ *          more blkid_probe handlers (see blkid_new_probe()).
+ *
+ * Returns: list of partitions, or NULL in case of error.
+ */
+blkid_partlist blkid_probe_get_partitions(blkid_probe pr)
+{
+	return (blkid_partlist) blkid_probe_get_binary_data(pr,
+			&pr->chains[BLKID_CHAIN_PARTS]);
+}
+
+/* for internal usage only */
+blkid_partlist blkid_probe_get_partlist(blkid_probe pr)
+{
+	return (blkid_partlist) pr->chains[BLKID_CHAIN_PARTS].data;
+}
+
+static void blkid_probe_set_partlist(blkid_probe pr, blkid_partlist ls)
+{
+	pr->chains[BLKID_CHAIN_PARTS].data = ls;
+}
+
+static void ref_parttable(blkid_parttable tab)
+{
+	tab->nparts++;
+}
+
+static void unref_parttable(blkid_parttable tab)
+{
+	tab->nparts--;
+
+	if (tab->nparts <= 0) {
+		list_del(&tab->t_tabs);
+		free(tab);
+	}
+}
+
+/* free all allocated parttables */
+static void free_parttables(blkid_partlist ls)
+{
+	if (!ls || !ls->l_tabs.next)
+		return;
+
+	/* remove unassigned partition tables */
+	while (!list_empty(&ls->l_tabs)) {
+		blkid_parttable tab = list_entry(ls->l_tabs.next,
+					struct blkid_struct_parttable, t_tabs);
+		unref_parttable(tab);
+	}
+}
+
+static void reset_partlist(blkid_partlist ls)
+{
+	if (!ls)
+		return;
+
+	free_parttables(ls);
+
+	if (ls->next_partno) {
+		/* already initialized - reset */
+		int tmp_nparts = ls->nparts_max;
+		blkid_partition tmp_parts = ls->parts;
+
+		memset(ls, 0, sizeof(struct blkid_struct_partlist));
+
+		ls->nparts_max = tmp_nparts;
+		ls->parts = tmp_parts;
+	}
+
+	ls->nparts = 0;
+	ls->next_partno = 1;
+	INIT_LIST_HEAD(&ls->l_tabs);
+
+	DBG(DEBUG_LOWPROBE, printf("partlist reset\n"));
+}
+
+static blkid_partlist partitions_init_data(struct blkid_chain *chn)
+{
+	blkid_partlist ls;
+
+	if (chn->data)
+		ls = (blkid_partlist) chn->data;
+	else {
+		/* allocate the new list of partitions */
+		ls = calloc(1, sizeof(struct blkid_struct_partlist));
+		if (!ls)
+			return NULL;
+		chn->data = (void *) ls;
+	}
+
+	reset_partlist(ls);
+
+	DBG(DEBUG_LOWPROBE,
+		printf("parts: initialized partitions list (%p, size=%d)\n",
+		ls, ls->nparts_max));
+	return ls;
+}
+
+static void partitions_free_data(blkid_probe pr __attribute__((__unused__)),
+				 void *data)
+{
+	blkid_partlist ls = (blkid_partlist) data;
+
+	if (!ls)
+		return;
+
+	free_parttables(ls);
+
+	/* deallocate partitions and partlist */
+	free(ls->parts);
+	free(ls);
+}
+
+blkid_parttable blkid_partlist_new_parttable(blkid_partlist ls,
+				const char *type, blkid_loff_t offset)
+{
+	blkid_parttable tab;
+
+	tab = calloc(1, sizeof(struct blkid_struct_parttable));
+	if (!tab)
+		return NULL;
+	tab->type = type;
+	tab->offset = offset;
+	tab->parent = ls->next_parent;
+
+	INIT_LIST_HEAD(&tab->t_tabs);
+	list_add_tail(&tab->t_tabs, &ls->l_tabs);
+
+	DBG(DEBUG_LOWPROBE,
+		printf("parts: create a new partition table "
+		       "(%p, type=%s, offset=%"PRId64")\n", tab, type, offset));
+	return tab;
+}
+
+static blkid_partition new_partition(blkid_partlist ls, blkid_parttable tab)
+{
+	blkid_partition par;
+
+	if (ls->nparts + 1 > ls->nparts_max) {
+		/* Linux kernel has DISK_MAX_PARTS=256, but it's too much for
+		 * generic Linux machine -- let start with 32 partititions.
+		 */
+		ls->parts = realloc(ls->parts, (ls->nparts_max + 32) *
+					sizeof(struct blkid_struct_partition));
+		if (!ls->parts)
+			return NULL;
+		ls->nparts_max += 32;
+	}
+
+	par = &ls->parts[ls->nparts++];
+	memset(par, 0, sizeof(struct blkid_struct_partition));
+
+	ref_parttable(tab);
+	par->tab = tab;
+	par->partno = blkid_partlist_increment_partno(ls);
+
+	return par;
+}
+
+blkid_partition blkid_partlist_add_partition(blkid_partlist ls,
+					blkid_parttable tab,
+					blkid_loff_t start, blkid_loff_t size)
+{
+	blkid_partition par = new_partition(ls, tab);
+
+	if (!par)
+		return NULL;
+
+	par->start = start;
+	par->size = size;
+
+	DBG(DEBUG_LOWPROBE,
+		printf("parts: add partition (%p start=%"
+		PRId64 ", size=%" PRId64 ", table=%p)\n",
+		par, par->start, par->size, tab));
+	return par;
+}
+
+/* allows to modify used partitions numbers (for example for logical partitions) */
+int blkid_partlist_set_partno(blkid_partlist ls, int partno)
+{
+	if (!ls)
+		return -1;
+	ls->next_partno = partno;
+	return 0;
+}
+
+int blkid_partlist_increment_partno(blkid_partlist ls)
+{
+	return ls ? ls->next_partno++ : -1;
+}
+
+/* allows to set "parent" for the next nested partition */
+int blkid_partlist_set_parent(blkid_partlist ls, blkid_partition par)
+{
+	if (!ls)
+		return -1;
+	ls->next_parent = par;
+	return 0;
+}
+
+blkid_partition blkid_partlist_get_parent(blkid_partlist ls)
+{
+	if (!ls)
+		return NULL;
+	return ls->next_parent;
+}
+
+int blkid_partitions_need_typeonly(blkid_probe pr)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	return chn && chn->data && chn->binary ? FALSE : TRUE;
+}
+
+/* get private chain flags */
+int blkid_partitions_get_flags(blkid_probe pr)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	return chn ? chn->flags : 0;
+}
+
+/* check if @start and @size are within @par partition */
+int blkid_is_nested_dimension(blkid_partition par,
+			blkid_loff_t start, blkid_loff_t size)
+{
+	blkid_loff_t pstart;
+	blkid_loff_t psize;
+
+	if (!par)
+		return 0;
+
+	pstart = blkid_partition_get_start(par);
+	psize = blkid_partition_get_size(par);
+
+	if (start < pstart || start + size > pstart + psize)
+		return 0;
+
+	return 1;
+}
+
+static int idinfo_probe(blkid_probe pr, const struct blkid_idinfo *id,
+			struct blkid_chain *chn)
+{
+	const struct blkid_idmag *mag = NULL;
+	blkid_loff_t off;
+	int rc = 1;		/* = nothing detected */
+
+	if (pr->size <= 0 || (id->minsz && id->minsz > pr->size))
+		goto nothing;	/* the device is too small */
+
+	if (blkid_probe_get_idmag(pr, id, &off, &mag))
+		goto nothing;
+
+	/* final check by probing function */
+	if (id->probefunc) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"%s: ---> call probefunc()\n", id->name));
+		rc = id->probefunc(pr, mag);
+	        if (rc == -1) {
+			/* reset after error */
+			reset_partlist(blkid_probe_get_partlist(pr));
+			if (chn && !chn->binary)
+				blkid_probe_chain_reset_vals(pr, chn);
+			DBG(DEBUG_LOWPROBE, printf(
+				"%s probefunc failed\n", id->name));
+		}
+		if (rc == 0 && mag && chn && !chn->binary)
+			rc = blkid_probe_set_magic(pr, off, mag->len,
+					(unsigned char *) mag->magic);
+
+		DBG(DEBUG_LOWPROBE, printf(
+			"%s: <--- (rc = %d)\n", id->name, rc));
+	}
+
+nothing:
+	return rc;
+}
+
+/*
+ * The blkid_do_probe() backend.
+ */
+static int partitions_probe(blkid_probe pr, struct blkid_chain *chn)
+{
+	int rc = 1;
+	size_t i;
+
+	if (!pr || chn->idx < -1)
+		return -1;
+	blkid_probe_chain_reset_vals(pr, chn);
+
+	if (chn->binary)
+		partitions_init_data(chn);
+
+	if (!pr->wipe_size && (pr->prob_flags & BLKID_PROBE_FL_IGNORE_PT))
+		goto details_only;
+
+	DBG(DEBUG_LOWPROBE,
+		printf("--> starting probing loop [PARTS idx=%d]\n",
+		chn->idx));
+
+	i = chn->idx < 0 ? 0 : chn->idx + 1U;
+
+	for ( ; i < ARRAY_SIZE(idinfos); i++) {
+		const char *name;
+
+		chn->idx = i;
+
+		/* apply filter */
+		if (chn->fltr && blkid_bmp_get_item(chn->fltr, i))
+			continue;
+
+		/* apply checks from idinfo */
+		if (idinfo_probe(pr, idinfos[i], chn) != 0)
+			continue;
+
+		name = idinfos[i]->name;
+
+		/* all checks passed */
+		if (!chn->binary)
+			blkid_probe_set_value(pr, "PTTYPE",
+						(unsigned char *) name,
+						strlen(name) + 1);
+		DBG(DEBUG_LOWPROBE,
+			printf("<-- leaving probing loop (type=%s) [PARTS idx=%d]\n",
+			name, chn->idx));
+		rc = 0;
+		break;
+	}
+
+	if (rc == 1) {
+		DBG(DEBUG_LOWPROBE,
+			printf("<-- leaving probing loop (failed) [PARTS idx=%d]\n",
+			chn->idx));
+	}
+
+details_only:
+	/*
+	 * Gather PART_ENTRY_* values if the current device is a partition.
+	 */
+	if (!chn->binary &&
+	    (blkid_partitions_get_flags(pr) & BLKID_PARTS_ENTRY_DETAILS)) {
+
+		if (!blkid_partitions_probe_partition(pr))
+			rc = 0;
+	}
+
+	return rc;
+}
+
+/* Probe for nested partition table within the parental partition */
+int blkid_partitions_do_subprobe(blkid_probe pr, blkid_partition parent,
+		const struct blkid_idinfo *id)
+{
+	blkid_probe prc;
+	int rc = 1;
+	blkid_partlist ls;
+	blkid_loff_t sz, off;
+
+	DBG(DEBUG_LOWPROBE, printf(
+		"parts: ----> %s subprobe requested (parent=%p)\n",
+		id->name, parent));
+
+	if (!pr || !parent || !parent->size)
+		return -1;
+
+	/* range defined by parent */
+	sz = ((blkid_loff_t) parent->size) << 9;
+	off = ((blkid_loff_t) parent->start) << 9;
+
+	if (off < pr->off || pr->off + pr->size < off + sz) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"ERROR: parts: <---- '%s' subprobe: overflow detected.\n",
+			id->name));
+		return -1;
+	}
+
+	/* create private prober */
+	prc = blkid_clone_probe(pr);
+	if (!prc)
+		return -1;
+
+	blkid_probe_set_dimension(prc, off, sz);
+
+	/* clone is always with reset chain, fix it */
+	prc->cur_chain = blkid_probe_get_chain(pr);
+
+	/*
+	 * Set 'parent' to the current list of the partitions and use the list
+	 * in cloned prober (so the cloned prober will extend the current list
+	 * of partitions rather than create a new).
+	 */
+	ls = blkid_probe_get_partlist(pr);
+	blkid_partlist_set_parent(ls, parent);
+
+	blkid_probe_set_partlist(prc, ls);
+
+	rc = idinfo_probe(prc, id, blkid_probe_get_chain(pr));
+
+	blkid_probe_set_partlist(prc, NULL);
+	blkid_partlist_set_parent(ls, NULL);
+
+	blkid_free_probe(prc);	/* free cloned prober */
+
+	DBG(DEBUG_LOWPROBE, printf(
+		"parts: <---- %s subprobe done (parent=%p, rc=%d)\n",
+		id->name, parent, rc));
+
+	return rc;
+}
+
+static int blkid_partitions_probe_partition(blkid_probe pr)
+{
+	int rc = 1;
+	blkid_probe disk_pr = NULL;
+	blkid_partlist ls;
+	blkid_partition par;
+	dev_t devno;
+
+	devno = blkid_probe_get_devno(pr);
+	if (!devno)
+		goto nothing;
+
+	disk_pr = blkid_probe_get_wholedisk_probe(pr);
+	if (!disk_pr)
+		goto nothing;
+
+	/* parse PT */
+	ls = blkid_probe_get_partitions(disk_pr);
+	if (!ls)
+		goto nothing;
+
+	par = blkid_partlist_devno_to_partition(ls, devno);
+	if (par) {
+		const char *v;
+		blkid_parttable tab = blkid_partition_get_table(par);
+		dev_t disk = blkid_probe_get_devno(disk_pr);
+
+		if (tab) {
+			v = blkid_parttable_get_type(tab);
+			if (v)
+				blkid_probe_set_value(pr, "PART_ENTRY_SCHEME",
+					(unsigned char *) v, strlen(v) + 1);
+		}
+
+		v = blkid_partition_get_name(par);
+		if (v)
+			blkid_probe_set_value(pr, "PART_ENTRY_NAME",
+				(unsigned char *) v, strlen(v) + 1);
+
+		v = blkid_partition_get_uuid(par);
+		if (v)
+			blkid_probe_set_value(pr, "PART_ENTRY_UUID",
+				(unsigned char *) v, strlen(v) + 1);
+
+		/* type */
+		v = blkid_partition_get_type_string(par);
+		if (v)
+			blkid_probe_set_value(pr, "PART_ENTRY_TYPE",
+				(unsigned char *) v, strlen(v) + 1);
+		else
+			blkid_probe_sprintf_value(pr, "PART_ENTRY_TYPE",
+				"0x%x", blkid_partition_get_type(par));
+
+		if (blkid_partition_get_flags(par))
+			blkid_probe_sprintf_value(pr, "PART_ENTRY_FLAGS",
+				"0x%llx", blkid_partition_get_flags(par));
+
+		blkid_probe_sprintf_value(pr, "PART_ENTRY_NUMBER",
+				"%d", blkid_partition_get_partno(par));
+
+		blkid_probe_sprintf_value(pr, "PART_ENTRY_OFFSET", "%jd",
+				blkid_partition_get_start(par));
+		blkid_probe_sprintf_value(pr, "PART_ENTRY_SIZE", "%jd",
+				blkid_partition_get_size(par));
+
+		blkid_probe_sprintf_value(pr, "PART_ENTRY_DISK", "%u:%u",
+				major(disk), minor(disk));
+	}
+	rc = 0;
+nothing:
+	return rc;
+}
+
+/*
+ * 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_probe prc;
+	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));
+
+	prc = blkid_clone_probe(pr);
+	if (!prc)
+		goto done;
+
+	ls = blkid_probe_get_partitions(prc);
+	if (!ls)
+		goto done;
+
+	nparts = blkid_partlist_numof_partitions(ls);
+	if (!nparts)
+		goto done;
+
+	end = (offset + size) >> 9;
+	start = offset >> 9;
+
+	/* check if the partition table fits into the device */
+	for (i = 0; i < nparts; i++) {
+		blkid_partition par = &ls->parts[i];
+
+		if (par->start + par->size > (pr->size >> 9)) {
+			DBG(DEBUG_LOWPROBE, printf("partition #%d overflows "
+				"device (off=%" PRId64 " size=%" PRId64 ")\n",
+				par->partno, par->start, par->size));
+			goto done;
+		}
+	}
+
+	/* check if the requested area is covered by PT */
+	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:
+	blkid_free_probe(prc);
+
+	DBG(DEBUG_LOWPROBE, printf("<= %s covered by PT\n", rc ? "IS" : "NOT"));
+	return rc;
+}
+
+/**
+ * blkid_known_pttype:
+ * @pttype: partiton name
+ *
+ * Returns: 1 for known or 0 for unknown partition type.
+ */
+int blkid_known_pttype(const char *pttype)
+{
+	size_t i;
+
+	if (!pttype)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(idinfos); i++) {
+		const struct blkid_idinfo *id = idinfos[i];
+		if (strcmp(id->name, pttype) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+/**
+ * blkid_partlist_numof_partitions:
+ * @ls: partitions list
+ *
+ * Returns: number of partitions in the list or -1 in case of error.
+ */
+int blkid_partlist_numof_partitions(blkid_partlist ls)
+{
+	return ls ? ls->nparts : -1;
+}
+
+/**
+ * blkid_partlist_get_table:
+ * @ls: partitions list
+ *
+ * Returns: top-level partition table or NULL of there is not a partition table
+ * on the device.
+ */
+blkid_parttable blkid_partlist_get_table(blkid_partlist ls)
+{
+	if (!ls || list_empty(&ls->l_tabs))
+		return NULL;
+
+	return list_entry(ls->l_tabs.next,
+			struct blkid_struct_parttable, t_tabs);
+}
+
+
+/**
+ * blkid_partlist_get_partition:
+ * @ls: partitions list
+ * @n: partition number in range 0..N, where 'N' is blkid_partlist_numof_partitions().
+ *
+ * It's possible that the list of partitions is *empty*, but there is a valid
+ * partition table on the disk. This happen when on-disk details about
+ * partitions are unknown or the partition table is empty.
+ *
+ * See also blkid_partlist_get_table().
+ *
+ * Returns: partition object or NULL in case or error.
+ */
+blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n)
+{
+	if (!ls || n < 0 || n >= ls->nparts)
+		return NULL;
+
+	return &ls->parts[n];
+}
+
+/**
+ * blkid_partlist_devno_to_partition:
+ * @ls: partitions list
+ * @devno: requested partition
+ *
+ * This function tries to get start and size for @devno from sysfs and
+ * returns a partition from @ls which matches with the values from sysfs.
+ *
+ * This function is necessary when you want to make a relation between an entry
+ * in the partition table (@ls) and block devices in your system.
+ *
+ * Returns: partition object or NULL in case or error.
+ */
+blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno)
+{
+	struct sysfs_cxt sysfs;
+	uint64_t start, size;
+	int i, rc, partno = 0;
+
+	DBG(DEBUG_LOWPROBE,
+		printf("triyng to convert devno 0x%llx to partition\n",
+			(long long) devno));
+
+	if (sysfs_init(&sysfs, devno, NULL)) {
+		DBG(DEBUG_LOWPROBE, printf("failed t init sysfs context\n"));
+		return NULL;
+	}
+	rc = sysfs_read_u64(&sysfs, "size", &size);
+	if (!rc) {
+		rc = sysfs_read_u64(&sysfs, "start", &start);
+		if (rc) {
+			/* try to get partition number from DM uuid.
+			 */
+			char *uuid = sysfs_strdup(&sysfs, "dm/uuid");
+			char *tmp = uuid;
+			char *prefix = uuid ? strsep(&tmp, "-") : NULL;
+
+			if (prefix && strncasecmp(prefix, "part", 4) == 0) {
+				char *end = NULL;
+
+				partno = strtol(prefix + 4, &end, 10);
+				if (prefix == end || (end && *end))
+					partno = 0;
+				else
+					rc = 0;		/* success */
+			}
+			free(uuid);
+		}
+	}
+
+	sysfs_deinit(&sysfs);
+
+	if (rc)
+		return NULL;
+
+	if (partno) {
+		DBG(DEBUG_LOWPROBE, printf("mapped by DM, using partno %d\n", partno));
+
+		/*
+		 * Partition mapped by kpartx does not provide "start" offset
+		 * in /sys, but if we know partno and size of the partition
+		 * that we can probably make the releation bettween the device
+		 * and an entry in partition table.
+		 */
+		 for (i = 0; i < ls->nparts; i++) {
+			 blkid_partition par = &ls->parts[i];
+
+			 if (partno != blkid_partition_get_partno(par))
+				 continue;
+
+			 if ((blkid_loff_t) size == blkid_partition_get_size(par) ||
+			     (blkid_partition_is_extended(par) && size <= 1024))
+				 return par;
+
+		 }
+		 return NULL;
+	}
+
+	DBG(DEBUG_LOWPROBE, printf("searching by offset/size\n"));
+
+	for (i = 0; i < ls->nparts; i++) {
+		blkid_partition par = &ls->parts[i];
+
+		if (blkid_partition_get_start(par) == (blkid_loff_t) start &&
+		    blkid_partition_get_size(par) == (blkid_loff_t) size)
+			return par;
+
+		/* exception for extended dos partitions */
+		if (blkid_partition_get_start(par) == (blkid_loff_t) start &&
+		    blkid_partition_is_extended(par) && size <= 1024)
+			return par;
+
+	}
+
+	DBG(DEBUG_LOWPROBE, printf("not found partition for device\n"));
+	return NULL;
+}
+
+int blkid_partition_set_type(blkid_partition par, int type)
+{
+	if (!par)
+		return -1;
+	par->type = type;
+	return 0;
+}
+
+/**
+ * blkid_parttable_get_type:
+ * @tab: partition table
+ *
+ * Returns: partition table type (type name, e.g. "dos", "gpt", ...)
+ */
+const char *blkid_parttable_get_type(blkid_parttable tab)
+{
+	return tab ? tab->type : NULL;
+}
+
+/**
+ * blkid_parttable_get_parent:
+ * @tab: partition table
+ *
+ * Returns: parent for nexted partitition tables or NULL.
+ */
+blkid_partition blkid_parttable_get_parent(blkid_parttable tab)
+{
+	return tab ? tab->parent : NULL;
+}
+
+/**
+ * blkid_parttable_get_offset:
+ * @tab: partition table
+ *
+ * Note the position is relative to begin of the device as defined by
+ * blkid_probe_set_device() for primary partition table, and relative
+ * to parental partition for nested patition tables.
+ *
+ * <informalexample>
+ *   <programlisting>
+ * off_t offset;
+ * blkid_partition parent = blkid_parttable_get_parent(tab);
+ *
+ * offset = blkid_parttable_get_offset(tab);
+ *
+ * if (parent)
+ *      / * 'tab' is nested partition table * /
+ *	offset += blkid_partition_get_start(parent);
+ *   </programlisting>
+ * </informalexample>
+
+ * Returns: position (in bytes) of the partition table or -1 in case of error.
+ *
+ */
+blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab)
+{
+	return tab ? tab->offset : -1;
+}
+
+/**
+ * blkid_partition_get_table:
+ * @par: partition
+ *
+ * The "parttable" describes partition table. The table is usually the same for
+ * all partitions -- except nested partition tables.
+ *
+ * For example bsd, solaris, etc. use a nested partition table within
+ * standard primary dos partition:
+ *
+ * <informalexample>
+ *   <programlisting>
+ *
+ *  -- dos partition table
+ *  0: sda1     dos primary partition
+ *  1: sda2     dos primary partition
+ *     -- bsd partition table (with in sda2)
+ *  2:    sda5  bds partition
+ *  3:    sda6  bds partition
+ *
+ *   </programlisting>
+ * </informalexample>
+ *
+ * The library does not to use a separate partition table object for dos logical
+ * partitions (partitions within extended partition). It's possible to
+ * differentiate between logical, extended and primary partitions by
+ *
+ *	blkid_partition_is_{extended,primary,logical}().
+ *
+ * Returns: partition table object or NULL in case of error.
+ */
+blkid_parttable blkid_partition_get_table(blkid_partition par)
+{
+	return par ? par->tab : NULL;
+}
+
+static int partition_get_logical_type(blkid_partition par)
+{
+	blkid_parttable tab;
+
+	if (!par)
+		return -1;
+
+	tab = blkid_partition_get_table(par);
+	if (!tab || !tab->type)
+		return -1;
+
+	if (tab->parent)
+		return 'L';  /* report nested partitions as logical */
+
+	if (!strcmp(tab->type, "dos")) {
+		if (par->partno > 4)
+			return 'L';	/* logical */
+
+	        if(par->type == BLKID_DOS_EXTENDED_PARTITION ||
+                   par->type == BLKID_W95_EXTENDED_PARTITION ||
+		   par->type == BLKID_LINUX_EXTENDED_PARTITION)
+			return 'E';
+	}
+	return 'P';
+}
+
+/**
+ * blkid_partition_is_primary:
+ * @par: partition
+ *
+ * Note, this function returns FALSE for DOS extended partitions and
+ * all partitions in nested partition tables.
+ *
+ * Returns: 1 if the partitions is primary partition or 0 if not.
+ */
+int blkid_partition_is_primary(blkid_partition par)
+{
+	return partition_get_logical_type(par) == 'P' ? TRUE : FALSE;
+}
+
+/**
+ * blkid_partition_is_extended:
+ * @par: partition
+ *
+ * Returns: 1 if the partitions is extended (dos, windows or linux)
+ * partition or 0 if not.
+ */
+int blkid_partition_is_extended(blkid_partition par)
+{
+	return partition_get_logical_type(par) == 'E' ? TRUE : FALSE;
+}
+
+/**
+ * blkid_partition_is_logical:
+ * @par: partition
+ *
+ * Note that this function returns TRUE for all partitions in all
+ * nested partition tables (e.g. BSD labels).
+ *
+ * Returns: 1 if the partitions is logical partition or 0 if not.
+ */
+int blkid_partition_is_logical(blkid_partition par)
+{
+	return partition_get_logical_type(par) == 'L' ? TRUE : FALSE;
+}
+
+static void set_string(unsigned char *item, size_t max,
+				const unsigned char *data, size_t len)
+{
+	if (len >= max)
+		len = max - 1;
+
+	memcpy(item, data, len);
+	item[len] = '\0';
+
+	blkid_rtrim_whitespace(item);
+}
+
+int blkid_partition_set_name(blkid_partition par,
+		const unsigned char *name, size_t len)
+{
+	if (!par)
+		return -1;
+
+	set_string(par->name, sizeof(par->name), name, len);
+	return 0;
+}
+
+int blkid_partition_set_utf8name(blkid_partition par, const unsigned char *name,
+		size_t len, int enc)
+{
+	if (!par)
+		return -1;
+
+	blkid_encode_to_utf8(enc, par->name, sizeof(par->name), name, len);
+	blkid_rtrim_whitespace(par->name);
+	return 0;
+}
+
+int blkid_partition_set_uuid(blkid_partition par, const unsigned char *uuid)
+{
+	if (!par)
+		return -1;
+
+	blkid_unparse_uuid(uuid, par->uuid, sizeof(par->uuid));
+	return 0;
+}
+
+/**
+ * blkid_partition_get_name:
+ * @par: partition
+ *
+ * Returns: partition name string if supported by PT (e.g. Mac) or NULL.
+ */
+const char *blkid_partition_get_name(blkid_partition par)
+{
+	return par && *par->name ? (char *) par->name : NULL;
+}
+
+/**
+ * blkid_partition_get_uuid:
+ * @par: partition
+ *
+ * Returns: partition UUID string if supported by PT (e.g. GPT) or NULL.
+ */
+const char *blkid_partition_get_uuid(blkid_partition par)
+{
+	return par && *par->uuid ? par->uuid : NULL;
+}
+
+/**
+ * blkid_partition_get_partno:
+ * @par: partition
+ *
+ * Returns: proposed partitin number (e.g. 'N' from sda'N') or -1 in case of
+ * error. Note that the number is generate by library independenly on your OS.
+ */
+int blkid_partition_get_partno(blkid_partition par)
+{
+	return par ? par->partno : -1;
+}
+
+/**
+ * blkid_partition_get_start:
+ * @par: partition
+ *
+ * Be careful if you _not_ probe whole disk:
+ *
+ * 1) the offset is usully relative to begin of the disk -- but if you probe a
+ *    fragment of the disk only -- then the offset could be still relative to
+ *    the begin of the disk rather that relative to the fragment.
+ *
+ * 2) the offset for nested partitions could be releative to parent (e.g. Solaris)
+ *    _or_ relative to the begin of the whole disk (e.g. bsd).
+ *
+ * You don't have to care about such details if you proble whole disk. In such
+ * a case libblkid always returns the offset relative to the begin of the disk.
+ *
+ * Returns: start of the partition (in 512-sectors).
+ */
+blkid_loff_t blkid_partition_get_start(blkid_partition par)
+{
+	return par ? par->start : -1;
+}
+
+/**
+ * blkid_partition_get_size:
+ * @par: partition
+ *
+ * WARNING: be very careful when you work with MS-DOS extended partitions. The
+ *          library always returns full size of the partition. If you want add
+ *          the partition to the Linux system (BLKPG_ADD_PARTITION ioctl) you
+ *          need to reduce the size of the partition to 1 or 2 blocks. The
+ *          rest of the partition has to be unaccessible for mkfs or mkswap
+ *          programs, we need a small space for boot loaders only.
+ *
+ *          For some unknown reason this (safe) practice is not to used for
+ *          nested BSD, Solaris, ..., partition tables in Linux kernel.
+ *
+ * Returns: size of the partition (in 512-sectors).
+ */
+blkid_loff_t blkid_partition_get_size(blkid_partition par)
+{
+	return par ? par->size : -1;
+}
+
+/**
+ * blkid_partition_get_type:
+ * @par: partition
+ *
+ * Returns: partition type.
+ */
+int blkid_partition_get_type(blkid_partition par)
+{
+	return par ? par->type : 0;
+}
+
+/* Sets partition 'type' for PT where the type is defined by string rather
+ * than by number
+ */
+int blkid_partition_set_type_string(blkid_partition par,
+		const unsigned char *type, size_t len)
+{
+	if (!par)
+		return -1;
+
+	set_string((unsigned char *) par->typestr,
+			sizeof(par->typestr), type, len);
+	return 0;
+}
+
+/* Sets partition 'type' for PT where the type is defined by UUIDrather
+ * than by number
+ */
+int blkid_partition_set_type_uuid(blkid_partition par, const unsigned char *uuid)
+{
+	if (!par)
+		return -1;
+
+	blkid_unparse_uuid(uuid, par->typestr, sizeof(par->typestr));
+	return 0;
+}
+
+/**
+ * blkid_partition_get_type_string:
+ * @par: partition
+ *
+ * The type string is supported by a small subset of partition tables (e.g Mac
+ * and EFI GPT).  Note that GPT uses type UUID and this function returns this
+ * UUID as string.
+ *
+ * Returns: partition type string or NULL.
+ */
+const char *blkid_partition_get_type_string(blkid_partition par)
+{
+	return par && *par->typestr ? par->typestr : NULL;
+}
+
+
+int blkid_partition_set_flags(blkid_partition par, unsigned long long flags)
+{
+	if (!par)
+		return -1;
+	par->flags = flags;
+	return 0;
+}
+
+/**
+ * blkid_partition_get_flags
+ * @par: partition
+ *
+ * Returns: partition flags (or attributes for gpt).
+ */
+unsigned long long blkid_partition_get_flags(blkid_partition par)
+{
+	return par ? par->flags : 0;
+}
+
diff --git a/src/partitions/partitions.h b/src/partitions/partitions.h
new file mode 100644
index 0000000..c4ccd3b
--- /dev/null
+++ b/src/partitions/partitions.h
@@ -0,0 +1,62 @@
+#ifndef BLKID_PARTITIONS_H
+#define BLKID_PARTITIONS_H
+
+#include "blkidP.h"
+#include "blkid_parttypes.h"
+
+extern int blkid_partitions_get_flags(blkid_probe pr);
+
+extern blkid_parttable blkid_partlist_new_parttable(blkid_partlist ls,
+				const char *type, blkid_loff_t offset);
+
+extern blkid_partition blkid_partlist_add_partition(blkid_partlist ls,
+				blkid_parttable tab,
+				blkid_loff_t start, blkid_loff_t size);
+
+extern int blkid_partlist_set_partno(blkid_partlist ls, int partno);
+extern int blkid_partlist_increment_partno(blkid_partlist ls);
+
+extern blkid_partition blkid_partlist_get_parent(blkid_partlist ls);
+
+extern int blkid_partitions_do_subprobe(blkid_probe pr,
+			blkid_partition parent, const struct blkid_idinfo *id);
+
+extern int blkid_partitions_need_typeonly(blkid_probe pr);
+extern int blkid_is_nested_dimension(blkid_partition par,
+                        blkid_loff_t start, blkid_loff_t size);
+
+extern int blkid_partition_set_name(blkid_partition par,
+		const unsigned char *name, size_t len);
+
+extern int blkid_partition_set_utf8name(blkid_partition par,
+		const unsigned char *name, size_t len, int enc);
+
+extern int blkid_partition_set_uuid(blkid_partition par,
+		const unsigned char *uuid);
+
+extern int blkid_partition_set_type(blkid_partition par, int type);
+
+extern int blkid_partition_set_type_string(blkid_partition par,
+                const unsigned char *type, size_t len);
+
+extern int blkid_partition_set_type_uuid(blkid_partition par,
+		const unsigned char *uuid);
+
+extern int blkid_partition_set_flags(blkid_partition par, unsigned long long flags);
+
+/*
+ * partition probers
+ */
+extern const struct blkid_idinfo aix_pt_idinfo;
+extern const struct blkid_idinfo bsd_pt_idinfo;
+extern const struct blkid_idinfo unixware_pt_idinfo;
+extern const struct blkid_idinfo solaris_x86_pt_idinfo;
+extern const struct blkid_idinfo sun_pt_idinfo;
+extern const struct blkid_idinfo sgi_pt_idinfo;
+extern const struct blkid_idinfo mac_pt_idinfo;
+extern const struct blkid_idinfo dos_pt_idinfo;
+extern const struct blkid_idinfo minix_pt_idinfo;
+extern const struct blkid_idinfo gpt_pt_idinfo;
+extern const struct blkid_idinfo ultrix_pt_idinfo;
+
+#endif /* BLKID_PARTITIONS_H */
diff --git a/src/partitions/sgi.c b/src/partitions/sgi.c
new file mode 100644
index 0000000..b89e463
--- /dev/null
+++ b/src/partitions/sgi.c
@@ -0,0 +1,159 @@
+/*
+ * sgi partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+
+#define SGI_MAXPARTITIONS	16
+
+/* partition type */
+#define SGI_TYPE_VOLHDR		0x00
+#define SGI_TYPE_VOLULME	0x06	/* entire disk */
+
+struct sgi_device_parameter {
+	unsigned char skew;
+	unsigned char gap1;
+	unsigned char gap2;
+	unsigned char sparecyl;
+
+	uint16_t pcylcount;
+	uint16_t head_vol0;
+	uint16_t ntrks;		/* tracks in cyl 0 or vol 0 */
+
+	unsigned char cmd_tag_queue_depth;
+	unsigned char unused0;
+
+	uint16_t unused1;
+	uint16_t nsect;		/* sectors/tracks in cyl 0 or vol 0 */
+	uint16_t bytes;
+	uint16_t ilfact;
+	uint32_t flags;		/* controller flags */
+	uint32_t datarate;
+	uint32_t retries_on_error;
+	uint32_t ms_per_word;
+	uint16_t xylogics_gap1;
+	uint16_t xylogics_syncdelay;
+	uint16_t xylogics_readdelay;
+	uint16_t xylogics_gap2;
+	uint16_t xylogics_readgate;
+	uint16_t xylogics_writecont;
+} __attribute__((packed));
+
+struct sgi_disklabel {
+	uint32_t magic;			/* magic number */
+	uint16_t root_part_num;		/* # root partition */
+	uint16_t swap_part_num;		/* # swap partition */
+	unsigned char boot_file[16];	/* name of boot file */
+
+	struct sgi_device_parameter	devparam;	/* not used now */
+
+	struct sgi_volume {
+		unsigned char name[8];	/* name of volume */
+		uint32_t block_num;	/* logical block number */
+		uint32_t num_bytes;	/* how big, in bytes */
+	} __attribute__((packed)) volume[15];
+
+	struct sgi_partition {
+		uint32_t num_blocks;	/* size in logical blocks */
+		uint32_t first_block;	/* first logical block */
+		uint32_t type;		/* type of this partition */
+	} __attribute__((packed)) partitions[SGI_MAXPARTITIONS];
+
+	/* checksum is the 32bit 2's complement sum of the disklabel */
+	uint32_t csum;			/* disk label checksum */
+	uint32_t padding;		/* padding */
+} __attribute__((packed));
+
+static uint32_t count_checksum(struct sgi_disklabel *label)
+{
+	int i;
+	uint32_t *ptr = (uint32_t *) label;
+	uint32_t sum = 0;
+
+	i = sizeof(*label) / sizeof(*ptr);
+
+	while (i--)
+		sum += be32_to_cpu(ptr[i]);
+
+	return sum;
+}
+
+
+static int probe_sgi_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct sgi_disklabel *l;
+	struct sgi_partition *p;
+	blkid_parttable tab = NULL;
+	blkid_partlist ls;
+	int i;
+
+	l = (struct sgi_disklabel *) blkid_probe_get_sector(pr, 0);
+	if (!l)
+		goto nothing;
+
+	if (count_checksum(l)) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"detected corrupted sgi disk label -- ignore\n"));
+		goto nothing;
+	}
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	tab = blkid_partlist_new_parttable(ls, "sgi", 0);
+	if (!tab)
+		goto err;
+
+	for(i = 0, p = &l->partitions[0]; i < SGI_MAXPARTITIONS; i++, p++) {
+		uint32_t size = be32_to_cpu(p->num_blocks);
+		uint32_t start = be32_to_cpu(p->first_block);
+		uint32_t type = be32_to_cpu(p->type);
+		blkid_partition par;
+
+		if (size == 0 || type == SGI_TYPE_VOLULME ||
+			         type == SGI_TYPE_VOLHDR) {
+			blkid_partlist_increment_partno(ls);
+			continue;
+		}
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_type(par, type);
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+const struct blkid_idinfo sgi_pt_idinfo =
+{
+	.name		= "sgi",
+	.probefunc	= probe_sgi_pt,
+	.magics		=
+	{
+		{ .magic = "\x0B\xE5\xA9\x41", .len = 4	},
+		{ NULL }
+	}
+};
+
diff --git a/src/partitions/solaris_x86.c b/src/partitions/solaris_x86.c
new file mode 100644
index 0000000..7824f4e
--- /dev/null
+++ b/src/partitions/solaris_x86.c
@@ -0,0 +1,151 @@
+/*
+ * Solaris x86 partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+
+/*
+ * Solaris-x86 is always within primary dos partition (nested PT table).  The
+ * solaris-x86 vtoc allows to split the entire partition to "slices". The
+ * offset (start) of the slice is always relatively to the primary dos
+ * partition.
+ *
+ * Note that Solaris-SPARC uses entire disk with a different partitionning
+ * scheme.
+ */
+
+/* some other implementation than Linux kernel assume 8 partitions only */
+#define SOLARIS_MAXPARTITIONS	16
+
+/* disklabel (vtoc) location  */
+#define SOLARIS_SECTOR		1			/* in 512-sectors */
+#define SOLARIS_OFFSET		(SOLARIS_SECTOR << 9)	/* in bytes */
+#define SOLARIS_MAGICOFFSET	(SOLARIS_OFFSET + 12)	/* v_sanity offset in bytes */
+
+/* slice tags */
+#define SOLARIS_TAG_WHOLEDISK	5
+
+struct solaris_slice {
+	uint16_t s_tag;      /* ID tag of partition */
+	uint16_t s_flag;     /* permission flags */
+	uint32_t s_start;    /* start sector no of partition */
+	uint32_t s_size;     /* # of blocks in partition */
+} __attribute__((packed));
+
+struct solaris_vtoc {
+	unsigned int v_bootinfo[3];     /* info needed by mboot (unsupported) */
+
+	uint32_t     v_sanity;          /* to verify vtoc sanity */
+	uint32_t     v_version;         /* layout version */
+	char         v_volume[8];       /* volume name */
+	uint16_t     v_sectorsz;        /* sector size in bytes */
+	uint16_t     v_nparts;          /* number of partitions */
+	unsigned int v_reserved[10];    /* free space */
+
+	struct solaris_slice v_slice[SOLARIS_MAXPARTITIONS]; /* slices */
+
+	unsigned int timestamp[SOLARIS_MAXPARTITIONS]; /* timestamp (unsupported) */
+	char         v_asciilabel[128];	/* for compatibility */
+} __attribute__((packed));
+
+static int probe_solaris_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct solaris_vtoc *l;	/* disk label */
+	struct solaris_slice *p;	/* partitsion */
+	blkid_parttable tab = NULL;
+	blkid_partition parent;
+	blkid_partlist ls;
+	int i;
+	uint16_t nparts;
+
+	l = (struct solaris_vtoc *) blkid_probe_get_sector(pr, SOLARIS_SECTOR);
+	if (!l)
+		goto nothing;
+
+	if (le32_to_cpu(l->v_version) != 1) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"WARNING: unsupported solaris x86 version %d, ignore\n",
+			le32_to_cpu(l->v_version)));
+		goto nothing;
+	}
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	parent = blkid_partlist_get_parent(ls);
+
+	tab = blkid_partlist_new_parttable(ls, "solaris", SOLARIS_OFFSET);
+	if (!tab)
+		goto err;
+
+	nparts = le16_to_cpu(l->v_nparts);
+	if (nparts > SOLARIS_MAXPARTITIONS)
+		nparts = SOLARIS_MAXPARTITIONS;
+
+	for (i = 1, p = &l->v_slice[0];	i < nparts; i++, p++) {
+
+		uint32_t start = le32_to_cpu(p->s_start);
+		uint32_t size = le32_to_cpu(p->s_size);
+		blkid_partition par;
+
+		if (size == 0 || le16_to_cpu(p->s_tag) == SOLARIS_TAG_WHOLEDISK)
+			continue;
+
+		if (parent)
+			/* Solaris slices are relative to the parent (primary
+			 * DOS partition) */
+			start += blkid_partition_get_start(parent);
+
+		if (parent && !blkid_is_nested_dimension(parent, start, size)) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"WARNING: solaris partition (%d) overflow "
+				"detected, ignore\n", i));
+			continue;
+		}
+
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_type(par, le16_to_cpu(p->s_tag));
+		blkid_partition_set_flags(par, le16_to_cpu(p->s_flag));
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+const struct blkid_idinfo solaris_x86_pt_idinfo =
+{
+	.name		= "solaris",
+	.probefunc	= probe_solaris_pt,
+	.magics		=
+	{
+		{
+		  .magic = "\xEE\xDE\x0D\x60",	/* little-endian magic string */
+		  .len = 4,			/* v_sanity size in bytes */
+		  .sboff = SOLARIS_MAGICOFFSET	/* offset of v_sanity */
+		},
+		{ NULL }
+	}
+};
+
diff --git a/src/partitions/sun.c b/src/partitions/sun.c
new file mode 100644
index 0000000..f151f46
--- /dev/null
+++ b/src/partitions/sun.c
@@ -0,0 +1,188 @@
+/*
+ * sun (solaris-sparc) partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "partitions.h"
+
+/* Supported VTOC setting */
+#define SUN_VTOC_SANITY		0x600DDEEE	/* magic number */
+#define SUN_VTOC_VERSION	1
+
+#define SUN_MAXPARTITIONS	8
+
+/* Partition IDs */
+#define SUN_TAG_WHOLEDISK          0x05
+
+struct sun_disklabel {
+	unsigned char info[128];   /* Informative text string */
+
+	struct sun_vtoc {
+		uint32_t version;     /* version */
+		char	 volume[8];   /* volume name */
+		uint16_t nparts;      /* num of partitions */
+
+		struct sun_info {     /* partition information */
+			uint16_t id;  /* tag */
+			uint16_t flags;
+		} __attribute__ ((packed)) infos[8];
+
+		uint16_t padding;      /* padding */
+		uint32_t bootinfo[3];  /* info needed by mboot */
+		uint32_t sanity;       /* magic number */
+		uint32_t reserved[10]; /* padding */
+		uint32_t timestamp[8]; /* partition timestamp */
+	} __attribute__ ((packed)) vtoc;
+
+	uint32_t write_reinstruct;     /* sectors to skip, writes */
+	uint32_t read_reinstruct;      /* sectors to skip, reads */
+	unsigned char spare[148];      /* padding */
+	uint16_t rspeed;               /* disk rotational speed */
+	uint16_t pcylcount;            /* physical cylinder count */
+	uint16_t sparecyl;             /* extra sects per cylinder */
+	uint16_t obs1;
+	uint16_t obs2;
+	uint16_t ilfact;               /* interleave factor */
+	uint16_t ncyl;                 /* data cylinder count */
+	uint16_t nacyl;                /* alt. cylinder count */
+	uint16_t ntrks;                /* tracks per cylinder   <---- */
+	uint16_t nsect;                /* sectors per track     <---- */
+	uint16_t obs3;
+	uint16_t obs4;
+
+	struct sun_partition {         /* partitions */
+		uint32_t start_cylinder;
+		uint32_t num_sectors;
+	} __attribute__ ((packed)) partitions[8];
+
+	uint16_t magic;                /* magic number */
+	uint16_t csum;                 /* label xor'd checksum */
+} __attribute__ ((packed));
+
+
+uint16_t count_checksum(struct sun_disklabel *label)
+{
+	uint16_t *ptr = ((uint16_t *) (label + 1)) - 1;
+	uint16_t sum;
+
+	for (sum = 0; ptr >= ((uint16_t *) label);)
+		sum ^= *ptr--;
+
+	return sum;
+}
+
+static int probe_sun_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct sun_disklabel *l;
+	struct sun_partition *p;
+	blkid_parttable tab = NULL;
+	blkid_partlist ls;
+	uint16_t nparts;
+	blkid_loff_t spc;
+	int i, use_vtoc;
+
+	l = (struct sun_disklabel *) blkid_probe_get_sector(pr, 0);
+	if (!l)
+		goto nothing;
+
+	if (count_checksum(l)) {
+		DBG(DEBUG_LOWPROBE, printf(
+			"detected corrupted sun disk label -- ignore\n"));
+		goto nothing;
+	}
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	tab = blkid_partlist_new_parttable(ls, "sun", 0);
+	if (!tab)
+		goto err;
+
+	/* sectors per cylinder (partition offset is in cylinders...) */
+	spc = be16_to_cpu(l->ntrks) * be16_to_cpu(l->nsect);
+
+	DBG(DEBUG_LOWPROBE,
+		printf("Sun VTOC sanity=%u version=%u nparts=%u\n",
+			be32_to_cpu(l->vtoc.sanity),
+			be32_to_cpu(l->vtoc.version),
+			be16_to_cpu(l->vtoc.nparts)));
+
+	/* Check to see if we can use the VTOC table */
+	use_vtoc = ((be32_to_cpu(l->vtoc.sanity) == SUN_VTOC_SANITY) &&
+		    (be32_to_cpu(l->vtoc.version) == SUN_VTOC_VERSION) &&
+		    (be16_to_cpu(l->vtoc.nparts) <= SUN_MAXPARTITIONS));
+
+	/* Use 8 partition entries if not specified in validated VTOC */
+	nparts = use_vtoc ? be16_to_cpu(l->vtoc.nparts) : SUN_MAXPARTITIONS;
+
+	/*
+	 * So that old Linux-Sun partitions continue to work,
+	 * alow the VTOC to be used under the additional condition ...
+	 */
+	use_vtoc = use_vtoc || !(l->vtoc.sanity || l->vtoc.version || l->vtoc.nparts);
+
+	for (i = 0, p = l->partitions; i < nparts; i++, p++) {
+
+		blkid_loff_t start, size;
+		uint16_t type = 0, flags = 0;
+		blkid_partition par;
+
+                start = be32_to_cpu(p->start_cylinder) * spc;
+		size = be32_to_cpu(p->num_sectors);
+		if (use_vtoc) {
+			type = be16_to_cpu(l->vtoc.infos[i].id);
+			flags = be16_to_cpu(l->vtoc.infos[i].flags);
+		}
+
+		if (type == SUN_TAG_WHOLEDISK || !size) {
+			blkid_partlist_increment_partno(ls);
+			continue;
+		}
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		if (type)
+			blkid_partition_set_type(par, type);
+		if (flags)
+			blkid_partition_set_flags(par, flags);
+	}
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+
+const struct blkid_idinfo sun_pt_idinfo =
+{
+	.name		= "sun",
+	.probefunc	= probe_sun_pt,
+	.magics		=
+	{
+		{
+		  .magic = "\xDA\xBE",		/* big-endian magic string */
+		  .len = 2,
+		  .sboff = offsetof(struct sun_disklabel, magic)
+		},
+		{ NULL }
+	}
+};
+
diff --git a/src/partitions/ultrix.c b/src/partitions/ultrix.c
new file mode 100644
index 0000000..853ae6e
--- /dev/null
+++ b/src/partitions/ultrix.c
@@ -0,0 +1,96 @@
+/*
+ * uktrix partition parsing code
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+
+#define ULTRIX_MAXPARTITIONS	8
+
+#define ULTRIX_MAGIC		0x032957
+#define ULTRIX_MAGIC_STR	"\x02\x29\x57"
+
+/* sector with partition table */
+#define ULTRIX_SECTOR		((16384 - sizeof(struct ultrix_disklabel)) >> 9)
+/* position of partition table within ULTRIX_SECTOR */
+#define ULTRIX_OFFSET		(512 - sizeof(struct ultrix_disklabel))
+
+struct ultrix_disklabel {
+	int32_t	pt_magic;	/* magic no. indicating part. info exits */
+	int32_t	pt_valid;	/* set by driver if pt is current */
+	struct  pt_info {
+		int32_t		pi_nblocks; /* no. of sectors */
+		uint32_t	pi_blkoff;  /* block offset for start */
+	} pt_part[ULTRIX_MAXPARTITIONS];
+} __attribute__((packed));
+
+
+static int probe_ultrix_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	unsigned char *data;
+	struct ultrix_disklabel *l;
+	blkid_parttable tab = NULL;
+	blkid_partlist ls;
+	int i;
+
+	data = blkid_probe_get_sector(pr, ULTRIX_SECTOR);
+	if (!data)
+		goto nothing;
+
+	l = (struct ultrix_disklabel *) (data + ULTRIX_OFFSET);
+
+	if (l->pt_magic != ULTRIX_MAGIC || l->pt_valid != 1)
+		goto nothing;
+
+	if (blkid_probe_set_magic(pr, (ULTRIX_SECTOR << 9) + ULTRIX_OFFSET,
+			sizeof(ULTRIX_MAGIC_STR) - 1,
+			(unsigned char *) ULTRIX_MAGIC_STR))
+		goto err;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	tab = blkid_partlist_new_parttable(ls, "ultrix", 0);
+	if (!tab)
+		goto err;
+
+	for (i = 0; i < ULTRIX_MAXPARTITIONS; i++) {
+		if (!l->pt_part[i].pi_nblocks)
+			 blkid_partlist_increment_partno(ls);
+		else {
+			if (!blkid_partlist_add_partition(ls, tab,
+						l->pt_part[i].pi_blkoff,
+						l->pt_part[i].pi_nblocks))
+				goto err;
+		}
+	}
+
+	return 0;
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+const struct blkid_idinfo ultrix_pt_idinfo =
+{
+	.name		= "ultrix",
+	.probefunc	= probe_ultrix_pt,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/src/partitions/unixware.c b/src/partitions/unixware.c
new file mode 100644
index 0000000..e9bcba3
--- /dev/null
+++ b/src/partitions/unixware.c
@@ -0,0 +1,194 @@
+/*
+ * unixware partition parsing code
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ *
+ * The intersting information about unixware PT:
+ *   - Linux kernel / partx
+ *   - vtoc(7) SCO UNIX command man page
+ *   - evms source code (http://evms.sourceforge.net/)
+ *   - vxtools source code (http://martin.hinner.info/fs/vxfs/)
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+
+/* disklabel location */
+#define UNIXWARE_SECTOR		29
+#define UNIXWARE_OFFSET		(UNIXWARE_SECTOR << 9)	/* offset in bytes */
+#define UNIXWARE_KBOFFSET	(UNIXWARE_OFFSET >> 10)	/* offset in 1024-blocks */
+
+/* disklabel->d_magic offset within the last 1024 block */
+#define UNIXWARE_MAGICOFFSET	(UNIXWARE_OFFSET - UNIXWARE_KBOFFSET + 4)
+
+#define UNIXWARE_VTOCMAGIC	0x600DDEEEUL
+#define UNIXWARE_MAXPARTITIONS	16
+
+/* unixware_partition->s_label flags */
+#define UNIXWARE_TAG_UNUSED       0x0000  /* unused partition */
+#define UNIXWARE_TAG_BOOT         0x0001  /* boot fs */
+#define UNIXWARE_TAG_ROOT         0x0002  /* root fs */
+#define UNIXWARE_TAG_SWAP         0x0003  /* swap fs */
+#define UNIXWARE_TAG_USER         0x0004  /* user fs */
+#define UNIXWARE_TAG_ENTIRE_DISK  0x0005  /* whole disk */
+#define UNIXWARE_TAG_ALT_S        0x0006  /* alternate sector space */
+#define UNIXWARE_TAG_OTHER        0x0007  /* non unix */
+#define UNIXWARE_TAG_ALT_T        0x0008  /* alternate track space */
+#define UNIXWARE_TAG_STAND        0x0009  /* stand partition */
+#define UNIXWARE_TAG_VAR          0x000a  /* var partition */
+#define UNIXWARE_TAG_HOME         0x000b  /* home partition */
+#define UNIXWARE_TAG_DUMP         0x000c  /* dump partition */
+#define UNIXWARE_TAG_ALT_ST       0x000d  /* alternate sector track */
+#define UNIXWARE_TAG_VM_PUBLIC    0x000e  /* volume mgt public partition */
+#define UNIXWARE_TAG_VM_PRIVATE   0x000f  /* volume mgt private partition */
+
+
+/* unixware_partition->s_flags flags */
+#define UNIXWARE_FLAG_VALID	0x0200
+
+struct unixware_partition {
+	uint16_t	s_label;	/* partition label (tag) */
+	uint16_t	s_flags;	/* permission flags */
+	uint32_t	start_sect;	/* starting sector */
+	uint32_t	nr_sects;	/* number of sectors */
+} __attribute__((packed));
+
+struct unixware_disklabel {
+	uint32_t	d_type;		/* drive type */
+	uint32_t	d_magic;	/* the magic number */
+	uint32_t	d_version;	/* version number */
+	char		d_serial[12];	/* serial number of the device */
+	uint32_t	d_ncylinders;	/* # of data cylinders per device */
+	uint32_t	d_ntracks;	/* # of tracks per cylinder */
+	uint32_t	d_nsectors;	/* # of data sectors per track */
+	uint32_t	d_secsize;	/* # of bytes per sector */
+	uint32_t	d_part_start;	/* # of first sector of this partition */
+	uint32_t	d_unknown1[12];	/* ? */
+	uint32_t	d_alt_tbl;	/* byte offset of alternate table */
+	uint32_t	d_alt_len;	/* byte length of alternate table */
+	uint32_t	d_phys_cyl;	/* # of physical cylinders per device */
+	uint32_t	d_phys_trk;	/* # of physical tracks per cylinder */
+	uint32_t	d_phys_sec;	/* # of physical sectors per track */
+	uint32_t	d_phys_bytes;	/* # of physical bytes per sector */
+	uint32_t	d_unknown2;	/* ? */
+	uint32_t	d_unknown3;	/* ? */
+	uint32_t	d_pad[8];	/* pad */
+
+	struct unixware_vtoc {
+		uint32_t	v_magic;	/* the magic number */
+		uint32_t	v_version;	/* version number */
+		char		v_name[8];	/* volume name */
+		uint16_t	v_nslices;	/* # of partitions */
+		uint16_t	v_unknown1;	/* ? */
+		uint32_t	v_reserved[10];	/* reserved */
+
+		struct unixware_partition
+			v_slice[UNIXWARE_MAXPARTITIONS]; /* partition */
+	} __attribute__((packed)) vtoc;
+};
+
+static int probe_unixware_pt(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct unixware_disklabel *l;
+	struct unixware_partition *p;
+	blkid_parttable tab = NULL;
+	blkid_partition parent;
+	blkid_partlist ls;
+	int i;
+
+	l = (struct unixware_disklabel *)
+			blkid_probe_get_sector(pr, UNIXWARE_SECTOR);
+	if (!l)
+		goto nothing;
+
+	if (le32_to_cpu(l->vtoc.v_magic) != UNIXWARE_VTOCMAGIC)
+		goto nothing;
+
+	if (blkid_partitions_need_typeonly(pr))
+		/* caller does not ask for details about partitions */
+		return 0;
+
+	ls = blkid_probe_get_partlist(pr);
+	if (!ls)
+		goto err;
+
+	parent = blkid_partlist_get_parent(ls);
+
+	tab = blkid_partlist_new_parttable(ls, "unixware", UNIXWARE_OFFSET);
+	if (!tab)
+		goto err;
+
+	/* Skip the first partition that describe whole disk
+	 */
+	for (i = 1, p = &l->vtoc.v_slice[1];
+			i < UNIXWARE_MAXPARTITIONS; i++, p++) {
+
+		uint32_t start, size;
+		uint16_t tag, flg;
+		blkid_partition par;
+
+		tag = le16_to_cpu(p->s_label);
+		flg = le16_to_cpu(p->s_flags);
+
+		if (tag == UNIXWARE_TAG_UNUSED ||
+		    tag == UNIXWARE_TAG_ENTIRE_DISK ||
+		    flg != UNIXWARE_FLAG_VALID)
+			continue;
+
+		start = le32_to_cpu(p->start_sect);
+		size = le32_to_cpu(p->nr_sects);
+
+		if (parent && !blkid_is_nested_dimension(parent, start, size)) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"WARNING: unixware partition (%d) overflow "
+				"detected, ignore\n", i));
+			continue;
+		}
+
+		par = blkid_partlist_add_partition(ls, tab, start, size);
+		if (!par)
+			goto err;
+
+		blkid_partition_set_type(par, tag);
+		blkid_partition_set_flags(par, flg);
+	}
+
+	return 0;
+
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+
+/*
+ * The unixware partition table is within primary DOS partition.  The PT is
+ * located on 29 sector, PT magic string is d_magic member of 'struct
+ * unixware_disklabel'.
+ */
+const struct blkid_idinfo unixware_pt_idinfo =
+{
+	.name		= "unixware",
+	.probefunc	= probe_unixware_pt,
+	.minsz		= 1024 * 1440 + 1,		/* ignore floppies */
+	.magics		=
+	{
+		{
+		  .magic = "\x0D\x60\xE5\xCA",	/* little-endian magic string */
+		  .len = 4,			/* d_magic size in bytes */
+		  .kboff = UNIXWARE_KBOFFSET,
+		  .sboff = UNIXWARE_MAGICOFFSET
+		},
+		{ NULL }
+	}
+};
+
diff --git a/src/probe.c b/src/probe.c
new file mode 100644
index 0000000..467b1fd
--- /dev/null
+++ b/src/probe.c
@@ -0,0 +1,1681 @@
+/*
+ * Low-level libblkid probing API
+ *
+ * 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 reset 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 <unistd.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_ERRNO_H
+#include <errno.h>
+#endif
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#ifdef HAVE_LIBUUID
+# include <uuid.h>
+#endif
+
+#include "blkidP.h"
+#include "all-io.h"
+
+/* chains */
+extern const struct blkid_chaindrv superblocks_drv;
+extern const struct blkid_chaindrv topology_drv;
+extern const struct blkid_chaindrv partitions_drv;
+
+/*
+ * 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
+};
+
+static void blkid_probe_reset_vals(blkid_probe pr);
+static void blkid_probe_reset_buffer(blkid_probe pr);
+
+/**
+ * blkid_new_probe:
+ *
+ * Returns: a pointer to the newly allocated probe struct or NULL in case of error.
+ */
+blkid_probe blkid_new_probe(void)
+{
+	int i;
+	blkid_probe pr;
+
+	blkid_init_debug(0);
+	pr = calloc(1, sizeof(struct blkid_struct_probe));
+	if (!pr)
+		return NULL;
+
+	DBG(DEBUG_LOWPROBE, printf("allocate a new probe %p\n", pr));
+
+	/* 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;
+	}
+	INIT_LIST_HEAD(&pr->buffers);
+	return pr;
+}
+
+/*
+ * Clone @parent, the new clone shares all, but except:
+ *
+ *	- probing result
+ *	- bufferes if another device (or offset) is set to the prober
+ */
+blkid_probe blkid_clone_probe(blkid_probe parent)
+{
+	blkid_probe pr;
+
+	if (!parent)
+		return NULL;
+
+	DBG(DEBUG_LOWPROBE, printf("allocate a probe clone\n"));
+
+	pr = blkid_new_probe();
+	if (!pr)
+		return NULL;
+
+	pr->fd = parent->fd;
+	pr->off = parent->off;
+	pr->size = parent->size;
+	pr->devno = parent->devno;
+	pr->disk_devno = parent->disk_devno;
+	pr->blkssz = parent->blkssz;
+	pr->flags = parent->flags;
+	pr->parent = parent;
+
+	return pr;
+}
+
+
+
+/**
+ * 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_from_filename(const char *filename)
+{
+	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_FL_PRIVATE_FD;
+	return pr;
+err:
+	if (fd >= 0)
+		close(fd);
+	blkid_free_probe(pr);
+	return NULL;
+}
+
+/**
+ * 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;
+
+	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_FL_PRIVATE_FD) && pr->fd >= 0)
+		close(pr->fd);
+	blkid_probe_reset_buffer(pr);
+	blkid_free_probe(pr->disk_probe);
+
+	DBG(DEBUG_LOWPROBE, printf("free probe %p\n", pr));
+	free(pr);
+}
+
+
+/*
+ * Removes chain values from probing result.
+ */
+void blkid_probe_chain_reset_vals(blkid_probe pr, struct blkid_chain *chn)
+{
+	int nvals = pr->nvals;
+	int i, x;
+
+	for (x = 0, i = 0; i < pr->nvals; i++) {
+		struct blkid_prval *v = &pr->vals[i];
+
+		if (v->chain != chn && x == i) {
+			x++;
+			continue;
+		}
+		if (v->chain == chn) {
+			--nvals;
+			continue;
+		}
+		memcpy(&pr->vals[x++], v, sizeof(struct blkid_prval));
+	}
+	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.
+ */
+int blkid_probe_chain_copy_vals(blkid_probe pr, struct blkid_chain *chn,
+		struct blkid_prval *vals, int nvals)
+{
+	int i, x;
+
+	for (x = 0, i = 0; i < pr->nvals && x < nvals; i++) {
+		struct blkid_prval *v = &pr->vals[i];
+
+		if (v->chain != chn)
+			continue;
+		memcpy(&vals[x++], v, sizeof(struct blkid_prval));
+	}
+	return x;
+}
+
+/*
+ * Appends values from @vals to the probing result
+ */
+void blkid_probe_append_vals(blkid_probe pr, struct blkid_prval *vals, int nvals)
+{
+	int i = 0;
+
+	while (i < nvals && pr->nvals < BLKID_NVALS) {
+		memcpy(&pr->vals[pr->nvals++], &vals[i++],
+				sizeof(struct blkid_prval));
+	}
+}
+
+static void blkid_probe_reset_vals(blkid_probe pr)
+{
+	memset(pr->vals, 0, sizeof(pr->vals));
+	pr->nvals = 0;
+}
+
+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)
+{
+	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;
+
+	blkid_probe_reset_vals(pr);
+	blkid_probe_set_wiper(pr, 0, 0);
+
+	pr->cur_chain = NULL;
+
+	for (i = 0; i < BLKID_NCHAINS; i++)
+		blkid_probe_chain_reset_position(&pr->chains[i]);
+}
+
+/***
+static int blkid_probe_dump_filter(blkid_probe pr, int chain)
+{
+	struct blkid_chain *chn;
+	int i;
+
+	if (!pr || chain < 0 || chain >= BLKID_NCHAINS)
+		return -1;
+
+	chn = &pr->chains[chain];
+
+	if (!chn->fltr)
+		return -1;
+
+	for (i = 0; i < chn->driver->nidinfos; i++) {
+		const struct blkid_idinfo *id = chn->driver->idinfos[i];
+
+		DBG(DEBUG_LOWPROBE, printf("%d: %s: %s\n",
+			i,
+			id->name,
+			blkid_bmp_get_item(chn->fltr, i)
+				? "disabled" : "enabled <--"));
+	}
+	return 0;
+}
+***/
+
+/*
+ * Returns properly initialized chain filter
+ */
+unsigned long *blkid_probe_get_filter(blkid_probe pr, int chain, int create)
+{
+	struct blkid_chain *chn;
+
+	if (!pr || chain < 0 || chain >= BLKID_NCHAINS)
+		return NULL;
+
+	chn = &pr->chains[chain];
+
+	/* always when you touch the chain filter all indexes are reset and
+	 * probing starts from scratch
+	 */
+	blkid_probe_chain_reset_position(chn);
+	pr->cur_chain = NULL;
+
+	if (!chn->driver->has_fltr || (!chn->fltr && !create))
+		return NULL;
+
+	if (!chn->fltr)
+		chn->fltr = calloc(1, blkid_bmp_nbytes(chn->driver->nidinfos));
+	else
+		memset(chn->fltr, 0, blkid_bmp_nbytes(chn->driver->nidinfos));
+
+	/* blkid_probe_dump_filter(pr, chain); */
+	return chn->fltr;
+}
+
+/*
+ * Generic private functions for filter setting
+ */
+int __blkid_probe_invert_filter(blkid_probe pr, int chain)
+{
+	size_t i;
+	struct blkid_chain *chn;
+
+	chn = &pr->chains[chain];
+
+	if (!chn->driver->has_fltr || !chn->fltr)
+		return -1;
+
+	for (i = 0; i < blkid_bmp_nwords(chn->driver->nidinfos); i++)
+		chn->fltr[i] = ~chn->fltr[i];
+
+	DBG(DEBUG_LOWPROBE, printf("probing filter inverted\n"));
+	/* blkid_probe_dump_filter(pr, chain); */
+	return 0;
+}
+
+int __blkid_probe_reset_filter(blkid_probe pr, int chain)
+{
+	return blkid_probe_get_filter(pr, chain, FALSE) ? 0 : -1;
+}
+
+int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[])
+{
+	unsigned long *fltr;
+	struct blkid_chain *chn;
+	size_t i;
+
+	fltr = blkid_probe_get_filter(pr, chain, TRUE);
+	if (!fltr)
+		return -1;
+
+	chn = &pr->chains[chain];
+
+	for (i = 0; i < chn->driver->nidinfos; i++) {
+		int has = 0;
+		const struct blkid_idinfo *id = chn->driver->idinfos[i];
+		char **n;
+
+		for (n = names; *n; n++) {
+			if (!strcmp(id->name, *n)) {
+				has = 1;
+				break;
+			}
+		}
+		if (flag & BLKID_FLTR_ONLYIN) {
+		       if (!has)
+				blkid_bmp_set_item(fltr, i);
+		} else if (flag & BLKID_FLTR_NOTIN) {
+			if (has)
+				blkid_bmp_set_item(fltr, i);
+		}
+	}
+
+	DBG(DEBUG_LOWPROBE,
+		printf("%s: a new probing type-filter initialized\n",
+		chn->driver->name));
+	/* blkid_probe_dump_filter(pr, chain); */
+	return 0;
+}
+
+unsigned char *blkid_probe_get_buffer(blkid_probe pr,
+				blkid_loff_t off, blkid_loff_t len)
+{
+	struct list_head *p;
+	struct blkid_bufinfo *bf = NULL;
+
+	if (pr->size <= 0)
+		return NULL;
+
+	if (pr->parent &&
+	    pr->parent->devno == pr->devno &&
+	    pr->parent->off <= pr->off &&
+	    pr->parent->off + pr->parent->size >= pr->off + pr->size) {
+		/*
+		 * This is a cloned prober and points to the same area as
+		 * parent. Let's use parent's buffers.
+		 *
+		 * Note that pr->off (and pr->parent->off) is always from the
+		 * beginig of the device.
+		 */
+		return blkid_probe_get_buffer(pr->parent,
+				pr->off + off - pr->parent->off, len);
+	}
+
+	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 pr=%p\n",
+							x->off, x->len, pr));
+			bf = x;
+			break;
+		}
+	}
+	if (!bf) {
+		ssize_t ret;
+
+		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 pr=%p\n",
+				off, len, pr));
+
+		ret = read(pr->fd, bf->data, len);
+		if (ret != (ssize_t) len) {
+			free(bf);
+			return NULL;
+		}
+		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 pr=%p\n", pr));
+
+	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);
+}
+
+/*
+ * Small devices need a special care.
+ */
+int blkid_probe_is_tiny(blkid_probe pr)
+{
+	return pr && (pr->flags & BLKID_FL_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_FL_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)
+ *
+ * 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);
+	blkid_probe_reset_buffer(pr);
+
+	if ((pr->flags & BLKID_FL_PRIVATE_FD) && pr->fd >= 0)
+		close(pr->fd);
+
+	pr->flags &= ~BLKID_FL_PRIVATE_FD;
+	pr->flags &= ~BLKID_FL_TINY_DEV;
+	pr->flags &= ~BLKID_FL_CDROM_DEV;
+	pr->prob_flags = 0;
+	pr->fd = fd;
+	pr->off = off;
+	pr->size = 0;
+	pr->devno = 0;
+	pr->disk_devno = 0;
+	pr->mode = 0;
+	pr->blkssz = 0;
+	pr->wipe_off = 0;
+	pr->wipe_size = 0;
+	pr->wipe_chain = NULL;
+
+#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 {
+		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 (pr->off > pr->size)
+			goto err;
+
+		/* The probing area cannot be larger than whole device, pr->off
+		 * is offset within the device */
+		pr->size -= pr->off;
+	}
+
+	if (pr->size <= 1440 * 1024 && !S_ISCHR(sb.st_mode))
+		pr->flags |= BLKID_FL_TINY_DEV;
+
+#ifdef CDROM_GET_CAPABILITY
+	if (S_ISBLK(sb.st_mode) &&
+	    !blkid_probe_is_tiny(pr) &&
+	    blkid_probe_is_wholedisk(pr) &&
+	    ioctl(fd, CDROM_GET_CAPABILITY, NULL) >= 0)
+		pr->flags |= BLKID_FL_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_get_dimension(blkid_probe pr,
+		blkid_loff_t *off, blkid_loff_t *size)
+{
+	if (!pr)
+		return -1;
+
+	*off = pr->off;
+	*size = pr->size;
+	return 0;
+}
+
+int blkid_probe_set_dimension(blkid_probe pr,
+		blkid_loff_t off, blkid_loff_t size)
+{
+	if (!pr)
+		return -1;
+
+	DBG(DEBUG_LOWPROBE, printf(
+		"changing probing area pr=%p: size=%llu, off=%llu "
+		"-to-> size=%llu, off=%llu\n",
+		pr,
+		(unsigned long long) pr->size,
+		(unsigned long long) pr->off,
+		(unsigned long long) size,
+		(unsigned long long) off));
+
+	pr->off = off;
+	pr->size = size;
+	pr->flags &= ~BLKID_FL_TINY_DEV;
+
+	if (pr->size <= 1440 * 1024 && !S_ISCHR(pr->mode))
+		pr->flags |= BLKID_FL_TINY_DEV;
+
+	blkid_probe_reset_buffer(pr);
+
+	return 0;
+}
+
+int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
+			blkid_loff_t *offset, const struct blkid_idmag **res)
+{
+	const struct blkid_idmag *mag = NULL;
+	blkid_loff_t off = 0;
+
+	if (id)
+		mag = &id->magics[0];
+	if (res)
+		*res = NULL;
+
+	/* try to detect by magic string */
+	while(mag && mag->magic) {
+		unsigned char *buf;
+
+		off = (mag->kboff + (mag->sboff >> 10)) << 10;
+		buf = blkid_probe_get_buffer(pr, off, 1024);
+
+		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++;
+	}
+
+	if (id && id->magics[0].magic)
+		/* magic string(s) defined, but not found */
+		return 1;
+
+	return 0;
+}
+
+static inline void blkid_probe_start(blkid_probe pr)
+{
+	if (pr) {
+		pr->cur_chain = NULL;
+		pr->prob_flags = 0;
+		blkid_probe_set_wiper(pr, 0, 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);
+	}
+}
+
+/**
+ * blkid_do_probe:
+ * @pr: prober
+ *
+ * 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 reset
+ * 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);
+ *		for (n = 0; n < nvals; n++) {
+ *			if (blkid_probe_get_value(pr, n, &name, &data, &len) == 0)
+ *				printf("%s = %s\n", name, data);
+ *		}
+ *	}
+ *  </programlisting>
+ * </example>
+ *
+ * <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>
+ *
+ * See also blkid_reset_probe().
+ *
+ * Returns: 0 on success, 1 when probing is done and -1 in case of error.
+ */
+int blkid_do_probe(blkid_probe pr)
+{
+	int rc = 1;
+
+	if (!pr)
+		return -1;
+
+	do {
+		struct blkid_chain *chn = pr->cur_chain;
+
+		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 == (int) chn->driver->nidinfos ||
+				     chn->idx == -1)) {
+
+			size_t idx = chn->driver->id + 1;
+
+			if (idx < BLKID_NCHAINS)
+				chn = pr->cur_chain = &pr->chains[idx];
+			else {
+				blkid_probe_end(pr);
+				return 1;	/* all chains already probed */
+			}
+		}
+
+		chn->binary = FALSE;		/* for sure... */
+
+		DBG(DEBUG_LOWPROBE, printf("chain probe %s %s (idx=%d)\n",
+				chn->driver->name,
+				chn->enabled? "ENABLED" : "DISABLED",
+				chn->idx));
+
+		if (!chn->enabled)
+			continue;
+
+		/* rc: -1 = error, 0 = success, 1 = no result */
+		rc = chn->driver->probe(pr, chn);
+
+	} while (rc == 1);
+
+	return rc;
+}
+
+/**
+ * blkid_do_wipe:
+ * @pr: prober
+ * @dryrun: if TRUE then don't touch the device.
+ *
+ * This function erases the current signature detected by @pr. The @pr has to
+ * be open in O_RDWR mode, BLKID_SUBLKS_MAGIC or/and BLKID_PARTS_MAGIC flags
+ * has to be enabled.
+ *
+ * After successful signature removing the @pr prober will be moved one step
+ * back and the next blkid_do_probe() call will again call previously called
+ * probing function.
+ *
+ *  <example>
+ *  <title>wipe all filesystems or raids from the device</title>
+ *   <programlisting>
+ *      fd = open(devname, O_RDWR);
+ *      blkid_probe_set_device(pr, fd, 0, 0);
+ *
+ *      blkid_probe_enable_superblocks(pr, 1);
+ *      blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC);
+ *
+ *	while (blkid_do_probe(pr) == 0)
+ *		blkid_do_wipe(pr, FALSE);
+ *  </programlisting>
+ * </example>
+ *
+ * Returns: 0 on success, 1 when probing is done and -1 in case of error.
+ */
+int blkid_do_wipe(blkid_probe pr, int dryrun)
+{
+	const char *off = NULL;
+	size_t len = 0;
+	loff_t offset, l;
+	char buf[BUFSIZ];
+	int fd, rc = 0;
+	struct blkid_chain *chn;
+
+	if (!pr)
+		return -1;
+
+	chn = pr->cur_chain;
+	if (!chn)
+		return -1;
+
+	switch (chn->driver->id) {
+	case BLKID_CHAIN_SUBLKS:
+		rc = blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &off, NULL);
+		if (!rc)
+			rc = blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len);
+		break;
+	case BLKID_CHAIN_PARTS:
+		rc = blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &off, NULL);
+		if (!rc)
+			rc = blkid_probe_lookup_value(pr, "PTMAGIC", NULL, &len);
+		break;
+	default:
+		return 0;
+	}
+
+	if (rc || len == 0 || off == NULL)
+		return 0;
+
+	offset = strtoll(off, NULL, 10);
+	fd = blkid_probe_get_fd(pr);
+	if (fd < 0)
+		return -1;
+
+	if (len > sizeof(buf))
+		len = sizeof(buf);
+
+	DBG(DEBUG_LOWPROBE, printf(
+	    "do_wipe [offset=0x%jx, len=%zd, chain=%s, idx=%d, dryrun=%s]\n",
+	    offset, len, chn->driver->name, chn->idx, dryrun ? "yes" : "not"));
+
+	l = lseek(fd, offset, SEEK_SET);
+	if (l == (off_t) -1)
+		return -1;
+
+	memset(buf, 0, len);
+
+	if (!dryrun && len) {
+		if (write_all(fd, buf, len))
+			return -1;
+		fsync(fd);
+
+		blkid_probe_reset_buffer(pr);
+
+		if (chn->idx >= 0) {
+			chn->idx--;
+			DBG(DEBUG_LOWPROBE,
+				printf("do_wipe: moving %s chain index to %d\n",
+				chn->driver->name,
+				chn->idx));
+		}
+		if (chn->idx == -1) {
+			/* blkid_do_probe() goes to the next chain if the index
+			 * of the current chain is -1, so we have to set the
+			 * chain pointer to the previos chain.
+			 */
+			size_t idx = chn->driver->id > 0 ?
+					chn->driver->id - 1 : 0;
+
+			if (idx > 0)
+				pr->cur_chain = &pr->chains[idx];
+			else if (idx == 0)
+				pr->cur_chain = NULL;
+		}
+	}
+	return 0;
+}
+
+/**
+ * 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;
+
+	if (!pr)
+		return -1;
+
+	blkid_probe_start(pr);
+
+	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;
+
+		blkid_probe_chain_reset_position(chn);
+
+		rc = chn->driver->safeprobe(pr, chn);
+
+		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 */
+	}
+
+done:
+	blkid_probe_end(pr);
+	if (rc < 0)
+		return rc;
+	return count ? 0 : 1;
+}
+
+/**
+ * blkid_do_fullprobe:
+ * @pr: prober
+ *
+ * This function gathers probing results from all enabled chains. Same as
+ * blkid_do_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_fullprobe(blkid_probe pr)
+{
+	int i, count = 0, rc = 0;
+
+	if (!pr)
+		return -1;
+
+	blkid_probe_start(pr);
+
+	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 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;
+}
+
+/* same sa blkid_probe_get_buffer() but works with 512-sectors */
+unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector)
+{
+	return pr ? blkid_probe_get_buffer(pr,
+			((blkid_loff_t) sector) << 9, 0x200) : NULL;
+}
+
+struct blkid_prval *blkid_probe_assign_value(
+			blkid_probe pr, const char *name)
+{
+	struct blkid_prval *v;
+
+	if (!name)
+		return NULL;
+	if (pr->nvals >= BLKID_NVALS)
+		return NULL;
+
+	v = &pr->vals[pr->nvals];
+	v->name = name;
+	v->chain = pr->cur_chain;
+	pr->nvals++;
+
+	DBG(DEBUG_LOWPROBE,
+		printf("assigning %s [%s]\n", name, v->chain->driver->name));
+	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)
+{
+	struct blkid_prval *v;
+
+	if (len > BLKID_PROBVAL_BUFSIZ)
+		len = BLKID_PROBVAL_BUFSIZ;
+
+	v = blkid_probe_assign_value(pr, name);
+	if (!v)
+		return -1;
+
+	memcpy(v->data, data, len);
+	v->len = len;
+	return 0;
+}
+
+int blkid_probe_vsprintf_value(blkid_probe pr, const char *name,
+		const char *fmt, va_list ap)
+{
+	struct blkid_prval *v;
+	ssize_t len;
+
+	v = blkid_probe_assign_value(pr, name);
+	if (!v)
+		return -1;
+
+	len = vsnprintf((char *) v->data, sizeof(v->data), fmt, ap);
+
+	if (len <= 0 || (size_t) len >= sizeof(v->data)) {
+		blkid_probe_reset_last_value(pr);
+		return -1;
+	}
+	v->len = len + 1;
+	return 0;
+}
+
+int blkid_probe_sprintf_value(blkid_probe pr, const char *name,
+		const char *fmt, ...)
+{
+	int rc;
+	va_list ap;
+
+	va_start(ap, fmt);
+	rc = blkid_probe_vsprintf_value(pr, name, fmt, ap);
+	va_end(ap);
+
+	return rc;
+}
+
+int blkid_probe_set_magic(blkid_probe pr, blkid_loff_t offset,
+			size_t len, unsigned char *magic)
+{
+	int rc = 0;
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	if (!chn || !magic || !len || chn->binary)
+		return 0;
+
+	switch (chn->driver->id) {
+	case BLKID_CHAIN_SUBLKS:
+		if (!(chn->flags & BLKID_SUBLKS_MAGIC))
+			return 0;
+		rc = blkid_probe_set_value(pr, "SBMAGIC", magic, len);
+		if (!rc)
+			rc = blkid_probe_sprintf_value(pr,
+					"SBMAGIC_OFFSET", "%llu", (unsigned long long)offset);
+		break;
+	case BLKID_CHAIN_PARTS:
+		if (!(chn->flags & BLKID_PARTS_MAGIC))
+			return 0;
+		rc = blkid_probe_set_value(pr, "PTMAGIC", magic, len);
+		if (!rc)
+			rc = blkid_probe_sprintf_value(pr,
+					"PTMAGIC_OFFSET", "%llu", (unsigned long long)offset);
+		break;
+	default:
+		break;
+	}
+
+	return rc;
+}
+
+/**
+ * blkid_probe_get_devno:
+ * @pr: probe
+ *
+ * Returns: block device number, or 0 for regular files.
+ */
+dev_t blkid_probe_get_devno(blkid_probe pr)
+{
+	return pr->devno;
+}
+
+/**
+ * blkid_probe_get_wholedisk_devno:
+ * @pr: probe
+ *
+ * Returns: device number of the wholedisk, or 0 for regular files.
+ */
+dev_t blkid_probe_get_wholedisk_devno(blkid_probe pr)
+{
+	if (!pr->disk_devno) {
+		dev_t devno, disk_devno = 0;
+
+		devno = blkid_probe_get_devno(pr);
+		if (!devno)
+			return 0;
+
+		 if (blkid_devno_to_wholedisk(devno, NULL, 0, &disk_devno) == 0)
+			pr->disk_devno = disk_devno;
+	}
+	return pr->disk_devno;
+}
+
+/**
+ * blkid_probe_is_wholedisk:
+ * @pr: probe
+ *
+ * Returns: 1 if the device is whole-disk or 0.
+ */
+int blkid_probe_is_wholedisk(blkid_probe pr)
+{
+	dev_t devno, disk_devno;
+
+	devno = blkid_probe_get_devno(pr);
+	if (!devno)
+		return 0;
+
+	disk_devno = blkid_probe_get_wholedisk_devno(pr);
+	if (!disk_devno)
+		return 0;
+
+	return devno == disk_devno;
+}
+
+blkid_probe blkid_probe_get_wholedisk_probe(blkid_probe pr)
+{
+	dev_t disk;
+
+	if (blkid_probe_is_wholedisk(pr))
+		return NULL;			/* this is not partition */
+
+	if (pr->parent)
+		/* this is cloned blkid_probe, use parent's stuff */
+		return blkid_probe_get_wholedisk_probe(pr->parent);
+
+	disk = blkid_probe_get_wholedisk_devno(pr);
+
+	if (pr->disk_probe && pr->disk_probe->devno != disk) {
+		/* we have disk prober, but for another disk... close it */
+		blkid_free_probe(pr->disk_probe);
+		pr->disk_probe = NULL;
+	}
+
+	if (!pr->disk_probe) {
+		/* Open a new disk prober */
+		char *disk_path = blkid_devno_to_devname(disk);
+
+		if (!disk_path)
+			return NULL;
+
+		DBG(DEBUG_LOWPROBE, printf("allocate a wholedisk probe\n"));
+
+		pr->disk_probe = blkid_new_probe_from_filename(disk_path);
+
+		free(disk_path);
+
+		if (!pr->disk_probe)
+			return NULL;	/* ENOMEM? */
+	}
+
+	return pr->disk_probe;
+}
+
+/**
+ * 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)
+{
+	return pr ? pr->size : -1;
+}
+
+/**
+ * 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)
+{
+	return pr ? pr->off : -1;
+}
+
+/**
+ * blkid_probe_get_fd:
+ * @pr: probe
+ *
+ * Returns: file descriptor for assigned device/file.
+ */
+int blkid_probe_get_fd(blkid_probe pr)
+{
+	return pr ? pr->fd : -1;
+}
+
+/**
+ * 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)
+{
+	if (!pr)
+		return DEFAULT_SECTOR_SIZE;  /*... and good luck! */
+
+	if (pr->blkssz)
+		return pr->blkssz;
+
+	if (S_ISBLK(pr->mode) &&
+	    blkdev_get_sector_size(pr->fd, (int *) &pr->blkssz) == 0)
+		return pr->blkssz;
+
+	pr->blkssz = DEFAULT_SECTOR_SIZE;
+	return pr->blkssz;
+}
+
+/**
+ * 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;
+}
+
+/**
+ * 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)
+{
+	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 = __blkid_probe_get_value(pr, num);
+
+	if (!v)
+		return -1;
+	if (name)
+		*name = v->name;
+	if (data)
+		*data = (char *) v->data;
+	if (len)
+		*len = v->len;
+
+	DBG(DEBUG_LOWPROBE, printf("returning %s value\n", v->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 NULL;
+
+	for (i = 0; i < pr->nvals; i++) {
+		struct blkid_prval *v = &pr->vals[i];
+
+		if (v->name && strcmp(name, v->name) == 0) {
+			DBG(DEBUG_LOWPROBE, printf("returning %s value\n", v->name));
+			return v;
+		}
+	}
+	return NULL;
+}
+
+
+/* converts DCE UUID (uuid[16]) to human readable string
+ * - the @len should be always 37 */
+#ifdef HAVE_LIBUUID
+void blkid_unparse_uuid(const unsigned char *uuid, char *str,
+			size_t len __attribute__((__unused__)))
+{
+	uuid_unparse(uuid, str);
+}
+#else
+void blkid_unparse_uuid(const unsigned char *uuid, char *str, size_t len)
+{
+	snprintf(str, len,
+		"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+		uuid[0], uuid[1], uuid[2], uuid[3],
+		uuid[4], uuid[5],
+		uuid[6], uuid[7],
+		uuid[8], uuid[9],
+		uuid[10], uuid[11], uuid[12], uuid[13], uuid[14],uuid[15]);
+}
+#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) within the
+ * 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).
+ *
+ * Note that we usually expect only one signature on the device, it means that
+ * we have to remember only one wiped area from previously successfully
+ * detected signature.
+ *
+ * blkid_probe_set_wiper() -- defines wiped area (e.g. LVM)
+ * blkid_probe_use_wiper() -- try to use area (e.g. MBR)
+ *
+ * Note that there is not relation between _wiper and blkid_to_wipe().
+ *
+ */
+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 || (size_t) 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;
+}
+
+/*
+ *  Try to use any area -- if the area has been previously wiped then the
+ *  previous probing result should be ignored (reseted).
+ */
+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("previously wiped area modified "
+				       " -- ignore previous results\n"));
+		blkid_probe_set_wiper(pr, 0, 0);
+		blkid_probe_chain_reset_vals(pr, chn);
+	}
+}
+
diff --git a/src/read.c b/src/read.c
new file mode 100644
index 0000000..60d13db
--- /dev/null
+++ b/src/read.c
@@ -0,0 +1,500 @@
+/*
+ * read.c - read the blkid cache from disk, to avoid scanning all devices
+ *
+ * Copyright (C) 2001, 2003 Theodore Y. Ts'o
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "blkidP.h"
+
+#ifdef HAVE_STDLIB_H
+# ifndef _XOPEN_SOURCE
+#  define _XOPEN_SOURCE 600	/* for inclusion of strtoull */
+# endif
+# include <stdlib.h>
+#endif
+
+#ifdef HAVE_STRTOULL
+#define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */
+#else
+/* FIXME: need to support real strtoull here */
+#define STRTOULL strtoul
+#endif
+
+#ifdef TEST_PROGRAM
+#define blkid_debug_dump_dev(dev)	(debug_dump_dev(dev))
+static void debug_dump_dev(blkid_dev dev);
+#endif
+
+/*
+ * File format:
+ *
+ *	<device [<NAME="value"> ...]>device_name</device>
+ *
+ *	The following tags are required for each entry:
+ *	<ID="id">	unique (within this file) ID number of this device
+ *	<TIME="sec.usec"> (time_t and suseconds_t) time this entry was last
+ *	                 read from disk
+ *	<TYPE="type">	(detected) type of filesystem/data for this partition
+ *
+ *	The following tags may be present, depending on the device contents
+ *	<LABEL="label">	(user supplied) label (volume name, etc)
+ *	<UUID="uuid">	(generated) universally unique identifier (serial no)
+ */
+
+static char *skip_over_blank(char *cp)
+{
+	while (*cp && isspace(*cp))
+		cp++;
+	return cp;
+}
+
+static char *skip_over_word(char *cp)
+{
+	char ch;
+
+	while ((ch = *cp)) {
+		/* If we see a backslash, skip the next character */
+		if (ch == '\\') {
+			cp++;
+			if (*cp == '\0')
+				break;
+			cp++;
+			continue;
+		}
+		if (isspace(ch) || ch == '<' || ch == '>')
+			break;
+		cp++;
+	}
+	return cp;
+}
+
+static char *strip_line(char *line)
+{
+	char	*p;
+
+	line = skip_over_blank(line);
+
+	p = line + strlen(line) - 1;
+
+	while (*line) {
+		if (isspace(*p))
+			*p-- = '\0';
+		else
+			break;
+	}
+
+	return line;
+}
+
+#if 0
+static char *parse_word(char **buf)
+{
+	char *word, *next;
+
+	word = *buf;
+	if (*word == '\0')
+		return NULL;
+
+	word = skip_over_blank(word);
+	next = skip_over_word(word);
+	if (*next) {
+		char *end = next - 1;
+		if (*end == '"' || *end == '\'')
+			*end = '\0';
+		*next++ = '\0';
+	}
+	*buf = next;
+
+	if (*word == '"' || *word == '\'')
+		word++;
+	return word;
+}
+#endif
+
+/*
+ * Start parsing a new line from the cache.
+ *
+ * line starts with "<device" return 1 -> continue parsing line
+ * line starts with "<foo", empty, or # return 0 -> skip line
+ * line starts with other, return -BLKID_ERR_CACHE -> error
+ */
+static int parse_start(char **cp)
+{
+	char *p;
+
+	p = strip_line(*cp);
+
+	/* Skip comment or blank lines.  We can't just NUL the first '#' char,
+	 * in case it is inside quotes, or escaped.
+	 */
+	if (*p == '\0' || *p == '#')
+		return 0;
+
+	if (!strncmp(p, "<device", 7)) {
+		DBG(DEBUG_READ, printf("found device header: %8s\n", p));
+		p += 7;
+
+		*cp = p;
+		return 1;
+	}
+
+	if (*p == '<')
+		return 0;
+
+	return -BLKID_ERR_CACHE;
+}
+
+/* Consume the remaining XML on the line (cosmetic only) */
+static int parse_end(char **cp)
+{
+	*cp = skip_over_blank(*cp);
+
+	if (!strncmp(*cp, "</device>", 9)) {
+		DBG(DEBUG_READ, printf("found device trailer %9s\n", *cp));
+		*cp += 9;
+		return 0;
+	}
+
+	return -BLKID_ERR_CACHE;
+}
+
+/*
+ * Allocate a new device struct with device name filled in.  Will handle
+ * finding the device on lines of the form:
+ * <device foo=bar>devname</device>
+ * <device>devname<foo>bar</foo></device>
+ */
+static int parse_dev(blkid_cache cache, blkid_dev *dev, char **cp)
+{
+	char *start, *tmp, *end, *name;
+	int ret;
+
+	if ((ret = parse_start(cp)) <= 0)
+		return ret;
+
+	start = tmp = strchr(*cp, '>');
+	if (!start) {
+		DBG(DEBUG_READ,
+		    printf("blkid: short line parsing dev: %s\n", *cp));
+		return -BLKID_ERR_CACHE;
+	}
+	start = skip_over_blank(start + 1);
+	end = skip_over_word(start);
+
+	DBG(DEBUG_READ, printf("device should be %*s\n",
+			       (int)(end - start), start));
+
+	if (**cp == '>')
+		*cp = end;
+	else
+		(*cp)++;
+
+	*tmp = '\0';
+
+	if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) {
+		DBG(DEBUG_READ,
+		    printf("blkid: missing </device> ending: %s\n", end));
+	} else if (tmp)
+		*tmp = '\0';
+
+	if (end - start <= 1) {
+		DBG(DEBUG_READ, printf("blkid: empty device name: %s\n", *cp));
+		return -BLKID_ERR_CACHE;
+	}
+
+	name = blkid_strndup(start, end-start);
+	if (name == NULL)
+		return -BLKID_ERR_MEM;
+
+	DBG(DEBUG_READ, printf("found dev %s\n", name));
+
+	if (!(*dev = blkid_get_dev(cache, name, BLKID_DEV_CREATE))) {
+		free(name);
+		return -BLKID_ERR_MEM;
+	}
+
+	free(name);
+	return 1;
+}
+
+/*
+ * Extract a tag of the form NAME="value" from the line.
+ */
+static int parse_token(char **name, char **value, char **cp)
+{
+	char *end;
+
+	if (!name || !value || !cp)
+		return -BLKID_ERR_PARAM;
+
+	if (!(*value = strchr(*cp, '=')))
+		return 0;
+
+	**value = '\0';
+	*name = strip_line(*cp);
+	*value = skip_over_blank(*value + 1);
+
+	if (**value == '"') {
+		end = strchr(*value + 1, '"');
+		if (!end) {
+			DBG(DEBUG_READ,
+			    printf("unbalanced quotes at: %s\n", *value));
+			*cp = *value;
+			return -BLKID_ERR_CACHE;
+		}
+		(*value)++;
+		*end = '\0';
+		end++;
+	} else {
+		end = skip_over_word(*value);
+		if (*end) {
+			*end = '\0';
+			end++;
+		}
+	}
+	*cp = end;
+
+	return 1;
+}
+
+/*
+ * Extract a tag of the form <NAME>value</NAME> from the line.
+ */
+/*
+static int parse_xml(char **name, char **value, char **cp)
+{
+	char *end;
+
+	if (!name || !value || !cp)
+		return -BLKID_ERR_PARAM;
+
+	*name = strip_line(*cp);
+
+	if ((*name)[0] != '<' || (*name)[1] == '/')
+		return 0;
+
+	FIXME: finish this.
+}
+*/
+
+/*
+ * Extract a tag from the line.
+ *
+ * Return 1 if a valid tag was found.
+ * Return 0 if no tag found.
+ * Return -ve error code.
+ */
+static int parse_tag(blkid_cache cache, blkid_dev dev, char **cp)
+{
+	char *name = NULL;
+	char *value = NULL;
+	int ret;
+
+	if (!cache || !dev)
+		return -BLKID_ERR_PARAM;
+
+	if ((ret = parse_token(&name, &value, cp)) <= 0 /* &&
+	    (ret = parse_xml(&name, &value, cp)) <= 0 */)
+		return ret;
+
+	/* Some tags are stored directly in the device struct */
+	if (!strcmp(name, "DEVNO"))
+		dev->bid_devno = STRTOULL(value, 0, 0);
+	else if (!strcmp(name, "PRI"))
+		dev->bid_pri = strtol(value, 0, 0);
+	else if (!strcmp(name, "TIME")) {
+		char *end = NULL;
+		dev->bid_time = STRTOULL(value, &end, 0);
+		if (end && *end == '.')
+			dev->bid_utime = STRTOULL(end + 1, 0, 0);
+	} else
+		ret = blkid_set_tag(dev, name, value, strlen(value));
+
+	DBG(DEBUG_READ, printf("    tag: %s=\"%s\"\n", name, value));
+
+	return ret < 0 ? ret : 1;
+}
+
+/*
+ * Parse a single line of data, and return a newly allocated dev struct.
+ * Add the new device to the cache struct, if one was read.
+ *
+ * Lines are of the form <device [TAG="value" ...]>/dev/foo</device>
+ *
+ * Returns -ve value on error.
+ * Returns 0 otherwise.
+ * If a valid device was read, *dev_p is non-NULL, otherwise it is NULL
+ * (e.g. comment lines, unknown XML content, etc).
+ */
+static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp)
+{
+	blkid_dev dev;
+	int ret;
+
+	if (!cache || !dev_p)
+		return -BLKID_ERR_PARAM;
+
+	*dev_p = NULL;
+
+	DBG(DEBUG_READ, printf("line: %s\n", cp));
+
+	if ((ret = parse_dev(cache, dev_p, &cp)) <= 0)
+		return ret;
+
+	dev = *dev_p;
+
+	while ((ret = parse_tag(cache, dev, &cp)) > 0) {
+		;
+	}
+
+	if (dev->bid_type == NULL) {
+		DBG(DEBUG_READ,
+		    printf("blkid: device %s has no TYPE\n",dev->bid_name));
+		blkid_free_dev(dev);
+		goto done;
+	}
+
+	DBG(DEBUG_READ, blkid_debug_dump_dev(dev));
+
+done:
+	return ret;
+}
+
+/*
+ * Parse the specified filename, and return the data in the supplied or
+ * a newly allocated cache struct.  If the file doesn't exist, return a
+ * new empty cache struct.
+ */
+void blkid_read_cache(blkid_cache cache)
+{
+	FILE *file;
+	char buf[4096];
+	int fd, lineno = 0;
+	struct stat st;
+
+	if (!cache)
+		return;
+
+	/*
+	 * If the file doesn't exist, then we just return an empty
+	 * struct so that the cache can be populated.
+	 */
+	if ((fd = open(cache->bic_filename, O_RDONLY)) < 0)
+		return;
+	if (fstat(fd, &st) < 0)
+		goto errout;
+	if ((st.st_mtime == cache->bic_ftime) ||
+	    (cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+		DBG(DEBUG_CACHE, printf("skipping re-read of %s\n",
+					cache->bic_filename));
+		goto errout;
+	}
+
+	DBG(DEBUG_CACHE, printf("reading cache file %s\n",
+				cache->bic_filename));
+
+	file = fdopen(fd, "r");
+	if (!file)
+		goto errout;
+
+	while (fgets(buf, sizeof(buf), file)) {
+		blkid_dev dev;
+		unsigned int end;
+
+		lineno++;
+		if (buf[0] == 0)
+			continue;
+		end = strlen(buf) - 1;
+		/* Continue reading next line if it ends with a backslash */
+		while (end < (sizeof(buf) - 2) && buf[end] == '\\' &&
+		       fgets(buf + end, sizeof(buf) - end, file)) {
+			end = strlen(buf) - 1;
+			lineno++;
+		}
+
+		if (blkid_parse_line(cache, &dev, buf) < 0) {
+			DBG(DEBUG_READ,
+			    printf("blkid: bad format on line %d\n", lineno));
+			continue;
+		}
+	}
+	fclose(file);
+
+	/*
+	 * Initially we do not need to write out the cache file.
+	 */
+	cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+	cache->bic_ftime = st.st_mtime;
+
+	return;
+errout:
+	close(fd);
+	return;
+}
+
+#ifdef TEST_PROGRAM
+static void debug_dump_dev(blkid_dev dev)
+{
+	struct list_head *p;
+
+	if (!dev) {
+		printf("  dev: NULL\n");
+		return;
+	}
+
+	printf("  dev: name = %s\n", dev->bid_name);
+	printf("  dev: DEVNO=\"0x%0llx\"\n", (long long)dev->bid_devno);
+	printf("  dev: TIME=\"%ld.%ld\"\n", (long)dev->bid_time, (long)dev->bid_utime);
+	printf("  dev: PRI=\"%d\"\n", dev->bid_pri);
+	printf("  dev: flags = 0x%08X\n", dev->bid_flags);
+
+	list_for_each(p, &dev->bid_tags) {
+		blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+		if (tag)
+			printf("    tag: %s=\"%s\"\n", tag->bit_name,
+			       tag->bit_val);
+		else
+			printf("    tag: NULL\n");
+	}
+	printf("\n");
+}
+
+int main(int argc, char**argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_init_debug(DEBUG_ALL);
+	if (argc > 2) {
+		fprintf(stderr, "Usage: %s [filename]\n"
+			"Test parsing of the cache (filename)\n", argv[0]);
+		exit(1);
+	}
+	if ((ret = blkid_get_cache(&cache, argv[1])) < 0)
+		fprintf(stderr, "error %d reading cache file %s\n", ret,
+			argv[1] ? argv[1] : blkid_get_cache_filename(NULL));
+
+	blkid_put_cache(cache);
+
+	return ret;
+}
+#endif
diff --git a/src/resolve.c b/src/resolve.c
new file mode 100644
index 0000000..bf5041d
--- /dev/null
+++ b/src/resolve.c
@@ -0,0 +1,139 @@
+/*
+ * resolve.c - resolve names and tags into specific devices
+ *
+ * Copyright (C) 2001, 2003 Theodore Ts'o.
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "blkidP.h"
+
+/*
+ * Find a tagname (e.g. LABEL or UUID) on a specific device.
+ */
+char *blkid_get_tag_value(blkid_cache cache, const char *tagname,
+			  const char *devname)
+{
+	blkid_tag found;
+	blkid_dev dev;
+	blkid_cache c = cache;
+	char *ret = NULL;
+
+	DBG(DEBUG_RESOLVE, printf("looking for %s on %s\n", tagname, devname));
+
+	if (!devname)
+		return NULL;
+
+	if (!cache) {
+		if (blkid_get_cache(&c, NULL) < 0)
+			return NULL;
+	}
+
+	if ((dev = blkid_get_dev(c, devname, BLKID_DEV_NORMAL)) &&
+	    (found = blkid_find_tag_dev(dev, tagname)))
+		ret = blkid_strdup(found->bit_val);
+
+	if (!cache)
+		blkid_put_cache(c);
+
+	return ret;
+}
+
+/*
+ * Locate a device name from a token (NAME=value string), or (name, value)
+ * pair.  In the case of a token, value is ignored.  If the "token" is not
+ * of the form "NAME=value" and there is no value given, then it is assumed
+ * to be the actual devname and a copy is returned.
+ */
+char *blkid_get_devname(blkid_cache cache, const char *token,
+			const char *value)
+{
+	blkid_dev dev;
+	blkid_cache c = cache;
+	char *t = 0, *v = 0;
+	char *ret = NULL;
+
+	if (!token)
+		return NULL;
+
+	if (!cache) {
+		if (blkid_get_cache(&c, NULL) < 0)
+			return NULL;
+	}
+
+	DBG(DEBUG_RESOLVE,
+	    printf("looking for %s%s%s %s\n", token, value ? "=" : "",
+		   value ? value : "", cache ? "in cache" : "from disk"));
+
+	if (!value) {
+		if (!strchr(token, '=')) {
+			ret = blkid_strdup(token);
+			goto out;
+		}
+		blkid_parse_tag_string(token, &t, &v);
+		if (!t || !v)
+			goto out;
+		token = t;
+		value = v;
+	}
+
+	dev = blkid_find_dev_with_tag(c, token, value);
+	if (!dev)
+		goto out;
+
+	ret = blkid_strdup(blkid_dev_devname(dev));
+
+out:
+	free(t);
+	free(v);
+	if (!cache) {
+		blkid_put_cache(c);
+	}
+	return (ret);
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	char *value;
+	blkid_cache cache;
+
+	blkid_init_debug(DEBUG_ALL);
+	if (argc != 2 && argc != 3) {
+		fprintf(stderr, "Usage:\t%s tagname=value\n"
+			"\t%s tagname devname\n"
+			"Find which device holds a given token or\n"
+			"Find what the value of a tag is in a device\n",
+			argv[0], argv[0]);
+		exit(1);
+	}
+	if (blkid_get_cache(&cache, "/dev/null") < 0) {
+		fprintf(stderr, "Couldn't get blkid cache\n");
+		exit(1);
+	}
+
+	if (argv[2]) {
+		value = blkid_get_tag_value(cache, argv[1], argv[2]);
+		printf("%s has tag %s=%s\n", argv[2], argv[1],
+		       value ? value : "<missing>");
+	} else {
+		value = blkid_get_devname(cache, argv[1], NULL);
+		printf("%s has tag %s\n", value ? value : "<none>", argv[1]);
+	}
+	blkid_put_cache(cache);
+	return value ? 0 : 1;
+}
+#endif
diff --git a/src/save.c b/src/save.c
new file mode 100644
index 0000000..8ce8ea9
--- /dev/null
+++ b/src/save.c
@@ -0,0 +1,227 @@
+/*
+ * save.c - write the cache struct to disk
+ *
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "blkidP.h"
+
+static int save_dev(blkid_dev dev, FILE *file)
+{
+	struct list_head *p;
+
+	if (!dev || dev->bid_name[0] != '/')
+		return 0;
+
+	DBG(DEBUG_SAVE,
+	    printf("device %s, type %s\n", dev->bid_name, dev->bid_type ?
+		   dev->bid_type : "(null)"));
+
+	fprintf(file, "<device DEVNO=\"0x%04lx\" TIME=\"%ld.%ld\"",
+			(unsigned long) dev->bid_devno,
+			(long) dev->bid_time,
+			(long) dev->bid_utime);
+
+	if (dev->bid_pri)
+		fprintf(file, " PRI=\"%d\"", dev->bid_pri);
+	list_for_each(p, &dev->bid_tags) {
+		blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
+		fprintf(file, " %s=\"%s\"", tag->bit_name,tag->bit_val);
+	}
+	fprintf(file, ">%s</device>\n", dev->bid_name);
+
+	return 0;
+}
+
+/*
+ * Write out the cache struct to the cache file on disk.
+ */
+int blkid_flush_cache(blkid_cache cache)
+{
+	struct list_head *p;
+	char *tmp = NULL;
+	char *opened = NULL;
+	char *filename;
+	FILE *file = NULL;
+	int fd, ret = 0;
+	struct stat st;
+
+	if (!cache)
+		return -BLKID_ERR_PARAM;
+
+	if (list_empty(&cache->bic_devs) ||
+	    !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+		DBG(DEBUG_SAVE, printf("skipping cache file write\n"));
+		return 0;
+	}
+
+	filename = cache->bic_filename ? cache->bic_filename :
+					 blkid_get_cache_filename(NULL);
+	if (!filename)
+		return -BLKID_ERR_PARAM;
+
+	if (strncmp(filename,
+	    BLKID_RUNTIME_DIR "/", sizeof(BLKID_RUNTIME_DIR)) == 0) {
+
+		/* default destination, create the directory if necessary */
+		if (stat(BLKID_RUNTIME_DIR, &st)
+		    && errno == ENOENT
+		    && mkdir(BLKID_RUNTIME_DIR, S_IWUSR|
+						S_IRUSR|S_IRGRP|S_IROTH|
+						S_IXUSR|S_IXGRP|S_IXOTH) != 0
+		    && errno != EEXIST) {
+			DBG(DEBUG_SAVE,
+				printf("can't create %s directory for cache file\n",
+					BLKID_RUNTIME_DIR));
+			return 0;
+		}
+	}
+
+	/* If we can't write to the cache file, then don't even try */
+	if (((ret = stat(filename, &st)) < 0 && errno != ENOENT) ||
+	    (ret == 0 && access(filename, W_OK) < 0)) {
+		DBG(DEBUG_SAVE,
+		    printf("can't write to cache file %s\n", filename));
+		return 0;
+	}
+
+	/*
+	 * Try and create a temporary file in the same directory so
+	 * that in case of error we don't overwrite the cache file.
+	 * If the cache file doesn't yet exist, it isn't a regular
+	 * file (e.g. /dev/null or a socket), or we couldn't create
+	 * a temporary file then we open it directly.
+	 */
+	if (ret == 0 && S_ISREG(st.st_mode)) {
+		tmp = malloc(strlen(filename) + 8);
+		if (tmp) {
+			sprintf(tmp, "%s-XXXXXX", filename);
+			fd = mkstemp(tmp);
+			if (fd >= 0) {
+				if (fchmod(fd, 0644) != 0)
+					DBG(DEBUG_SAVE,	printf("%s: fchmod failed\n", filename));
+				else if ((file = fdopen(fd, "w")))
+					opened = tmp;
+				if (!file)
+					close(fd);
+			}
+		}
+	}
+
+	if (!file) {
+		file = fopen(filename, "w");
+		opened = filename;
+	}
+
+	DBG(DEBUG_SAVE,
+	    printf("writing cache file %s (really %s)\n",
+		   filename, opened));
+
+	if (!file) {
+		ret = errno;
+		goto errout;
+	}
+
+	list_for_each(p, &cache->bic_devs) {
+		blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
+		if (!dev->bid_type || (dev->bid_flags & BLKID_BID_FL_REMOVABLE))
+			continue;
+		if ((ret = save_dev(dev, file)) < 0)
+			break;
+	}
+
+	if (ret >= 0) {
+		cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+		ret = 1;
+	}
+
+	fclose(file);
+	if (opened != filename) {
+		if (ret < 0) {
+			unlink(opened);
+			DBG(DEBUG_SAVE,
+			    printf("unlinked temp cache %s\n", opened));
+		} else {
+			char *backup;
+
+			backup = malloc(strlen(filename) + 5);
+			if (backup) {
+				sprintf(backup, "%s.old", filename);
+				unlink(backup);
+				if (link(filename, backup)) {
+					DBG(DEBUG_SAVE,
+						printf("can't link %s to %s\n",
+							filename, backup));
+				}
+				free(backup);
+			}
+			if (rename(opened, filename)) {
+				ret = errno;
+				DBG(DEBUG_SAVE,
+					printf("can't rename %s to %s\n",
+						opened, filename));
+			} else {
+				DBG(DEBUG_SAVE,
+				    printf("moved temp cache %s\n", opened));
+			}
+		}
+	}
+
+errout:
+	free(tmp);
+	if (filename != cache->bic_filename)
+		free(filename);
+	return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	blkid_cache cache = NULL;
+	int ret;
+
+	blkid_init_debug(DEBUG_ALL);
+	if (argc > 2) {
+		fprintf(stderr, "Usage: %s [filename]\n"
+			"Test loading/saving a cache (filename)\n", argv[0]);
+		exit(1);
+	}
+
+	if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+	if ((ret = blkid_probe_all(cache)) < 0) {
+		fprintf(stderr, "error (%d) probing devices\n", ret);
+		exit(1);
+	}
+	cache->bic_filename = blkid_strdup(argv[1]);
+
+	if ((ret = blkid_flush_cache(cache)) < 0) {
+		fprintf(stderr, "error (%d) saving cache\n", ret);
+		exit(1);
+	}
+
+	blkid_put_cache(cache);
+
+	return ret;
+}
+#endif
diff --git a/src/superblocks/adaptec_raid.c b/src/superblocks/adaptec_raid.c
new file mode 100644
index 0000000..02e900d
--- /dev/null
+++ b/src/superblocks/adaptec_raid.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct adaptec_metadata {
+
+	uint32_t	b0idcode;
+	uint8_t		lunsave[8];
+	uint16_t	sdtype;
+	uint16_t	ssavecyl;
+	uint8_t		ssavehed;
+	uint8_t		ssavesec;
+	uint8_t		sb0flags;
+	uint8_t		jbodEnable;
+	uint8_t		lundsave;
+	uint8_t		svpdirty;
+	uint16_t	biosInfo;
+	uint16_t	svwbskip;
+	uint16_t	svwbcln;
+	uint16_t	svwbmax;
+	uint16_t	res3;
+	uint16_t	svwbmin;
+	uint16_t	res4;
+	uint16_t	svrcacth;
+	uint16_t	svwcacth;
+	uint16_t	svwbdly;
+	uint8_t		svsdtime;
+	uint8_t		res5;
+	uint16_t	firmval;
+	uint16_t	firmbln;
+	uint32_t	firmblk;
+	uint32_t	fstrsvrb;
+	uint16_t	svBlockStorageTid;
+	uint16_t	svtid;
+	uint8_t		svseccfl;
+	uint8_t		res6;
+	uint8_t		svhbanum;
+	uint8_t		resver;
+	uint32_t	drivemagic;
+	uint8_t		reserved[20];
+	uint8_t		testnum;
+	uint8_t		testflags;
+	uint16_t	maxErrorCount;
+	uint32_t	count;
+	uint32_t	startTime;
+	uint32_t	interval;
+	uint8_t		tstxt0;
+	uint8_t		tstxt1;
+	uint8_t		serNum[32];
+	uint8_t		res8[102];
+	uint32_t	fwTestMagic;
+	uint32_t	fwTestSeqNum;
+	uint8_t		fwTestRes[8];
+	uint32_t	smagic;
+	uint32_t	raidtbl;
+	uint16_t	raidline;
+	uint8_t		res9[0xF6];
+} __attribute__((packed));
+
+#define AD_SIGNATURE	0x4450544D	/* "DPTM" */
+#define AD_MAGIC	0x37FC4D1E
+
+static int probe_adraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct adaptec_metadata *ad;
+
+	if (pr->size < 0x10000)
+		return -1;
+
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200)-1) * 0x200;
+	ad = (struct adaptec_metadata *)
+			blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct adaptec_metadata));
+	if (!ad)
+		return -1;
+	if (ad->smagic != be32_to_cpu(AD_SIGNATURE))
+		return -1;
+	if (ad->b0idcode != be32_to_cpu(AD_MAGIC))
+		return -1;
+	if (blkid_probe_sprintf_version(pr, "%u", ad->resver) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(ad->b0idcode),
+				(unsigned char *) &ad->b0idcode))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo adraid_idinfo = {
+	.name		= "adaptec_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_adraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/src/superblocks/befs.c b/src/superblocks/befs.c
new file mode 100644
index 0000000..452da1e
--- /dev/null
+++ b/src/superblocks/befs.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2010 Jeroen Oortwijn <oortwijn@gmail.com>
+ *
+ * Partly based on the Haiku BFS driver by
+ *     Axel Dörfler <axeld@pinc-software.de>
+ *
+ * Also inspired by the Linux BeFS driver by
+ *     Will Dyson <will_dyson@pobox.com>, et al.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "superblocks.h"
+
+#define B_OS_NAME_LENGTH	0x20
+#define SUPER_BLOCK_MAGIC1	0x42465331	/* BFS1 */
+#define SUPER_BLOCK_MAGIC2	0xdd121031
+#define SUPER_BLOCK_MAGIC3	0x15b6830e
+#define SUPER_BLOCK_FS_ENDIAN	0x42494745	/* BIGE */
+#define INODE_MAGIC1		0x3bbe0ad9
+#define BPLUSTREE_MAGIC		0x69f6c2e8
+#define BPLUSTREE_NULL		-1LL
+#define NUM_DIRECT_BLOCKS	12
+#define B_UINT64_TYPE		0x554c4c47	/* ULLG */
+#define KEY_NAME		"be:volume_id"
+#define KEY_SIZE		8
+
+#define FS16_TO_CPU(value, fs_is_le) (fs_is_le ? le16_to_cpu(value) \
+							: be16_to_cpu(value))
+#define FS32_TO_CPU(value, fs_is_le) (fs_is_le ? le32_to_cpu(value) \
+							: be32_to_cpu(value))
+#define FS64_TO_CPU(value, fs_is_le) (fs_is_le ? le64_to_cpu(value) \
+							: be64_to_cpu(value))
+
+typedef struct block_run {
+	int32_t		allocation_group;
+	uint16_t	start;
+	uint16_t	len;
+} __attribute__((packed)) block_run, inode_addr;
+
+struct befs_super_block {
+	char		name[B_OS_NAME_LENGTH];
+	int32_t		magic1;
+	int32_t		fs_byte_order;
+	uint32_t	block_size;
+	uint32_t	block_shift;
+	int64_t		num_blocks;
+	int64_t		used_blocks;
+	int32_t		inode_size;
+	int32_t		magic2;
+	int32_t		blocks_per_ag;
+	int32_t		ag_shift;
+	int32_t		num_ags;
+	int32_t		flags;
+	block_run	log_blocks;
+	int64_t		log_start;
+	int64_t		log_end;
+	int32_t		magic3;
+	inode_addr	root_dir;
+	inode_addr	indices;
+	int32_t		pad[8];
+} __attribute__((packed));
+
+typedef struct data_stream {
+	block_run	direct[NUM_DIRECT_BLOCKS];
+	int64_t		max_direct_range;
+	block_run	indirect;
+	int64_t		max_indirect_range;
+	block_run	double_indirect;
+	int64_t		max_double_indirect_range;
+	int64_t		size;
+} __attribute__((packed)) data_stream;
+
+struct befs_inode {
+	int32_t		magic1;
+	inode_addr	inode_num;
+	int32_t		uid;
+	int32_t		gid;
+	int32_t		mode;
+	int32_t		flags;
+	int64_t		create_time;
+	int64_t		last_modified_time;
+	inode_addr	parent;
+	inode_addr	attributes;
+	uint32_t	type;
+	int32_t		inode_size;
+	uint32_t	etc;
+	data_stream	data;
+	int32_t		pad[4];
+	int32_t		small_data[0];
+} __attribute__((packed));
+
+struct small_data {
+	uint32_t	type;
+	uint16_t	name_size;
+	uint16_t	data_size;
+	char		name[0];
+} __attribute__((packed));
+
+struct bplustree_header {
+	uint32_t	magic;
+	uint32_t	node_size;
+	uint32_t	max_number_of_levels;
+	uint32_t	data_type;
+	int64_t		root_node_pointer;
+	int64_t		free_node_pointer;
+	int64_t		maximum_size;
+} __attribute__((packed));
+
+struct bplustree_node {
+	int64_t		left_link;
+	int64_t		right_link;
+	int64_t		overflow_link;
+	uint16_t	all_key_count;
+	uint16_t	all_key_length;
+	char		name[0];
+} __attribute__((packed));
+
+static unsigned char *get_block_run(blkid_probe pr, const struct befs_super_block *bs,
+					const struct block_run *br, int fs_le)
+{
+	return blkid_probe_get_buffer(pr,
+			((blkid_loff_t) FS32_TO_CPU(br->allocation_group, fs_le)
+					<< FS32_TO_CPU(bs->ag_shift, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le))
+				+ ((blkid_loff_t) FS16_TO_CPU(br->start, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le)),
+			(blkid_loff_t) FS16_TO_CPU(br->len, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le));
+}
+
+static unsigned char *get_custom_block_run(blkid_probe pr,
+				const struct befs_super_block *bs,
+				const struct block_run *br,
+				int64_t offset, uint32_t length, int fs_le)
+{
+	if (offset + length > (int64_t) FS16_TO_CPU(br->len, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le))
+		return NULL;
+
+	return blkid_probe_get_buffer(pr,
+			((blkid_loff_t) FS32_TO_CPU(br->allocation_group, fs_le)
+					<< FS32_TO_CPU(bs->ag_shift, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le))
+				+ ((blkid_loff_t) FS16_TO_CPU(br->start, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le))
+				+ offset,
+			length);
+}
+
+static unsigned char *get_tree_node(blkid_probe pr, const struct befs_super_block *bs,
+				const struct data_stream *ds,
+				int64_t start, uint32_t length, int fs_le)
+{
+	if (start < (int64_t) FS64_TO_CPU(ds->max_direct_range, fs_le)) {
+		int64_t br_len;
+		size_t i;
+
+		for (i = 0; i < NUM_DIRECT_BLOCKS; i++) {
+			br_len = (int64_t) FS16_TO_CPU(ds->direct[i].len, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le);
+			if (start < br_len)
+				return get_custom_block_run(pr, bs,
+							&ds->direct[i],
+							start, length, fs_le);
+			else
+				start -= br_len;
+		}
+	} else if (start < (int64_t) FS64_TO_CPU(ds->max_indirect_range, fs_le)) {
+		struct block_run *br;
+		int64_t max_br, br_len, i;
+
+		start -= FS64_TO_CPU(ds->max_direct_range, fs_le);
+		max_br = ((int64_t) FS16_TO_CPU(ds->indirect.len, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le))
+				/ sizeof(struct block_run);
+
+		br = (struct block_run *) get_block_run(pr, bs, &ds->indirect,
+									fs_le);
+		if (!br)
+			return NULL;
+
+		for (i = 0; i < max_br; i++) {
+			br_len = (int64_t) FS16_TO_CPU(br[i].len, fs_le)
+					<< FS32_TO_CPU(bs->block_shift, fs_le);
+			if (start < br_len)
+				return get_custom_block_run(pr, bs, &br[i],
+							start, length, fs_le);
+			else
+				start -= br_len;
+		}
+	} else if (start < (int64_t) FS64_TO_CPU(ds->max_double_indirect_range, fs_le)) {
+		struct block_run *br;
+		int64_t di_br_size, br_per_di_br, di_index, i_index;
+
+		start -= (int64_t) FS64_TO_CPU(ds->max_indirect_range, fs_le);
+
+		di_br_size = (int64_t) FS16_TO_CPU(ds->double_indirect.len,
+				fs_le) << FS32_TO_CPU(bs->block_shift, fs_le);
+		if (di_br_size == 0)
+			return NULL;
+
+		br_per_di_br = di_br_size / sizeof(struct block_run);
+		if (br_per_di_br == 0)
+			return NULL;
+
+		di_index = start / (br_per_di_br * di_br_size);
+		i_index = (start % (br_per_di_br * di_br_size)) / di_br_size;
+		start = (start % (br_per_di_br * di_br_size)) % di_br_size;
+
+		br = (struct block_run *) get_block_run(pr, bs,
+						&ds->double_indirect, fs_le);
+		if (!br)
+			return NULL;
+
+		br = (struct block_run *) get_block_run(pr, bs, &br[di_index],
+									fs_le);
+		if (!br)
+			return NULL;
+
+		return get_custom_block_run(pr, bs, &br[i_index], start, length,
+									fs_le);
+	}
+	return NULL;
+}
+
+static int32_t compare_keys(const char keys1[], uint16_t keylengths1[], int32_t index,
+			const char *key2, uint16_t keylength2, int fs_le)
+{
+	const char *key1;
+	uint16_t keylength1;
+	int32_t result;
+
+	key1 = &keys1[index == 0 ? 0 : FS16_TO_CPU(keylengths1[index - 1],
+									fs_le)];
+	keylength1 = FS16_TO_CPU(keylengths1[index], fs_le)
+			- (index == 0 ? 0 : FS16_TO_CPU(keylengths1[index - 1],
+									fs_le));
+
+	result = strncmp(key1, key2, min(keylength1, keylength2));
+
+	if (result == 0)
+		return keylength1 - keylength2;
+
+	return result;
+}
+
+static int64_t get_key_value(blkid_probe pr, const struct befs_super_block *bs,
+			const struct befs_inode *bi, const char *key, int fs_le)
+{
+	struct bplustree_header *bh;
+	struct bplustree_node *bn;
+	uint16_t *keylengths;
+	int64_t *values;
+	int64_t node_pointer;
+	int32_t first, last, mid, cmp;
+
+	bh = (struct bplustree_header *) get_tree_node(pr, bs, &bi->data, 0,
+					sizeof(struct bplustree_header), fs_le);
+	if (!bh)
+		return -1;
+
+	if ((int32_t) FS32_TO_CPU(bh->magic, fs_le) != BPLUSTREE_MAGIC)
+		return -1;
+
+	node_pointer = FS64_TO_CPU(bh->root_node_pointer, fs_le);
+
+	do {
+		bn = (struct bplustree_node *) get_tree_node(pr, bs, &bi->data,
+			node_pointer, FS32_TO_CPU(bh->node_size, fs_le), fs_le);
+		if (!bn)
+			return -1;
+
+		keylengths = (uint16_t *) ((uint8_t *) bn
+				+ ((sizeof(struct bplustree_node)
+					+ FS16_TO_CPU(bn->all_key_length, fs_le)
+					+ sizeof(int64_t) - 1)
+						& ~(sizeof(int64_t) - 1)));
+		values = (int64_t *) ((uint8_t *) keylengths
+					+ FS16_TO_CPU(bn->all_key_count, fs_le)
+						* sizeof(uint16_t));
+		first = 0;
+		mid = 0;
+		last = FS16_TO_CPU(bn->all_key_count, fs_le) - 1;
+
+		cmp = compare_keys(bn->name, keylengths, last, key, strlen(key),
+									fs_le);
+		if (cmp == 0) {
+			if ((int64_t) FS64_TO_CPU(bn->overflow_link, fs_le)
+							== BPLUSTREE_NULL)
+				return FS64_TO_CPU(values[last], fs_le);
+			else
+				node_pointer = FS64_TO_CPU(values[last], fs_le);
+		} else if (cmp < 0)
+			node_pointer = FS64_TO_CPU(bn->overflow_link, fs_le);
+		else {
+			while (first <= last) {
+				mid = (first + last) / 2;
+
+				cmp = compare_keys(bn->name, keylengths, mid,
+						key, strlen(key), fs_le);
+				if (cmp == 0) {
+					if ((int64_t) FS64_TO_CPU(bn->overflow_link,
+						fs_le) == BPLUSTREE_NULL)
+						return FS64_TO_CPU(values[mid],
+									fs_le);
+					else
+						break;
+				} else if (cmp < 0)
+					first = mid + 1;
+				else
+					last = mid - 1;
+			}
+			if (cmp < 0)
+				node_pointer = FS64_TO_CPU(values[mid + 1],
+									fs_le);
+			else
+				node_pointer = FS64_TO_CPU(values[mid], fs_le);
+		}
+	} while ((int64_t) FS64_TO_CPU(bn->overflow_link, fs_le)
+						!= BPLUSTREE_NULL);
+	return 0;
+}
+
+static int get_uuid(blkid_probe pr, const struct befs_super_block *bs,
+					uint64_t * const uuid, int fs_le)
+{
+	struct befs_inode *bi;
+	struct small_data *sd;
+
+	bi = (struct befs_inode *) get_block_run(pr, bs, &bs->root_dir, fs_le);
+	if (!bi)
+		return -1;
+
+	if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
+		return -1;
+
+	sd = (struct small_data *) bi->small_data;
+
+	do {
+		if (FS32_TO_CPU(sd->type, fs_le) == B_UINT64_TYPE
+			&& FS16_TO_CPU(sd->name_size, fs_le) == strlen(KEY_NAME)
+			&& FS16_TO_CPU(sd->data_size, fs_le) == KEY_SIZE
+			&& strcmp(sd->name, KEY_NAME) == 0) {
+
+			memcpy(uuid,
+			       sd->name + FS16_TO_CPU(sd->name_size, fs_le) + 3,
+			       sizeof(uint64_t));
+
+			break;
+		} else if (FS32_TO_CPU(sd->type, fs_le) == 0
+				&& FS16_TO_CPU(sd->name_size, fs_le) == 0
+				&& FS16_TO_CPU(sd->data_size, fs_le) == 0)
+			break;
+
+		sd = (struct small_data *) ((uint8_t *) sd
+				+ sizeof(struct small_data)
+				+ FS16_TO_CPU(sd->name_size, fs_le) + 3
+				+ FS16_TO_CPU(sd->data_size, fs_le) + 1);
+
+	} while ((intptr_t) sd < (intptr_t) bi
+				+ (int32_t) FS32_TO_CPU(bi->inode_size, fs_le)
+				- (int32_t) sizeof(struct small_data));
+	if (*uuid == 0
+		&& (FS32_TO_CPU(bi->attributes.allocation_group, fs_le) != 0
+			|| FS16_TO_CPU(bi->attributes.start, fs_le) != 0
+			|| FS16_TO_CPU(bi->attributes.len, fs_le) != 0)) {
+		int64_t value;
+
+		bi = (struct befs_inode *) get_block_run(pr, bs,
+							&bi->attributes, fs_le);
+		if (!bi)
+			return -1;
+
+		if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
+			return -1;
+
+		value = get_key_value(pr, bs, bi, KEY_NAME, fs_le);
+
+		if (value < 0)
+			return value;
+		else if (value > 0) {
+			bi = (struct befs_inode *) blkid_probe_get_buffer(pr,
+				value << FS32_TO_CPU(bs->block_shift, fs_le),
+				FS32_TO_CPU(bs->block_size, fs_le));
+			if (!bi)
+				return -1;
+
+			if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
+				return -1;
+
+			if (FS32_TO_CPU(bi->type, fs_le) == B_UINT64_TYPE
+				&& FS64_TO_CPU(bi->data.size, fs_le) == KEY_SIZE
+				&& FS16_TO_CPU(bi->data.direct[0].len, fs_le)
+									== 1) {
+				uint64_t *attr_data;
+
+				attr_data = (uint64_t *) get_block_run(pr, bs,
+						&bi->data.direct[0], fs_le);
+				if (!attr_data)
+					return -1;
+
+				*uuid = *attr_data;
+			}
+		}
+	}
+	return 0;
+}
+
+static int probe_befs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct befs_super_block *bs;
+	const char *version = NULL;
+	uint64_t volume_id = 0;
+	int fs_le, ret;
+
+	bs = (struct befs_super_block *) blkid_probe_get_buffer(pr,
+					mag->sboff - B_OS_NAME_LENGTH,
+					sizeof(struct befs_super_block));
+	if (!bs)
+		return -1;
+
+	if (le32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1
+		&& le32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2
+		&& le32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3
+		&& le32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) {
+		fs_le = 1;
+		version = "little-endian";
+	} else if (be32_to_cpu(bs->magic1) == SUPER_BLOCK_MAGIC1
+		&& be32_to_cpu(bs->magic2) == SUPER_BLOCK_MAGIC2
+		&& be32_to_cpu(bs->magic3) == SUPER_BLOCK_MAGIC3
+		&& be32_to_cpu(bs->fs_byte_order) == SUPER_BLOCK_FS_ENDIAN) {
+		fs_le = 0;
+		version = "big-endian";
+	} else
+		return -1;
+
+	ret = get_uuid(pr, bs, &volume_id, fs_le);
+
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * all checks pass, set LABEL, VERSION and UUID
+	 */
+	if (strlen(bs->name))
+		blkid_probe_set_label(pr, (unsigned char *) bs->name,
+							sizeof(bs->name));
+	if (version)
+		blkid_probe_set_version(pr, version);
+
+	if (volume_id)
+		blkid_probe_sprintf_uuid(pr, (unsigned char *) &volume_id,
+					sizeof(volume_id), "%016" PRIx64,
+					FS64_TO_CPU(volume_id, fs_le));
+	return 0;
+}
+
+const struct blkid_idinfo befs_idinfo =
+{
+	.name		= "befs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_befs,
+	.minsz		= 1024 * 1440,
+	.magics		= {
+		{ .magic = "BFS1", .len = 4, .sboff = B_OS_NAME_LENGTH },
+		{ .magic = "1SFB", .len = 4, .sboff = B_OS_NAME_LENGTH },
+		{ .magic = "BFS1", .len = 4, .sboff = 0x200 +
+							B_OS_NAME_LENGTH },
+		{ .magic = "1SFB", .len = 4, .sboff = 0x200 +
+							B_OS_NAME_LENGTH },
+		{ NULL }
+	}
+};
diff --git a/src/superblocks/bfs.c b/src/superblocks/bfs.c
new file mode 100644
index 0000000..8a34c58
--- /dev/null
+++ b/src/superblocks/bfs.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include "superblocks.h"
+
+/*
+ * BFS actually has two different labels in the superblock, each
+ * of them only 6 bytes long.  Until we find out what their use
+ * we just ignore them.
+ */
+const struct blkid_idinfo bfs_idinfo =
+{
+	.name		= "bfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.magics		= {
+		{ .magic = "\xce\xfa\xad\x1b", .len = 4 },
+		{ NULL }
+	}
+};
diff --git a/src/superblocks/btrfs.c b/src/superblocks/btrfs.c
new file mode 100644
index 0000000..039be42
--- /dev/null
+++ b/src/superblocks/btrfs.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct btrfs_super_block {
+	uint8_t csum[32];
+	uint8_t fsid[16];
+	uint64_t bytenr;
+	uint64_t flags;
+	uint8_t magic[8];
+	uint64_t generation;
+	uint64_t root;
+	uint64_t chunk_root;
+	uint64_t log_root;
+	uint64_t log_root_transid;
+	uint64_t total_bytes;
+	uint64_t bytes_used;
+	uint64_t root_dir_objectid;
+	uint64_t num_devices;
+	uint32_t sectorsize;
+	uint32_t nodesize;
+	uint32_t leafsize;
+	uint32_t stripesize;
+	uint32_t sys_chunk_array_size;
+	uint64_t chunk_root_generation;
+	uint64_t compat_flags;
+	uint64_t compat_ro_flags;
+	uint64_t incompat_flags;
+	uint16_t csum_type;
+	uint8_t root_level;
+	uint8_t chunk_root_level;
+	uint8_t log_root_level;
+	struct btrfs_dev_item {
+		uint64_t devid;
+		uint64_t total_bytes;
+		uint64_t bytes_used;
+		uint32_t io_align;
+		uint32_t io_width;
+		uint32_t sector_size;
+		uint64_t type;
+		uint64_t generation;
+		uint64_t start_offset;
+		uint32_t dev_group;
+		uint8_t seek_speed;
+		uint8_t bandwidth;
+		uint8_t uuid[16];
+		uint8_t fsid[16];
+	} __attribute__ ((__packed__)) dev_item;
+	uint8_t label[256];
+} __attribute__ ((__packed__));
+
+static int probe_btrfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct btrfs_super_block *bfs;
+
+	bfs = blkid_probe_get_sb(pr, mag, struct btrfs_super_block);
+	if (!bfs)
+		return -1;
+
+	if (*bfs->label)
+		blkid_probe_set_label(pr,
+				(unsigned char *) bfs->label,
+				sizeof(bfs->label));
+
+	blkid_probe_set_uuid(pr, bfs->fsid);
+	blkid_probe_set_uuid_as(pr, bfs->dev_item.uuid, "UUID_SUB");
+
+	return 0;
+}
+
+const struct blkid_idinfo btrfs_idinfo =
+{
+	.name		= "btrfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_btrfs,
+	.minsz		= 1024 * 1024,
+	.magics		=
+	{
+		{ .magic = "_BHRfS_M", .len = 8, .kboff = 64, .sboff = 0x40 },
+		{ NULL }
+	}
+};
+
diff --git a/src/superblocks/cramfs.c b/src/superblocks/cramfs.c
new file mode 100644
index 0000000..b58ed08
--- /dev/null
+++ b/src/superblocks/cramfs.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct cramfs_super
+{
+	uint8_t		magic[4];
+	uint32_t	size;
+	uint32_t	flags;
+	uint32_t	future;
+	uint8_t		signature[16];
+	struct cramfs_info
+	{
+		uint32_t	crc;
+		uint32_t	edition;
+		uint32_t	blocks;
+		uint32_t	files;
+	} __attribute__((packed)) info;
+	uint8_t		name[16];
+} __attribute__((packed));
+
+static int probe_cramfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct cramfs_super *cs;
+
+	cs = blkid_probe_get_sb(pr, mag, struct cramfs_super);
+	if (!cs)
+		return -1;
+
+	blkid_probe_set_label(pr, cs->name, sizeof(cs->name));
+	return 0;
+}
+
+const struct blkid_idinfo cramfs_idinfo =
+{
+	.name		= "cramfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_cramfs,
+	.magics		=
+	{
+		{ "\x45\x3d\xcd\x28", 4, 0, 0 },
+		{ "\x28\xcd\x3d\x45", 4, 0, 0 },
+		{ NULL }
+	}
+};
+
+
diff --git a/src/superblocks/ddf_raid.c b/src/superblocks/ddf_raid.c
new file mode 100644
index 0000000..24df421
--- /dev/null
+++ b/src/superblocks/ddf_raid.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+/* http://www.snia.org/standards/home */
+#define DDF_GUID_LENGTH			24
+#define DDF_REV_LENGTH			8
+#define DDF_MAGIC			0xDE11DE11
+
+
+struct ddf_header {
+	uint32_t	signature;
+	uint32_t	crc;
+	uint8_t		guid[DDF_GUID_LENGTH];
+	char		ddf_rev[8];	/* 01.02.00 */
+	uint32_t	seq;		/* starts at '1' */
+	uint32_t	timestamp;
+	uint8_t		openflag;
+	uint8_t		foreignflag;
+	uint8_t		enforcegroups;
+	uint8_t		pad0;		/* 0xff */
+	uint8_t		pad1[12];	/* 12 * 0xff */
+	/* 64 bytes so far */
+	uint8_t		header_ext[32];	/* reserved: fill with 0xff */
+	uint64_t	primary_lba;
+	uint64_t	secondary_lba;
+	uint8_t		type;
+	uint8_t		pad2[3];	/* 0xff */
+	uint32_t	workspace_len;	/* sectors for vendor space -
+					 * at least 32768(sectors) */
+	uint64_t	workspace_lba;
+	uint16_t	max_pd_entries;	/* one of 15, 63, 255, 1023, 4095 */
+	uint16_t	max_vd_entries; /* 2^(4,6,8,10,12)-1 : i.e. as above */
+	uint16_t	max_partitions; /* i.e. max num of configuration
+					   record entries per disk */
+	uint16_t	config_record_len; /* 1 +ROUNDUP(max_primary_element_entries
+				           *12/512) */
+	uint16_t	max_primary_element_entries; /* 16, 64, 256, 1024, or 4096 */
+	uint8_t		pad3[54];	/* 0xff */
+	/* 192 bytes so far */
+	uint32_t	controller_section_offset;
+	uint32_t	controller_section_length;
+	uint32_t	phys_section_offset;
+	uint32_t	phys_section_length;
+	uint32_t	virt_section_offset;
+	uint32_t	virt_section_length;
+	uint32_t	config_section_offset;
+	uint32_t	config_section_length;
+	uint32_t	data_section_offset;
+	uint32_t	data_section_length;
+	uint32_t	bbm_section_offset;
+	uint32_t	bbm_section_length;
+	uint32_t	diag_space_offset;
+	uint32_t	diag_space_length;
+	uint32_t	vendor_offset;
+	uint32_t	vendor_length;
+	/* 256 bytes so far */
+	uint8_t		pad4[256];	/* 0xff */
+} __attribute__((packed));
+
+static int probe_ddf(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	int hdrs[] = { 1, 257 };
+	size_t i;
+	struct ddf_header *ddf = NULL;
+	char version[DDF_REV_LENGTH + 1];
+	uint64_t off, lba;
+
+	if (pr->size < 0x30000)
+		return -1;
+
+	for (i = 0; i < ARRAY_SIZE(hdrs); i++) {
+		off = ((pr->size / 0x200) - hdrs[i]) * 0x200;
+
+		ddf = (struct ddf_header *) blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct ddf_header));
+		if (!ddf)
+			return -1;
+
+		if (ddf->signature == cpu_to_be32(DDF_MAGIC) ||
+		    ddf->signature == cpu_to_le32(DDF_MAGIC))
+			break;
+		ddf = NULL;
+	}
+
+	if (!ddf)
+		return -1;
+
+	lba = ddf->signature == cpu_to_be32(DDF_MAGIC) ?
+			be64_to_cpu(ddf->primary_lba) :
+			le64_to_cpu(ddf->primary_lba);
+
+	if (lba > 0) {
+		/* check primary header */
+		unsigned char *buf;
+
+		buf = blkid_probe_get_buffer(pr,
+					lba << 9, sizeof(ddf->signature));
+		if (!buf || memcmp(buf, &ddf->signature, 4))
+			return -1;
+	}
+
+	blkid_probe_strncpy_uuid(pr, ddf->guid, sizeof(ddf->guid));
+
+	memcpy(version, ddf->ddf_rev, sizeof(ddf->ddf_rev));
+	*(version + sizeof(ddf->ddf_rev)) = '\0';
+
+	if (blkid_probe_set_version(pr, version) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off,
+			sizeof(ddf->signature),
+			(unsigned char *) &ddf->signature))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo ddfraid_idinfo = {
+	.name		= "ddf_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_ddf,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/src/superblocks/drbd.c b/src/superblocks/drbd.c
new file mode 100644
index 0000000..43e544e
--- /dev/null
+++ b/src/superblocks/drbd.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2009 by Bastian Friedrich <bastian.friedrich@collax.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * defines, structs taken from drbd source; file names represent drbd source
+ * files.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stddef.h>
+
+#include "superblocks.h"
+
+/*
+ * drbd/linux/drbd.h
+ */
+#define DRBD_MAGIC 0x83740267
+
+/*
+ * user/drbdmeta.c
+ * We only support v08 for now
+ */
+#define DRBD_MD_MAGIC_08         (DRBD_MAGIC+4)
+#define DRBD_MD_MAGIC_84_UNCLEAN (DRBD_MAGIC+5)
+
+/*
+ * drbd/linux/drbd.h
+ */
+enum drbd_uuid_index {
+	UI_CURRENT,
+	UI_BITMAP,
+	UI_HISTORY_START,
+	UI_HISTORY_END,
+	UI_SIZE,		/* nl-packet: number of dirty bits */
+	UI_FLAGS,		/* nl-packet: flags */
+	UI_EXTENDED_SIZE	/* Everything. */
+};
+
+/*
+ * user/drbdmeta.c
+ * Minor modifications wrt. types
+ */
+struct md_on_disk_08 {
+	uint64_t la_sect;         /* last agreed size. */
+	uint64_t uuid[UI_SIZE];   /* UUIDs */
+	uint64_t device_uuid;
+	uint64_t reserved_u64_1;
+	uint32_t flags;
+	uint32_t magic;
+	uint32_t md_size_sect;
+	int32_t  al_offset;       /* signed sector offset to this block */
+	uint32_t al_nr_extents;   /* important for restoring the AL */
+	int32_t  bm_offset;       /* signed sector offset to the bitmap, from here */
+	uint32_t bm_bytes_per_bit;
+	uint32_t reserved_u32[4];
+
+	char reserved[8 * 512 - (8*(UI_SIZE+3)+4*11)];
+};
+
+
+static int probe_drbd(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct md_on_disk_08 *md;
+	off_t off;
+
+	off = pr->size - sizeof(*md);
+
+	/* Small devices cannot be drbd (?) */
+	if (pr->size < 0x10000)
+		return -1;
+
+	md = (struct md_on_disk_08 *)
+			blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct md_on_disk_08));
+	if (!md)
+		return -1;
+
+	if (be32_to_cpu(md->magic) != DRBD_MD_MAGIC_08 &&
+			be32_to_cpu(md->magic) != DRBD_MD_MAGIC_84_UNCLEAN)
+		return -1;
+
+	/*
+	 * DRBD does not have "real" uuids; the following resembles DRBD's
+	 * notion of uuids (64 bit, see struct above)
+	 */
+	blkid_probe_sprintf_uuid(pr,
+		(unsigned char *) &md->device_uuid, sizeof(md->device_uuid),
+		"%" PRIx64, be64_to_cpu(md->device_uuid));
+
+	blkid_probe_set_version(pr, "v08");
+
+	if (blkid_probe_set_magic(pr,
+				off + offsetof(struct md_on_disk_08, magic),
+				sizeof(md->magic),
+				(unsigned char *) &md->magic))
+		return -1;
+
+	return 0;
+}
+
+const struct blkid_idinfo drbd_idinfo =
+{
+	.name		= "drbd",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_drbd,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/src/superblocks/drbdproxy_datalog.c b/src/superblocks/drbdproxy_datalog.c
new file mode 100644
index 0000000..afe4725
--- /dev/null
+++ b/src/superblocks/drbdproxy_datalog.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 by Philipp Marek <philipp.marek@linbit.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stddef.h>
+
+#include "superblocks.h"
+
+
+struct log_header_t {
+	uint64_t magic;
+	uint64_t version;
+
+	unsigned char uuid[16];
+
+	uint64_t flags;
+} __attribute__((packed));
+
+
+static int probe_drbdproxy_datalog(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct log_header_t *lh;
+
+	lh = (struct log_header_t *) blkid_probe_get_buffer(pr, 0, sizeof(*lh));
+	if (!lh)
+		return -1;
+
+	blkid_probe_set_uuid(pr, lh->uuid);
+	blkid_probe_sprintf_version(pr, "v%jd", le64_to_cpu(lh->version));
+
+	return 0;
+}
+
+const struct blkid_idinfo drbdproxy_datalog_idinfo =
+{
+	.name		= "drbdproxy_datalog",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_drbdproxy_datalog,
+	.minsz		= 16*1024,
+	.magics		=
+	{
+		{ .magic = "DRBDdlh*", .len = 8, .sboff = 0, .kboff = 0 },
+		{ NULL }
+	}
+};
diff --git a/src/superblocks/exfat.c b/src/superblocks/exfat.c
new file mode 100644
index 0000000..215c671
--- /dev/null
+++ b/src/superblocks/exfat.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010 Andrew Nayenko <resver@gmail.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include "superblocks.h"
+
+struct exfat_super_block {
+	uint8_t jump[3];
+	uint8_t oem_name[8];
+	uint8_t	__unused1[53];
+	uint64_t block_start;
+	uint64_t block_count;
+	uint32_t fat_block_start;
+	uint32_t fat_block_count;
+	uint32_t cluster_block_start;
+	uint32_t cluster_count;
+	uint32_t rootdir_cluster;
+	uint8_t volume_serial[4];
+	struct {
+		uint8_t minor;
+		uint8_t major;
+	} version;
+	uint16_t volume_state;
+	uint8_t block_bits;
+	uint8_t bpc_bits;
+	uint8_t fat_count;
+	uint8_t drive_no;
+	uint8_t allocated_percent;
+} __attribute__((__packed__));
+
+struct exfat_entry_label {
+	uint8_t type;
+	uint8_t length;
+	uint8_t name[30];
+} __attribute__((__packed__));
+
+#define BLOCK_SIZE(sb) (1 << (sb)->block_bits)
+#define CLUSTER_SIZE(sb) (BLOCK_SIZE(sb) << (sb)->bpc_bits)
+#define EXFAT_FIRST_DATA_CLUSTER 2
+#define EXFAT_LAST_DATA_CLUSTER 0xffffff6
+#define EXFAT_ENTRY_SIZE 32
+
+#define EXFAT_ENTRY_EOD		0x00
+#define EXFAT_ENTRY_LABEL	0x83
+
+static blkid_loff_t block_to_offset(const struct exfat_super_block *sb,
+		blkid_loff_t block)
+{
+	return (blkid_loff_t) block << sb->block_bits;
+}
+
+static blkid_loff_t cluster_to_block(const struct exfat_super_block *sb,
+		uint32_t cluster)
+{
+	return le32_to_cpu(sb->cluster_block_start) +
+			((blkid_loff_t) (cluster - EXFAT_FIRST_DATA_CLUSTER)
+					<< sb->bpc_bits);
+}
+
+static blkid_loff_t cluster_to_offset(const struct exfat_super_block *sb,
+		uint32_t cluster)
+{
+	return block_to_offset(sb, cluster_to_block(sb, cluster));
+}
+
+static uint32_t next_cluster(blkid_probe pr,
+		const struct exfat_super_block *sb, uint32_t cluster)
+{
+	uint32_t *next;
+	blkid_loff_t fat_offset;
+
+	fat_offset = block_to_offset(sb, le32_to_cpu(sb->fat_block_start))
+		+ (blkid_loff_t) cluster * sizeof(cluster);
+	next = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset,
+			sizeof(uint32_t));
+	if (!next)
+		return 0;
+	return le32_to_cpu(*next);
+}
+
+static struct exfat_entry_label *find_label(blkid_probe pr,
+		const struct exfat_super_block *sb)
+{
+	uint32_t cluster = le32_to_cpu(sb->rootdir_cluster);
+	blkid_loff_t offset = cluster_to_offset(sb, cluster);
+	uint8_t *entry;
+
+	for (;;) {
+		entry = (uint8_t *) blkid_probe_get_buffer(pr, offset,
+				EXFAT_ENTRY_SIZE);
+		if (!entry)
+			return NULL;
+		if (entry[0] == EXFAT_ENTRY_EOD)
+			return NULL;
+		if (entry[0] == EXFAT_ENTRY_LABEL)
+			return (struct exfat_entry_label *) entry;
+		offset += EXFAT_ENTRY_SIZE;
+		if (offset % CLUSTER_SIZE(sb) == 0) {
+			cluster = next_cluster(pr, sb, cluster);
+			if (cluster < EXFAT_FIRST_DATA_CLUSTER)
+				return NULL;
+			if (cluster > EXFAT_LAST_DATA_CLUSTER)
+				return NULL;
+			offset = cluster_to_offset(sb, cluster);
+		}
+	}
+}
+
+static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct exfat_super_block *sb;
+	struct exfat_entry_label *label;
+
+	sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block);
+	if (!sb)
+		return -1;
+
+	label = find_label(pr, sb);
+	if (label)
+		blkid_probe_set_utf8label(pr, label->name,
+				min(label->length * 2, 30), BLKID_ENC_UTF16LE);
+
+	blkid_probe_sprintf_uuid(pr, sb->volume_serial, 4,
+			"%02hhX%02hhX-%02hhX%02hhX",
+			sb->volume_serial[3], sb->volume_serial[2],
+			sb->volume_serial[1], sb->volume_serial[0]);
+
+	blkid_probe_sprintf_version(pr, "%u.%u",
+			sb->version.major, sb->version.minor);
+
+	return 0;
+}
+
+const struct blkid_idinfo exfat_idinfo =
+{
+	.name		= "exfat",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_exfat,
+	.magics		=
+	{
+		{ .magic = "EXFAT   ", .len = 8, .sboff = 3 },
+		{ NULL }
+	}
+};
diff --git a/src/superblocks/ext.c b/src/superblocks/ext.c
new file mode 100644
index 0000000..eff96a0
--- /dev/null
+++ b/src/superblocks/ext.c
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 1999, 2001 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+#ifdef __linux__
+#include <sys/utsname.h>
+#endif
+#include <time.h>
+
+#include "linux_version.h"
+#include "superblocks.h"
+
+struct ext2_super_block {
+	uint32_t		s_inodes_count;
+	uint32_t		s_blocks_count;
+	uint32_t		s_r_blocks_count;
+	uint32_t		s_free_blocks_count;
+	uint32_t		s_free_inodes_count;
+	uint32_t		s_first_data_block;
+	uint32_t		s_log_block_size;
+	uint32_t		s_dummy3[7];
+	unsigned char		s_magic[2];
+	uint16_t		s_state;
+	uint16_t		s_errors;
+	uint16_t		s_minor_rev_level;
+	uint32_t		s_lastcheck;
+	uint32_t		s_checkinterval;
+	uint32_t		s_creator_os;
+	uint32_t		s_rev_level;
+	uint16_t		s_def_resuid;
+	uint16_t		s_def_resgid;
+	uint32_t		s_first_ino;
+	uint16_t		s_inode_size;
+	uint16_t		s_block_group_nr;
+	uint32_t		s_feature_compat;
+	uint32_t		s_feature_incompat;
+	uint32_t		s_feature_ro_compat;
+	unsigned char		s_uuid[16];
+	char			s_volume_name[16];
+	char			s_last_mounted[64];
+	uint32_t		s_algorithm_usage_bitmap;
+	uint8_t			s_prealloc_blocks;
+	uint8_t			s_prealloc_dir_blocks;
+	uint16_t		s_reserved_gdt_blocks;
+	uint8_t			s_journal_uuid[16];
+	uint32_t		s_journal_inum;
+	uint32_t		s_journal_dev;
+	uint32_t		s_last_orphan;
+	uint32_t		s_hash_seed[4];
+	uint8_t			s_def_hash_version;
+	uint8_t			s_jnl_backup_type;
+	uint16_t		s_reserved_word_pad;
+	uint32_t		s_default_mount_opts;
+	uint32_t		s_first_meta_bg;
+	uint32_t		s_mkfs_time;
+	uint32_t		s_jnl_blocks[17];
+	uint32_t		s_blocks_count_hi;
+	uint32_t		s_r_blocks_count_hi;
+	uint32_t		s_free_blocks_hi;
+	uint16_t		s_min_extra_isize;
+	uint16_t		s_want_extra_isize;
+	uint32_t		s_flags;
+	uint16_t		s_raid_stride;
+	uint16_t		s_mmp_interval;
+	uint64_t		s_mmp_block;
+	uint32_t		s_raid_stripe_width;
+	uint32_t		s_reserved[163];
+} __attribute__((packed));
+
+/* magic string */
+#define EXT_SB_MAGIC				"\123\357"
+/* supper block offset */
+#define EXT_SB_OFF				0x400
+/* supper block offset in kB */
+#define EXT_SB_KBOFF				(EXT_SB_OFF >> 10)
+/* magic string offset within super block */
+#define EXT_MAG_OFF				0x38
+
+
+
+/* for s_flags */
+#define EXT2_FLAGS_TEST_FILESYS		0x0004
+
+/* for s_feature_compat */
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL		0x0004
+
+/* for s_feature_ro_compat */
+#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER	0x0001
+#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE	0x0002
+#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR	0x0004
+#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE	0x0008
+#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM		0x0010
+#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK	0x0020
+#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE	0x0040
+
+/* for s_feature_incompat */
+#define EXT2_FEATURE_INCOMPAT_FILETYPE		0x0002
+#define EXT3_FEATURE_INCOMPAT_RECOVER		0x0004
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV	0x0008
+#define EXT2_FEATURE_INCOMPAT_META_BG		0x0010
+#define EXT4_FEATURE_INCOMPAT_EXTENTS		0x0040 /* extents support */
+#define EXT4_FEATURE_INCOMPAT_64BIT		0x0080
+#define EXT4_FEATURE_INCOMPAT_MMP		0x0100
+#define EXT4_FEATURE_INCOMPAT_FLEX_BG		0x0200
+
+#define EXT2_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+					 EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+#define EXT2_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE| \
+					 EXT2_FEATURE_INCOMPAT_META_BG)
+#define EXT2_FEATURE_INCOMPAT_UNSUPPORTED	~EXT2_FEATURE_INCOMPAT_SUPP
+#define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED	~EXT2_FEATURE_RO_COMPAT_SUPP
+
+#define EXT3_FEATURE_RO_COMPAT_SUPP	(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+					 EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+					 EXT2_FEATURE_RO_COMPAT_BTREE_DIR)
+#define EXT3_FEATURE_INCOMPAT_SUPP	(EXT2_FEATURE_INCOMPAT_FILETYPE| \
+					 EXT3_FEATURE_INCOMPAT_RECOVER| \
+					 EXT2_FEATURE_INCOMPAT_META_BG)
+#define EXT3_FEATURE_INCOMPAT_UNSUPPORTED	~EXT3_FEATURE_INCOMPAT_SUPP
+#define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED	~EXT3_FEATURE_RO_COMPAT_SUPP
+
+/*
+ * Check to see if a filesystem is in /proc/filesystems.
+ * Returns 1 if found, 0 if not
+ */
+static int fs_proc_check(const char *fs_name)
+{
+	FILE	*f;
+	char	buf[80], *cp, *t;
+
+	f = fopen("/proc/filesystems", "r");
+	if (!f)
+		return 0;
+	while (!feof(f)) {
+		if (!fgets(buf, sizeof(buf), f))
+			break;
+		cp = buf;
+		if (!isspace(*cp)) {
+			while (*cp && !isspace(*cp))
+				cp++;
+		}
+		while (*cp && isspace(*cp))
+			cp++;
+		if ((t = strchr(cp, '\n')) != NULL)
+			*t = 0;
+		if ((t = strchr(cp, '\t')) != NULL)
+			*t = 0;
+		if ((t = strchr(cp, ' ')) != NULL)
+			*t = 0;
+		if (!strcmp(fs_name, cp)) {
+			fclose(f);
+			return 1;
+		}
+	}
+	fclose(f);
+	return (0);
+}
+
+/*
+ * Check to see if a filesystem is available as a module
+ * Returns 1 if found, 0 if not
+ */
+static int check_for_modules(const char *fs_name)
+{
+#ifdef __linux__
+	struct utsname	uts;
+	FILE		*f;
+	char		buf[1024], *cp;
+	int		namesz;
+
+	if (uname(&uts))
+		return 0;
+	snprintf(buf, sizeof(buf), "/lib/modules/%s/modules.dep", uts.release);
+
+	f = fopen(buf, "r");
+	if (!f)
+		return 0;
+
+	namesz = strlen(fs_name);
+
+	while (!feof(f)) {
+		if (!fgets(buf, sizeof(buf), f))
+			break;
+		if ((cp = strchr(buf, ':')) != NULL)
+			*cp = 0;
+		else
+			continue;
+		if ((cp = strrchr(buf, '/')) == NULL)
+			continue;
+		cp++;
+
+		if (!strncmp(cp, fs_name, namesz) &&
+		    (!strcmp(cp + namesz, ".ko") ||
+		     !strcmp(cp + namesz, ".ko.gz"))) {
+			fclose(f);
+			return 1;
+		}
+	}
+	fclose(f);
+#endif /* __linux__ */
+	return 0;
+}
+
+/*
+ * Starting in 2.6.29, ext4 can be used to support filesystems
+ * without a journal.
+ */
+#define EXT4_SUPPORTS_EXT2 KERNEL_VERSION(2, 6, 29)
+
+static int system_supports_ext2(void)
+{
+	static time_t	last_check = 0;
+	static int	ret = -1;
+	time_t		now = time(0);
+
+	if (ret != -1 || (now - last_check) < 5)
+		return ret;
+	last_check = now;
+	ret = (fs_proc_check("ext2") || check_for_modules("ext2"));
+	return ret;
+}
+
+static int system_supports_ext4(void)
+{
+	static time_t	last_check = 0;
+	static int	ret = -1;
+	time_t		now = time(0);
+
+	if (ret != -1 || (now - last_check) < 5)
+		return ret;
+	last_check = now;
+	ret = (fs_proc_check("ext4") || check_for_modules("ext4"));
+	return ret;
+}
+
+static int system_supports_ext4dev(void)
+{
+	static time_t	last_check = 0;
+	static int	ret = -1;
+	time_t		now = time(0);
+
+	if (ret != -1 || (now - last_check) < 5)
+		return ret;
+	last_check = now;
+	ret = (fs_proc_check("ext4dev") || check_for_modules("ext4dev"));
+	return ret;
+}
+
+static int system_supports_ext4_ext2(void)
+{
+#ifdef __linux__
+	return get_linux_version() >= EXT4_SUPPORTS_EXT2;
+#else
+	return 0;
+#endif
+}
+/*
+ * reads superblock and returns:
+ *	fc = feature_compat
+ *	fi = feature_incompat
+ *	frc = feature_ro_compat
+ */
+static struct ext2_super_block *ext_get_super(
+		blkid_probe pr, uint32_t *fc, uint32_t *fi, uint32_t *frc)
+{
+	struct ext2_super_block *es;
+
+	es = (struct ext2_super_block *)
+			blkid_probe_get_buffer(pr, EXT_SB_OFF, 0x200);
+	if (!es)
+		return NULL;
+	if (fc)
+		*fc = le32_to_cpu(es->s_feature_compat);
+	if (fi)
+		*fi = le32_to_cpu(es->s_feature_incompat);
+	if (frc)
+		*frc = le32_to_cpu(es->s_feature_ro_compat);
+
+	return es;
+}
+
+static void ext_get_info(blkid_probe pr, int ver, struct ext2_super_block *es)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n",
+		   le32_to_cpu(es->s_feature_compat),
+		   le32_to_cpu(es->s_feature_incompat),
+		   le32_to_cpu(es->s_feature_ro_compat)));
+
+	if (strlen(es->s_volume_name))
+		blkid_probe_set_label(pr, (unsigned char *) es->s_volume_name,
+					sizeof(es->s_volume_name));
+	blkid_probe_set_uuid(pr, es->s_uuid);
+
+	if (le32_to_cpu(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
+		blkid_probe_set_uuid_as(pr, es->s_journal_uuid, "EXT_JOURNAL");
+
+	if (ver != 2 && (chn->flags & BLKID_SUBLKS_SECTYPE) &&
+	    ((le32_to_cpu(es->s_feature_incompat) & EXT2_FEATURE_INCOMPAT_UNSUPPORTED) == 0))
+		blkid_probe_set_value(pr, "SEC_TYPE",
+				(unsigned char *) "ext2",
+				sizeof("ext2"));
+
+	blkid_probe_sprintf_version(pr, "%u.%u",
+		le32_to_cpu(es->s_rev_level),
+		le16_to_cpu(es->s_minor_rev_level));
+}
+
+
+static int probe_jbd(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct ext2_super_block *es;
+	uint32_t fi;
+
+	es = ext_get_super(pr, NULL, &fi, NULL);
+	if (!es)
+		return -BLKID_ERR_PARAM;
+	if (!(fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV))
+		return -BLKID_ERR_PARAM;
+
+	ext_get_info(pr, 2, es);
+	return 0;
+}
+
+static int probe_ext2(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct ext2_super_block *es;
+	uint32_t fc, frc, fi;
+
+	es = ext_get_super(pr, &fc, &fi, &frc);
+	if (!es)
+		return -BLKID_ERR_PARAM;
+
+	/* Distinguish between ext3 and ext2 */
+	if (fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
+		return -BLKID_ERR_PARAM;
+
+	/* Any features which ext2 doesn't understand */
+	if ((frc & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) ||
+	    (fi  & EXT2_FEATURE_INCOMPAT_UNSUPPORTED))
+		return -BLKID_ERR_PARAM;
+
+	/*
+	 * If ext2 is not present, but ext4 or ext4dev are, then
+	 * disclaim we are ext2
+	 */
+	if (!system_supports_ext2() &&
+	    (system_supports_ext4() || system_supports_ext4dev()) &&
+	    system_supports_ext4_ext2())
+		return -BLKID_ERR_PARAM;
+
+	ext_get_info(pr, 2, es);
+	return 0;
+}
+
+static int probe_ext3(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct ext2_super_block *es;
+	uint32_t fc, frc, fi;
+
+	es = ext_get_super(pr, &fc, &fi, &frc);
+	if (!es)
+		return -BLKID_ERR_PARAM;
+
+	/* ext3 requires journal */
+	if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+		return -BLKID_ERR_PARAM;
+
+	/* Any features which ext3 doesn't understand */
+	if ((frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) ||
+	    (fi  & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
+		return -BLKID_ERR_PARAM;
+
+	ext_get_info(pr, 3, es);
+	return 0;
+}
+
+
+static int probe_ext4dev(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct ext2_super_block *es;
+	uint32_t fc, frc, fi;
+
+	es = ext_get_super(pr, &fc, &fi, &frc);
+	if (!es)
+		return -BLKID_ERR_PARAM;
+
+	/* Distinguish from jbd */
+	if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+		return -BLKID_ERR_PARAM;
+
+	/*
+	 * If the filesystem does not have a journal and ext2 and ext4
+	 * is not present, then force this to be detected as an
+	 * ext4dev filesystem.
+	 */
+	if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
+	    !system_supports_ext2() && !system_supports_ext4() &&
+	    system_supports_ext4dev() &&
+	    system_supports_ext4_ext2())
+		goto force_ext4dev;
+
+	/*
+	 * If the filesystem is marked as OK for use by in-development
+	 * filesystem code, but ext4dev is not supported, and ext4 is,
+	 * then don't call ourselves ext4dev, since we should be
+	 * detected as ext4 in that case.
+	 *
+	 * If the filesystem is marked as in use by production
+	 * filesystem, then it can only be used by ext4 and NOT by
+	 * ext4dev, so always disclaim we are ext4dev in that case.
+	 */
+	if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) {
+		if (!system_supports_ext4dev() && system_supports_ext4())
+			return -BLKID_ERR_PARAM;
+	} else
+		return -BLKID_ERR_PARAM;
+
+force_ext4dev:
+	ext_get_info(pr, 4, es);
+	return 0;
+}
+
+static int probe_ext4(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct ext2_super_block *es;
+	uint32_t fc, frc, fi;
+
+	es = ext_get_super(pr, &fc, &fi, &frc);
+	if (!es)
+		return -1;
+
+	/* Distinguish from jbd */
+	if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+		return -BLKID_ERR_PARAM;
+
+	/*
+	 * If the filesystem does not have a journal and ext2 is not
+	 * present, then force this to be detected as an ext2
+	 * filesystem.
+	 */
+	if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
+	    !system_supports_ext2() && system_supports_ext4() &&
+	    system_supports_ext4_ext2())
+		goto force_ext4;
+
+	/* Ext4 has at least one feature which ext3 doesn't understand */
+	if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) &&
+	    !(fi  & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
+		return -BLKID_ERR_PARAM;
+
+force_ext4:
+	/*
+	 * If the filesystem is a OK for use by in-development
+	 * filesystem code, and ext4dev is supported or ext4 is not
+	 * supported, then don't call ourselves ext4, so we can redo
+	 * the detection and mark the filesystem as ext4dev.
+	 *
+	 * If the filesystem is marked as in use by production
+	 * filesystem, then it can only be used by ext4 and NOT by
+	 * ext4dev.
+	 */
+	if (le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) {
+		if (system_supports_ext4dev() || !system_supports_ext4())
+			return -BLKID_ERR_PARAM;
+	}
+
+	ext_get_info(pr, 4, es);
+	return 0;
+}
+
+#define BLKID_EXT_MAGICS \
+	{ \
+		{	 \
+			.magic = EXT_SB_MAGIC, \
+			.len = sizeof(EXT_SB_MAGIC) - 1, \
+			.kboff = EXT_SB_KBOFF, \
+			.sboff = EXT_MAG_OFF \
+		}, \
+		{ NULL } \
+	}
+
+const struct blkid_idinfo jbd_idinfo =
+{
+	.name		= "jbd",
+	.usage		= BLKID_USAGE_OTHER,
+	.probefunc	= probe_jbd,
+	.magics		= BLKID_EXT_MAGICS
+};
+
+const struct blkid_idinfo ext2_idinfo =
+{
+	.name		= "ext2",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ext2,
+	.magics		= BLKID_EXT_MAGICS
+};
+
+const struct blkid_idinfo ext3_idinfo =
+{
+	.name		= "ext3",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ext3,
+	.magics		= BLKID_EXT_MAGICS
+};
+
+const struct blkid_idinfo ext4_idinfo =
+{
+	.name		= "ext4",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ext4,
+	.magics		= BLKID_EXT_MAGICS
+};
+
+const struct blkid_idinfo ext4dev_idinfo =
+{
+	.name		= "ext4dev",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ext4dev,
+	.magics		= BLKID_EXT_MAGICS
+};
+
diff --git a/src/superblocks/gfs.c b/src/superblocks/gfs.c
new file mode 100644
index 0000000..b2c0163
--- /dev/null
+++ b/src/superblocks/gfs.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+/* Common gfs/gfs2 constants: */
+#define GFS_LOCKNAME_LEN        64
+
+/* gfs1 constants: */
+#define GFS_FORMAT_FS           1309
+#define GFS_FORMAT_MULTI        1401
+/* gfs2 constants: */
+#define GFS2_FORMAT_FS          1801
+#define GFS2_FORMAT_MULTI       1900
+
+struct gfs2_meta_header {
+	uint32_t mh_magic;
+	uint32_t mh_type;
+	uint64_t __pad0;          /* Was generation number in gfs1 */
+	uint32_t mh_format;
+	uint32_t __pad1;          /* Was incarnation number in gfs1 */
+};
+
+struct gfs2_inum {
+	uint64_t no_formal_ino;
+	uint64_t no_addr;
+};
+
+struct gfs2_sb {
+	struct gfs2_meta_header sb_header;
+
+	uint32_t sb_fs_format;
+	uint32_t sb_multihost_format;
+	uint32_t  __pad0;  /* Was superblock flags in gfs1 */
+
+	uint32_t sb_bsize;
+	uint32_t sb_bsize_shift;
+	uint32_t __pad1;   /* Was journal segment size in gfs1 */
+
+	struct gfs2_inum sb_master_dir; /* Was jindex dinode in gfs1 */
+	struct gfs2_inum __pad2; /* Was rindex dinode in gfs1 */
+	struct gfs2_inum sb_root_dir;
+
+	char sb_lockproto[GFS_LOCKNAME_LEN];
+	char sb_locktable[GFS_LOCKNAME_LEN];
+
+	struct gfs2_inum __pad3; /* Was quota inode in gfs1 */
+	struct gfs2_inum __pad4; /* Was licence inode in gfs1 */
+	uint8_t sb_uuid[16]; /* The UUID maybe 0 for backwards compat */
+} __attribute__((packed));
+
+static int probe_gfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct gfs2_sb *sbd;
+
+	sbd = blkid_probe_get_sb(pr, mag, struct gfs2_sb);
+	if (!sbd)
+		return -1;
+
+	if (be32_to_cpu(sbd->sb_fs_format) == GFS_FORMAT_FS &&
+	    be32_to_cpu(sbd->sb_multihost_format) == GFS_FORMAT_MULTI)
+	{
+		if (*sbd->sb_locktable)
+			blkid_probe_set_label(pr,
+				(unsigned char *) sbd->sb_locktable,
+				sizeof(sbd->sb_locktable));
+
+		blkid_probe_set_uuid(pr, sbd->sb_uuid);
+		return 0;
+	}
+
+	return -1;
+}
+
+static int probe_gfs2(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct gfs2_sb *sbd;
+
+	sbd = blkid_probe_get_sb(pr, mag, struct gfs2_sb);
+	if (!sbd)
+		return -1;
+
+	if (be32_to_cpu(sbd->sb_fs_format) == GFS2_FORMAT_FS &&
+	    be32_to_cpu(sbd->sb_multihost_format) == GFS2_FORMAT_MULTI)
+	{
+		if (*sbd->sb_locktable)
+			blkid_probe_set_label(pr,
+				(unsigned char *) sbd->sb_locktable,
+				sizeof(sbd->sb_locktable));
+		blkid_probe_set_uuid(pr, sbd->sb_uuid);
+		blkid_probe_set_version(pr, "1");
+		return 0;
+	}
+	return -1;
+}
+
+const struct blkid_idinfo gfs_idinfo =
+{
+	.name		= "gfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_gfs,
+	.minsz		= 32 * 1024 * 1024,	/* minimal size of GFS journal */
+	.magics		=
+	{
+		{ .magic = "\x01\x16\x19\x70", .len = 4, .kboff = 64 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo gfs2_idinfo =
+{
+	.name		= "gfs2",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_gfs2,
+	.minsz		= 32 * 1024 * 1024,	/* minimal size of GFS journal */
+	.magics		=
+	{
+		{ .magic = "\x01\x16\x19\x70", .len = 4, .kboff = 64 },
+		{ NULL }
+	}
+};
+
diff --git a/src/superblocks/hfs.c b/src/superblocks/hfs.c
new file mode 100644
index 0000000..6d960e9
--- /dev/null
+++ b/src/superblocks/hfs.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2004-2008 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "superblocks.h"
+#include "md5.h"
+
+/* HFS / HFS+ */
+struct hfs_finder_info {
+        uint32_t        boot_folder;
+        uint32_t        start_app;
+        uint32_t        open_folder;
+        uint32_t        os9_folder;
+        uint32_t        reserved;
+        uint32_t        osx_folder;
+        uint8_t         id[8];
+} __attribute__((packed));
+
+struct hfs_mdb {
+        uint8_t         signature[2];
+        uint32_t        cr_date;
+        uint32_t        ls_Mod;
+        uint16_t        atrb;
+        uint16_t        nm_fls;
+        uint16_t        vbm_st;
+        uint16_t        alloc_ptr;
+        uint16_t        nm_al_blks;
+        uint32_t        al_blk_size;
+        uint32_t        clp_size;
+        uint16_t        al_bl_st;
+        uint32_t        nxt_cnid;
+        uint16_t        free_bks;
+        uint8_t         label_len;
+        uint8_t         label[27];
+        uint32_t        vol_bkup;
+        uint16_t        vol_seq_num;
+        uint32_t        wr_cnt;
+        uint32_t        xt_clump_size;
+        uint32_t        ct_clump_size;
+        uint16_t        num_root_dirs;
+        uint32_t        file_count;
+        uint32_t        dir_count;
+        struct hfs_finder_info finder_info;
+        uint8_t         embed_sig[2];
+        uint16_t        embed_startblock;
+        uint16_t        embed_blockcount;
+} __attribute__((packed));
+
+
+#define HFS_NODE_LEAF			0xff
+#define HFSPLUS_POR_CNID		1
+
+struct hfsplus_bnode_descriptor {
+	uint32_t		next;
+	uint32_t		prev;
+	uint8_t		type;
+	uint8_t		height;
+	uint16_t		num_recs;
+	uint16_t		reserved;
+} __attribute__((packed));
+
+struct hfsplus_bheader_record {
+	uint16_t		depth;
+	uint32_t		root;
+	uint32_t		leaf_count;
+	uint32_t		leaf_head;
+	uint32_t		leaf_tail;
+	uint16_t		node_size;
+} __attribute__((packed));
+
+struct hfsplus_catalog_key {
+	uint16_t	key_len;
+	uint32_t	parent_id;
+	uint16_t	unicode_len;
+	uint8_t		unicode[255 * 2];
+} __attribute__((packed));
+
+struct hfsplus_extent {
+	uint32_t		start_block;
+	uint32_t		block_count;
+} __attribute__((packed));
+
+#define HFSPLUS_EXTENT_COUNT		8
+struct hfsplus_fork {
+	uint64_t		total_size;
+	uint32_t		clump_size;
+	uint32_t		total_blocks;
+	struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
+} __attribute__((packed));
+
+struct hfsplus_vol_header {
+	uint8_t		signature[2];
+	uint16_t		version;
+	uint32_t		attributes;
+	uint32_t		last_mount_vers;
+	uint32_t		reserved;
+	uint32_t		create_date;
+	uint32_t		modify_date;
+	uint32_t		backup_date;
+	uint32_t		checked_date;
+	uint32_t		file_count;
+	uint32_t		folder_count;
+	uint32_t		blocksize;
+	uint32_t		total_blocks;
+	uint32_t		free_blocks;
+	uint32_t		next_alloc;
+	uint32_t		rsrc_clump_sz;
+	uint32_t		data_clump_sz;
+	uint32_t		next_cnid;
+	uint32_t		write_count;
+	uint64_t		encodings_bmp;
+	struct hfs_finder_info finder_info;
+	struct hfsplus_fork alloc_file;
+	struct hfsplus_fork ext_file;
+	struct hfsplus_fork cat_file;
+	struct hfsplus_fork attr_file;
+	struct hfsplus_fork start_file;
+}  __attribute__((packed));
+
+#define HFSPLUS_SECTOR_SIZE        512
+
+static int hfs_set_uuid(blkid_probe pr, unsigned char const *hfs_info, size_t len)
+{
+	static unsigned char const hash_init[MD5LENGTH] = {
+		0xb3, 0xe2, 0x0f, 0x39, 0xf2, 0x92, 0x11, 0xd6,
+		0x97, 0xa4, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac
+	};
+	unsigned char uuid[MD5LENGTH];
+	struct MD5Context md5c;
+
+	if (memcmp(hfs_info, "\0\0\0\0\0\0\0\0", len) == 0)
+		return -1;
+	MD5Init(&md5c);
+	MD5Update(&md5c, hash_init, MD5LENGTH);
+	MD5Update(&md5c, hfs_info, len);
+	MD5Final(uuid, &md5c);
+	uuid[6] = 0x30 | (uuid[6] & 0x0f);
+	uuid[8] = 0x80 | (uuid[8] & 0x3f);
+	return blkid_probe_set_uuid(pr, uuid);
+}
+
+static int probe_hfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct hfs_mdb	*hfs;
+
+	hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
+	if (!hfs)
+		return -1;
+
+	if ((memcmp(hfs->embed_sig, "H+", 2) == 0) ||
+	    (memcmp(hfs->embed_sig, "HX", 2) == 0))
+		return 1;	/* Not hfs, but an embedded HFS+ */
+
+	hfs_set_uuid(pr, hfs->finder_info.id, sizeof(hfs->finder_info.id));
+
+	blkid_probe_set_label(pr, hfs->label, hfs->label_len);
+	return 0;
+}
+
+static int probe_hfsplus(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT];
+	struct hfsplus_bnode_descriptor *descr;
+	struct hfsplus_bheader_record *bnode;
+	struct hfsplus_catalog_key *key;
+	struct hfsplus_vol_header *hfsplus;
+	struct hfs_mdb *sbd;
+	unsigned int alloc_block_size;
+	unsigned int alloc_first_block;
+	unsigned int embed_first_block;
+	unsigned int off = 0;
+	unsigned int blocksize;
+	unsigned int cat_block;
+	unsigned int ext_block_start;
+	unsigned int ext_block_count;
+	unsigned int record_count;
+	unsigned int leaf_node_head;
+	unsigned int leaf_node_count;
+	unsigned int leaf_node_size;
+	unsigned int leaf_block;
+	int ext;
+	uint64_t leaf_off;
+	unsigned char *buf;
+
+	sbd = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
+	if (!sbd)
+		return -1;
+
+	/* Check for a HFS+ volume embedded in a HFS volume */
+	if (memcmp(sbd->signature, "BD", 2) == 0) {
+		if ((memcmp(sbd->embed_sig, "H+", 2) != 0) &&
+		    (memcmp(sbd->embed_sig, "HX", 2) != 0))
+			/* This must be an HFS volume, so fail */
+			return 1;
+
+		alloc_block_size = be32_to_cpu(sbd->al_blk_size);
+		alloc_first_block = be16_to_cpu(sbd->al_bl_st);
+		embed_first_block = be16_to_cpu(sbd->embed_startblock);
+		off = (alloc_first_block * 512) +
+			(embed_first_block * alloc_block_size);
+
+		buf = blkid_probe_get_buffer(pr,
+				off + (mag->kboff * 1024),
+				sizeof(struct hfsplus_vol_header));
+		hfsplus = (struct hfsplus_vol_header *) buf;
+
+	} else
+		hfsplus = blkid_probe_get_sb(pr, mag,
+				struct hfsplus_vol_header);
+
+	if (!hfsplus)
+		return -1;
+
+	if ((memcmp(hfsplus->signature, "H+", 2) != 0) &&
+	    (memcmp(hfsplus->signature, "HX", 2) != 0))
+		return 1;
+
+	hfs_set_uuid(pr, hfsplus->finder_info.id, sizeof(hfsplus->finder_info.id));
+
+	blocksize = be32_to_cpu(hfsplus->blocksize);
+	if (blocksize < HFSPLUS_SECTOR_SIZE)
+		return -1;
+
+	memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
+	cat_block = be32_to_cpu(extents[0].start_block);
+
+	buf = blkid_probe_get_buffer(pr,
+			off + ((blkid_loff_t) cat_block * blocksize), 0x2000);
+	if (!buf)
+		return 0;
+
+	bnode = (struct hfsplus_bheader_record *)
+		&buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+	leaf_node_head = be32_to_cpu(bnode->leaf_head);
+	leaf_node_size = be16_to_cpu(bnode->node_size);
+	leaf_node_count = be32_to_cpu(bnode->leaf_count);
+	if (leaf_node_count == 0)
+		return 0;
+
+	leaf_block = (leaf_node_head * leaf_node_size) / blocksize;
+
+	/* get physical location */
+	for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) {
+		ext_block_start = be32_to_cpu(extents[ext].start_block);
+		ext_block_count = be32_to_cpu(extents[ext].block_count);
+		if (ext_block_count == 0)
+			return 0;
+
+		/* this is our extent */
+		if (leaf_block < ext_block_count)
+			break;
+
+		leaf_block -= ext_block_count;
+	}
+	if (ext == HFSPLUS_EXTENT_COUNT)
+		return 0;
+
+	leaf_off = (ext_block_start + leaf_block) * blocksize;
+
+	buf = blkid_probe_get_buffer(pr,
+				(blkid_loff_t) off + leaf_off,
+				leaf_node_size);
+	if (!buf)
+		return 0;
+
+	descr = (struct hfsplus_bnode_descriptor *) buf;
+	record_count = be16_to_cpu(descr->num_recs);
+	if (record_count == 0)
+		return 0;
+
+	if (descr->type != HFS_NODE_LEAF)
+		return 0;
+
+	key = (struct hfsplus_catalog_key *)
+		&buf[sizeof(struct hfsplus_bnode_descriptor)];
+
+	if (be32_to_cpu(key->parent_id) != HFSPLUS_POR_CNID)
+		return 0;
+
+	blkid_probe_set_utf8label(pr, key->unicode,
+			be16_to_cpu(key->unicode_len) * 2,
+			BLKID_ENC_UTF16BE);
+	return 0;
+}
+
+const struct blkid_idinfo hfs_idinfo =
+{
+	.name		= "hfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_hfs,
+	.flags		= BLKID_IDINFO_TOLERANT,
+	.magics		=
+	{
+		{ .magic = "BD", .len = 2, .kboff = 1 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo hfsplus_idinfo =
+{
+	.name		= "hfsplus",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_hfsplus,
+	.magics		=
+	{
+		{ .magic = "BD", .len = 2, .kboff = 1 },
+		{ .magic = "H+", .len = 2, .kboff = 1 },
+		{ .magic = "HX", .len = 2, .kboff = 1 },
+		{ NULL }
+	}
+};
diff --git a/src/superblocks/highpoint_raid.c b/src/superblocks/highpoint_raid.c
new file mode 100644
index 0000000..0b41344
--- /dev/null
+++ b/src/superblocks/highpoint_raid.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct hpt45x_metadata {
+	uint32_t	magic;
+};
+
+#define HPT45X_MAGIC_OK			0x5a7816f3
+#define HPT45X_MAGIC_BAD		0x5a7816fd
+
+static int probe_highpoint45x(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct hpt45x_metadata *hpt;
+	uint64_t off;
+	uint32_t magic;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200) - 11) * 0x200;
+	hpt = (struct hpt45x_metadata *)
+			blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct hpt45x_metadata));
+	if (!hpt)
+		return -1;
+	magic = le32_to_cpu(hpt->magic);
+	if (magic != HPT45X_MAGIC_OK && magic != HPT45X_MAGIC_BAD)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(hpt->magic),
+				(unsigned char *) &hpt->magic))
+		return -1;
+	return 0;
+}
+
+static int probe_highpoint37x(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+	return 0;
+}
+
+
+const struct blkid_idinfo highpoint45x_idinfo = {
+	.name		= "hpt45x_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_highpoint45x,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+const struct blkid_idinfo highpoint37x_idinfo = {
+	.name		= "hpt37x_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_highpoint37x,
+	.magics		= {
+		/*
+		 * Superblok offset:                      4608 bytes  (9 sectors)
+		 * Magic string offset within superblock:   32 bytes
+		 *
+		 * kboff = (4608 + 32) / 1024
+		 * sboff = (4608 + 32) % kboff
+		 */
+		{ .magic = "\xf0\x16\x78\x5a", .len = 4, .kboff = 4, .sboff = 544 },
+		{ .magic = "\xfd\x16\x78\x5a", .len = 4, .kboff = 4, .sboff = 544 },
+		{ NULL }
+	}
+};
+
+
diff --git a/src/superblocks/hpfs.c b/src/superblocks/hpfs.c
new file mode 100644
index 0000000..f9b851a
--- /dev/null
+++ b/src/superblocks/hpfs.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct hpfs_boot_block
+{
+	uint8_t		jmp[3];
+	uint8_t		oem_id[8];
+	uint8_t		bytes_per_sector[2];
+	uint8_t		sectors_per_cluster;
+	uint8_t		n_reserved_sectors[2];
+	uint8_t		n_fats;
+	uint8_t		n_rootdir_entries[2];
+	uint8_t		n_sectors_s[2];
+	uint8_t		media_byte;
+	uint16_t	sectors_per_fat;
+	uint16_t	sectors_per_track;
+	uint16_t	heads_per_cyl;
+	uint32_t	n_hidden_sectors;
+	uint32_t	n_sectors_l;
+	uint8_t		drive_number;
+	uint8_t		mbz;
+	uint8_t		sig_28h;
+	uint8_t		vol_serno[4];
+	uint8_t		vol_label[11];
+	uint8_t		sig_hpfs[8];
+	uint8_t		pad[448];
+	uint8_t		magic[2];
+} __attribute__((packed));
+
+struct hpfs_super_block
+{
+	uint8_t		magic[4];
+	uint8_t		magic1[4];
+	uint8_t		version;
+} __attribute__((packed));
+
+struct hpfs_spare_super
+{
+	uint8_t		magic[4];
+	uint8_t		magic1[4];
+} __attribute__((packed));
+
+
+#define HPFS_SB_OFFSET			0x2000
+#define HPFS_SBSPARE_OFFSET		0x2200
+
+static int probe_hpfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct hpfs_super_block *hs;
+	struct hpfs_spare_super *hss;
+	struct hpfs_boot_block *hbb;
+	uint8_t version;
+
+	/* super block */
+	hs = blkid_probe_get_sb(pr, mag, struct hpfs_super_block);
+	if (!hs)
+		return -1;
+	version = hs->version;
+
+	/* spare super block */
+	hss = (struct hpfs_spare_super *)
+			blkid_probe_get_buffer(pr,
+				HPFS_SBSPARE_OFFSET,
+				sizeof(struct hpfs_spare_super));
+	if (!hss)
+		return -1;
+	if (memcmp(hss->magic, "\x49\x18\x91\xf9", 4) != 0)
+		return -1;
+
+	/* boot block (with UUID and LABEL) */
+	hbb = (struct hpfs_boot_block *)
+			blkid_probe_get_buffer(pr,
+				0,
+				sizeof(struct hpfs_boot_block));
+	if (!hbb)
+		return -1;
+	if (memcmp(hbb->magic, "\x55\xaa", 2) == 0 &&
+	    memcmp(hbb->sig_hpfs, "HPFS", 4) == 0 &&
+	    hbb->sig_28h == 0x28) {
+		blkid_probe_set_label(pr, hbb->vol_label, sizeof(hbb->vol_label));
+		blkid_probe_sprintf_uuid(pr,
+				hbb->vol_serno, sizeof(hbb->vol_serno),
+				"%02X%02X-%02X%02X",
+				hbb->vol_serno[3], hbb->vol_serno[2],
+				hbb->vol_serno[1], hbb->vol_serno[0]);
+	}
+	blkid_probe_sprintf_version(pr, "%u", version);
+
+	return 0;
+}
+
+const struct blkid_idinfo hpfs_idinfo =
+{
+	.name		= "hpfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_hpfs,
+	.magics		=
+	{
+		{
+		  .magic = "\x49\xe8\x95\xf9",
+		  .len = 4,
+		  .kboff = (HPFS_SB_OFFSET >> 10)
+		},
+		{ NULL }
+	}
+};
+
+
diff --git a/src/superblocks/iso9660.c b/src/superblocks/iso9660.c
new file mode 100644
index 0000000..f7d2b76
--- /dev/null
+++ b/src/superblocks/iso9660.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired also by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct iso9660_date {
+	unsigned char year[4];
+	unsigned char month[2];
+	unsigned char day[2];
+	unsigned char hour[2];
+	unsigned char minute[2];
+	unsigned char second[2];
+	unsigned char hundredth[2];
+	unsigned char offset;
+} __attribute__ ((packed));
+
+/* PVD - Primary volume descriptor */
+struct iso_volume_descriptor {
+	unsigned char	vd_type;
+	unsigned char	vd_id[5];
+	unsigned char	vd_version;
+	unsigned char	flags;
+	unsigned char	system_id[32];
+	unsigned char	volume_id[32];
+	unsigned char	unused[8];
+	unsigned char	space_size[8];
+	unsigned char	escape_sequences[8];
+	unsigned char   unused1[717];
+	struct iso9660_date created;
+	struct iso9660_date modified;
+} __attribute__((packed));
+
+#define ISO_SUPERBLOCK_OFFSET		0x8000
+#define ISO_SECTOR_SIZE			0x800
+#define ISO_VD_OFFSET			(ISO_SUPERBLOCK_OFFSET + ISO_SECTOR_SIZE)
+#define ISO_VD_SUPPLEMENTARY		0x2
+#define ISO_VD_END			0xff
+#define ISO_VD_MAX			16
+
+struct high_sierra_volume_descriptor {
+	unsigned char	foo[8];
+	unsigned char	type;
+	unsigned char	id[5];
+	unsigned char	version;
+	unsigned char	unused1;
+	unsigned char	system_id[32];
+	unsigned char   volume_id[32];
+} __attribute__((packed));
+
+/* returns 1 if the begin of @ascii is equal to @utf16 string.
+ */
+static int ascii_eq_utf16be(unsigned char *ascii,
+			unsigned char *utf16, size_t len)
+{
+	size_t a, u;
+
+	for (a = 0, u = 0; u < len; a++, u += 2) {
+		if (utf16[u] != 0x0 || ascii[a] != utf16[u + 1])
+			return 0;
+	}
+	return 1;
+}
+
+/* old High Sierra format */
+static int probe_iso9660_hsfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct high_sierra_volume_descriptor *iso;
+
+	iso = blkid_probe_get_sb(pr, mag, struct high_sierra_volume_descriptor);
+	if (!iso)
+		return -1;
+
+	blkid_probe_set_version(pr, "High Sierra");
+	blkid_probe_set_label(pr, iso->volume_id, sizeof(iso->volume_id));
+	return 0;
+}
+
+static int probe_iso9660_set_uuid (blkid_probe pr, const struct iso9660_date *date)
+{
+	unsigned char buffer[16];
+	unsigned int i, zeros = 0;
+
+	buffer[0] = date->year[0];
+	buffer[1] = date->year[1];
+	buffer[2] = date->year[2];
+	buffer[3] = date->year[3];
+	buffer[4] = date->month[0];
+	buffer[5] = date->month[1];
+	buffer[6] = date->day[0];
+	buffer[7] = date->day[1];
+	buffer[8] = date->hour[0];
+	buffer[9] = date->hour[1];
+	buffer[10] = date->minute[0];
+	buffer[11] = date->minute[1];
+	buffer[12] = date->second[0];
+	buffer[13] = date->second[1];
+	buffer[14] = date->hundredth[0];
+	buffer[15] = date->hundredth[1];
+
+	/* count the number of zeros ('0') in the date buffer */
+	for (i = 0, zeros = 0; i < sizeof(buffer); i++)
+		if (buffer[i] == '0')
+			zeros++;
+
+	/* due to the iso9660 standard if all date fields are '0' and offset is 0, the date is unset */
+	if (zeros == sizeof(buffer) && date->offset == 0)
+		return 0;
+
+	/* generate an UUID using this date and return success */
+	blkid_probe_sprintf_uuid (pr, buffer, sizeof(buffer),
+		"%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c",
+		buffer[0], buffer[1], buffer[2], buffer[3],
+		buffer[4], buffer[5],
+		buffer[6], buffer[7],
+		buffer[8], buffer[9],
+		buffer[10], buffer[11],
+		buffer[12], buffer[13],
+		buffer[14], buffer[15]);
+
+	return 1;
+}
+
+/* iso9660 [+ Microsoft Joliet Extension] */
+static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct iso_volume_descriptor *iso;
+	unsigned char label[32];
+	int i;
+	int off;
+
+	if (strcmp(mag->magic, "CDROM") == 0)
+		return probe_iso9660_hsfs(pr, mag);
+
+	iso = blkid_probe_get_sb(pr, mag, struct iso_volume_descriptor);
+	if (!iso)
+		return -1;
+
+	memcpy(label, iso->volume_id, sizeof(label));
+
+	/* create an UUID using the modified/created date */
+	if (! probe_iso9660_set_uuid(pr, &iso->modified))
+		probe_iso9660_set_uuid(pr, &iso->created);
+
+	/* Joliet Extension */
+	off = ISO_VD_OFFSET;
+	for (i = 0; i < ISO_VD_MAX; i++) {
+		iso = (struct iso_volume_descriptor *)
+			blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct iso_volume_descriptor));
+
+		if (iso == NULL || iso->vd_type == ISO_VD_END)
+			break;
+		if (iso->vd_type != ISO_VD_SUPPLEMENTARY)
+			continue;
+
+		if (memcmp(iso->escape_sequences, "%/@", 3) == 0 ||
+		    memcmp(iso->escape_sequences, "%/C", 3) == 0 ||
+		    memcmp(iso->escape_sequences, "%/E", 3) == 0) {
+
+			blkid_probe_set_version(pr, "Joliet Extension");
+
+			/* Is the Joliet (UTF16BE) label equal to the label in
+			 * the PVD? If yes, use PVD label.  The Jolied version
+			 * of the label could be trimed (because UTF16..).
+			 */
+			if (ascii_eq_utf16be(label, iso->volume_id, 32))
+				break;
+
+			blkid_probe_set_utf8label(pr,
+					iso->volume_id,
+					sizeof(iso->volume_id),
+					BLKID_ENC_UTF16BE);
+			goto has_label;
+		}
+		off += ISO_SECTOR_SIZE;
+	}
+
+	/* Joliet not found, let use standard iso label */
+	blkid_probe_set_label(pr, label, sizeof(label));
+
+has_label:
+	return 0;
+}
+
+const struct blkid_idinfo iso9660_idinfo =
+{
+	.name		= "iso9660",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_iso9660,
+	.flags		= BLKID_IDINFO_TOLERANT,
+	.magics		=
+	{
+		{ .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "CDROM", .len = 5, .kboff = 32, .sboff = 9 },
+		{ NULL }
+	}
+};
+
diff --git a/src/superblocks/isw_raid.c b/src/superblocks/isw_raid.c
new file mode 100644
index 0000000..755c1b6
--- /dev/null
+++ b/src/superblocks/isw_raid.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct isw_metadata {
+	uint8_t		sig[32];
+	uint32_t	check_sum;
+	uint32_t	mpb_size;
+	uint32_t	family_num;
+	uint32_t	generation_num;
+};
+
+#define ISW_SIGNATURE		"Intel Raid ISM Cfg Sig. "
+
+
+static int probe_iswraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct isw_metadata *isw;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200) - 2) * 0x200;
+	isw = (struct isw_metadata *)
+			blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct isw_metadata));
+	if (!isw)
+		return -1;
+	if (memcmp(isw->sig, ISW_SIGNATURE, sizeof(ISW_SIGNATURE)-1) != 0)
+		return -1;
+	if (blkid_probe_sprintf_version(pr, "%6s",
+			&isw->sig[sizeof(ISW_SIGNATURE)-1]) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(isw->sig),
+				(unsigned char *) isw->sig))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo iswraid_idinfo = {
+	.name		= "isw_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_iswraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/src/superblocks/jfs.c b/src/superblocks/jfs.c
new file mode 100644
index 0000000..78c018c
--- /dev/null
+++ b/src/superblocks/jfs.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct jfs_super_block {
+	unsigned char	js_magic[4];
+	uint32_t	js_version;
+	uint64_t	js_size;
+	uint32_t	js_bsize;	/* 4: aggregate block size in bytes */
+	uint16_t	js_l2bsize;	/* 2: log2 of s_bsize */
+	uint16_t	js_l2bfactor;	/* 2: log2(s_bsize/hardware block size) */
+	uint32_t	js_pbsize;	/* 4: hardware/LVM block size in bytes */
+	uint16_t	js_l2pbsize;	/* 2: log2 of s_pbsize */
+	uint16_t	js_pad;		/* 2: padding necessary for alignment */
+	uint32_t	js_dummy2[26];
+	unsigned char	js_uuid[16];
+	unsigned char	js_label[16];
+	unsigned char	js_loguuid[16];
+};
+
+static int probe_jfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct jfs_super_block *js;
+
+	js = blkid_probe_get_sb(pr, mag, struct jfs_super_block);
+	if (!js)
+		return -1;
+	if (le32_to_cpu(js->js_bsize) != (1U << le16_to_cpu(js->js_l2bsize)))
+		return 1;
+	if (le32_to_cpu(js->js_pbsize) != (1U << le16_to_cpu(js->js_l2pbsize)))
+		return 1;
+	if ((le16_to_cpu(js->js_l2bsize) - le16_to_cpu(js->js_l2pbsize)) !=
+	    le16_to_cpu(js->js_l2bfactor))
+		return 1;
+
+	if (strlen((char *) js->js_label))
+		blkid_probe_set_label(pr, js->js_label, sizeof(js->js_label));
+	blkid_probe_set_uuid(pr, js->js_uuid);
+	return 0;
+}
+
+
+const struct blkid_idinfo jfs_idinfo =
+{
+	.name		= "jfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_jfs,
+	.minsz		= 16 * 1024 * 1024,
+	.magics		=
+	{
+		{ .magic = "JFS1", .len = 4, .kboff = 32 },
+		{ NULL }
+	}
+};
+
diff --git a/src/superblocks/jmicron_raid.c b/src/superblocks/jmicron_raid.c
new file mode 100644
index 0000000..c708078
--- /dev/null
+++ b/src/superblocks/jmicron_raid.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct jm_metadata {
+	int8_t		signature[2];
+	uint8_t		minor_version;
+	uint8_t		major_version;
+	uint16_t	checksum;
+};
+
+#define JM_SIGNATURE		"JM"
+
+static int probe_jmraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct jm_metadata *jm;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200) - 1) * 0x200;
+	jm = (struct jm_metadata *)
+		blkid_probe_get_buffer(pr,
+				off,
+				sizeof(struct jm_metadata));
+	if (!jm)
+		return -1;
+	if (memcmp(jm->signature, JM_SIGNATURE, sizeof(JM_SIGNATURE) - 1) != 0)
+		return -1;
+	if (blkid_probe_sprintf_version(pr, "%u.%u",
+				jm->major_version, jm->minor_version) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(jm->signature),
+				(unsigned char *) jm->signature))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo jmraid_idinfo = {
+	.name		= "jmicron_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_jmraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/src/superblocks/linux_raid.c b/src/superblocks/linux_raid.c
new file mode 100644
index 0000000..a3f9d67
--- /dev/null
+++ b/src/superblocks/linux_raid.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct mdp0_super_block {
+	uint32_t	md_magic;
+	uint32_t	major_version;
+	uint32_t	minor_version;
+	uint32_t	patch_version;
+	uint32_t	gvalid_words;
+	uint32_t	set_uuid0;
+	uint32_t	ctime;
+	uint32_t	level;
+	uint32_t	size;
+	uint32_t	nr_disks;
+	uint32_t	raid_disks;
+	uint32_t	md_minor;
+	uint32_t	not_persistent;
+	uint32_t	set_uuid1;
+	uint32_t	set_uuid2;
+	uint32_t	set_uuid3;
+};
+
+/*
+ * Version-1, little-endian.
+ */
+struct mdp1_super_block {
+	/* constant array information - 128 bytes */
+	uint32_t	magic;		/* MD_SB_MAGIC: 0xa92b4efc - little endian */
+	uint32_t	major_version;	/* 1 */
+	uint32_t	feature_map;	/* 0 for now */
+	uint32_t	pad0;		/* always set to 0 when writing */
+
+	uint8_t		set_uuid[16];	/* user-space generated. */
+	unsigned char	set_name[32];	/* set and interpreted by user-space */
+
+	uint64_t	ctime;		/* lo 40 bits are seconds, top 24 are microseconds or 0*/
+	uint32_t	level;		/* -4 (multipath), -1 (linear), 0,1,4,5 */
+	uint32_t	layout;		/* only for raid5 currently */
+	uint64_t	size;		/* used size of component devices, in 512byte sectors */
+
+	uint32_t	chunksize;	/* in 512byte sectors */
+	uint32_t	raid_disks;
+	uint32_t	bitmap_offset;	/* sectors after start of superblock that bitmap starts
+					 * NOTE: signed, so bitmap can be before superblock
+					 * only meaningful of feature_map[0] is set.
+					 */
+
+	/* These are only valid with feature bit '4' */
+	uint32_t	new_level;	/* new level we are reshaping to		*/
+	uint64_t	reshape_position;	/* next address in array-space for reshape */
+	uint32_t	delta_disks;	/* change in number of raid_disks		*/
+	uint32_t	new_layout;	/* new layout					*/
+	uint32_t	new_chunk;	/* new chunk size (bytes)			*/
+	uint8_t		pad1[128-124];	/* set to 0 when written */
+
+	/* constant this-device information - 64 bytes */
+	uint64_t	data_offset;	/* sector start of data, often 0 */
+	uint64_t	data_size;	/* sectors in this device that can be used for data */
+	uint64_t	super_offset;	/* sector start of this superblock */
+	uint64_t	recovery_offset;/* sectors before this offset (from data_offset) have been recovered */
+	uint32_t	dev_number;	/* permanent identifier of this  device - not role in raid */
+	uint32_t	cnt_corrected_read; /* number of read errors that were corrected by re-writing */
+	uint8_t		device_uuid[16]; /* user-space setable, ignored by kernel */
+        uint8_t		devflags;        /* per-device flags.  Only one defined...*/
+	uint8_t		pad2[64-57];	/* set to 0 when writing */
+
+	/* array state information - 64 bytes */
+	uint64_t	utime;		/* 40 bits second, 24 btes microseconds */
+	uint64_t	events;		/* incremented when superblock updated */
+	uint64_t	resync_offset;	/* data before this offset (from data_offset) known to be in sync */
+	uint32_t	sb_csum;	/* checksum up to dev_roles[max_dev] */
+	uint32_t	max_dev;	/* size of dev_roles[] array to consider */
+	uint8_t		pad3[64-32];	/* set to 0 when writing */
+
+	/* device state information. Indexed by dev_number.
+	 * 2 bytes per device
+	 * Note there are no per-device state flags. State information is rolled
+	 * into the 'roles' value.  If a device is spare or faulty, then it doesn't
+	 * have a meaningful role.
+	 */
+	uint16_t	dev_roles[0];	/* role in array, or 0xffff for a spare, or 0xfffe for faulty */
+};
+
+
+#define MD_RESERVED_BYTES		0x10000
+#define MD_SB_MAGIC			0xa92b4efc
+
+static int probe_raid0(blkid_probe pr, blkid_loff_t off)
+{
+	struct mdp0_super_block *mdp0;
+	union {
+		uint32_t ints[4];
+		uint8_t bytes[16];
+	} uuid;
+	uint32_t ma, mi, pa;
+	uint64_t size;
+
+	if (pr->size < MD_RESERVED_BYTES)
+		return -1;
+	mdp0 = (struct mdp0_super_block *)
+			blkid_probe_get_buffer(pr,
+				off,
+				sizeof(struct mdp0_super_block));
+	if (!mdp0)
+		return -1;
+
+	memset(uuid.ints, 0, sizeof(uuid.ints));
+
+	if (le32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) {
+		uuid.ints[0] = swab32(mdp0->set_uuid0);
+		if (le32_to_cpu(mdp0->minor_version) >= 90) {
+			uuid.ints[1] = swab32(mdp0->set_uuid1);
+			uuid.ints[2] = swab32(mdp0->set_uuid2);
+			uuid.ints[3] = swab32(mdp0->set_uuid3);
+		}
+		ma = le32_to_cpu(mdp0->major_version);
+		mi = le32_to_cpu(mdp0->minor_version);
+		pa = le32_to_cpu(mdp0->patch_version);
+		size = le32_to_cpu(mdp0->size);
+
+	} else if (be32_to_cpu(mdp0->md_magic) == MD_SB_MAGIC) {
+		uuid.ints[0] = mdp0->set_uuid0;
+		if (be32_to_cpu(mdp0->minor_version) >= 90) {
+			uuid.ints[1] = mdp0->set_uuid1;
+			uuid.ints[2] = mdp0->set_uuid2;
+			uuid.ints[3] = mdp0->set_uuid3;
+		}
+		ma = be32_to_cpu(mdp0->major_version);
+		mi = be32_to_cpu(mdp0->minor_version);
+		pa = be32_to_cpu(mdp0->patch_version);
+		size = be32_to_cpu(mdp0->size);
+	} else
+		return 1;
+
+	size <<= 10;	/* convert KiB to bytes */
+
+	if (pr->size < 0 || (uint64_t) pr->size < size + MD_RESERVED_BYTES)
+		/* device is too small */
+		return 1;
+
+	if (off < 0 || (uint64_t) off < size)
+		/* no space before superblock */
+		return 1;
+
+	/*
+	 * Check for collisions between RAID and partition table
+	 *
+	 * For example the superblock is at the end of the last partition, it's
+	 * the same position as at the end of the disk...
+	 */
+	if ((S_ISREG(pr->mode) || blkid_probe_is_wholedisk(pr)) &&
+	    blkid_probe_is_covered_by_pt(pr,
+			off - size,				/* min. start  */
+			size + MD_RESERVED_BYTES)) {		/* min. length */
+
+		/* ignore this superblock, it's within any partition and
+		 * we are working with whole-disk now */
+		return 1;
+	}
+
+	if (blkid_probe_sprintf_version(pr, "%u.%u.%u", ma, mi, pa) != 0)
+		return -1;
+	if (blkid_probe_set_uuid(pr, (unsigned char *) uuid.bytes) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(mdp0->md_magic),
+				(unsigned char *) &mdp0->md_magic))
+		return -1;
+	return 0;
+}
+
+static int probe_raid1(blkid_probe pr, off_t off)
+{
+	struct mdp1_super_block *mdp1;
+
+	mdp1 = (struct mdp1_super_block *)
+			blkid_probe_get_buffer(pr,
+				off,
+				sizeof(struct mdp1_super_block));
+	if (!mdp1)
+		return -1;
+	if (le32_to_cpu(mdp1->magic) != MD_SB_MAGIC)
+		return -1;
+	if (le32_to_cpu(mdp1->major_version) != 1U)
+		return -1;
+	if (le64_to_cpu(mdp1->super_offset) != (uint64_t) off >> 9)
+		return -1;
+	if (blkid_probe_set_uuid(pr, (unsigned char *) mdp1->set_uuid) != 0)
+		return -1;
+	if (blkid_probe_set_uuid_as(pr,
+			(unsigned char *) mdp1->device_uuid, "UUID_SUB") != 0)
+		return -1;
+	if (blkid_probe_set_label(pr, mdp1->set_name,
+				sizeof(mdp1->set_name)) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(mdp1->magic),
+				(unsigned char *) &mdp1->magic))
+		return -1;
+	return 0;
+}
+
+int probe_raid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	const char *ver = NULL;
+
+	if (pr->size > MD_RESERVED_BYTES) {
+		/* version 0 at the end of the device */
+		uint64_t sboff = (pr->size & ~(MD_RESERVED_BYTES - 1))
+			         - MD_RESERVED_BYTES;
+		if (probe_raid0(pr, sboff) == 0)
+			return 0;
+
+		/* version 1.0 at the end of the device */
+		sboff = (pr->size & ~(0x1000 - 1)) - 0x2000;
+		if (probe_raid1(pr, sboff) == 0)
+			ver = "1.0";
+	}
+
+	if (!ver) {
+		/* version 1.1 at the start of the device */
+		if (probe_raid1(pr, 0) == 0)
+			ver = "1.1";
+
+		/* version 1.2 at 4k offset from the start */
+		else if (probe_raid1(pr, 0x1000) == 0)
+			ver = "1.2";
+	}
+
+	if (ver) {
+		blkid_probe_set_version(pr, ver);
+		return 0;
+	}
+	return -1;
+}
+
+
+const struct blkid_idinfo linuxraid_idinfo = {
+	.name		= "linux_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_raid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/src/superblocks/lsi_raid.c b/src/superblocks/lsi_raid.c
new file mode 100644
index 0000000..56721dd
--- /dev/null
+++ b/src/superblocks/lsi_raid.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct lsi_metadata {
+	uint8_t		sig[6];
+};
+
+
+#define LSI_SIGNATURE		"$XIDE$"
+
+static int probe_lsiraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct lsi_metadata *lsi;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200) - 1) * 0x200;
+	lsi = (struct lsi_metadata *)
+		blkid_probe_get_buffer(pr,
+				off,
+				sizeof(struct lsi_metadata));
+	if (!lsi)
+		return -1;
+
+	if (memcmp(lsi->sig, LSI_SIGNATURE, sizeof(LSI_SIGNATURE)-1) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(lsi->sig),
+				(unsigned char *) lsi->sig))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo lsiraid_idinfo = {
+	.name		= "lsi_mega_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_lsiraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/src/superblocks/luks.c b/src/superblocks/luks.c
new file mode 100644
index 0000000..f716e31
--- /dev/null
+++ b/src/superblocks/luks.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+#define LUKS_CIPHERNAME_L		32
+#define LUKS_CIPHERMODE_L		32
+#define LUKS_HASHSPEC_L			32
+#define LUKS_DIGESTSIZE			20
+#define LUKS_SALTSIZE			32
+#define LUKS_MAGIC_L			6
+#define UUID_STRING_L			40
+
+struct luks_phdr {
+	uint8_t		magic[LUKS_MAGIC_L];
+	uint16_t	version;
+	uint8_t		cipherName[LUKS_CIPHERNAME_L];
+	uint8_t		cipherMode[LUKS_CIPHERMODE_L];
+	uint8_t		hashSpec[LUKS_HASHSPEC_L];
+	uint32_t	payloadOffset;
+	uint32_t	keyBytes;
+	uint8_t		mkDigest[LUKS_DIGESTSIZE];
+	uint8_t		mkDigestSalt[LUKS_SALTSIZE];
+	uint32_t	mkDigestIterations;
+	uint8_t		uuid[UUID_STRING_L];
+} __attribute__((packed));
+
+static int probe_luks(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct luks_phdr *header;
+
+	header = blkid_probe_get_sb(pr, mag, struct luks_phdr);
+	if (header == NULL)
+		return -1;
+
+	blkid_probe_strncpy_uuid(pr, (unsigned char *) header->uuid,
+			sizeof(header->uuid));
+	blkid_probe_sprintf_version(pr, "%u", be16_to_cpu(header->version));
+	return 0;
+}
+
+const struct blkid_idinfo luks_idinfo =
+{
+	.name		= "crypto_LUKS",
+	.usage		= BLKID_USAGE_CRYPTO,
+	.probefunc	= probe_luks,
+	.magics		=
+	{
+		{ .magic = "LUKS\xba\xbe", .len = 6 },
+		{ NULL }
+	}
+};
diff --git a/src/superblocks/lvm.c b/src/superblocks/lvm.c
new file mode 100644
index 0000000..0afc773
--- /dev/null
+++ b/src/superblocks/lvm.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2012 Milan Broz <mbroz@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+#define LVM1_ID_LEN 128
+#define LVM2_ID_LEN 32
+
+struct lvm2_pv_label_header {
+	/* label_header */
+	uint8_t		id[8];		/* LABELONE */
+	uint64_t	sector_xl;	/* Sector number of this label */
+	uint32_t	crc_xl;		/* From next field to end of sector */
+	uint32_t	offset_xl;	/* Offset from start of struct to contents */
+	uint8_t		type[8];	/* LVM2 001 */
+	/* pv_header */
+	uint8_t		pv_uuid[LVM2_ID_LEN];
+} __attribute__ ((packed));
+
+struct lvm1_pv_label_header {
+	uint8_t id[2];			/* HM */
+	uint16_t version;		/* version 1 or 2 */
+	uint32_t _notused[10];		/* lvm1 internals */
+	uint8_t pv_uuid[LVM1_ID_LEN];
+} __attribute__ ((packed));
+
+#define LVM2_LABEL_SIZE 512
+static unsigned int lvm2_calc_crc(const void *buf, unsigned int size)
+{
+	static const unsigned int crctab[] = {
+		0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+		0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+		0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+		0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
+	};
+	unsigned int i, crc = 0xf597a6cf;
+	const uint8_t *data = (const uint8_t *) buf;
+
+	for (i = 0; i < size; i++) {
+		crc ^= *data++;
+		crc = (crc >> 4) ^ crctab[crc & 0xf];
+		crc = (crc >> 4) ^ crctab[crc & 0xf];
+	}
+	return crc;
+}
+
+/* Length of real UUID is always LVM2_ID_LEN */
+static void format_lvm_uuid(char *dst_uuid, char *src_uuid)
+{
+	unsigned int i, b;
+
+	for (i = 0, b = 1; i < LVM2_ID_LEN; i++, b <<= 1) {
+		if (b & 0x4444440)
+			*dst_uuid++ = '-';
+		*dst_uuid++ = *src_uuid++;
+	}
+	*dst_uuid = '\0';
+}
+
+static int probe_lvm2(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	int sector = mag->kboff << 1;
+	struct lvm2_pv_label_header *label;
+	char uuid[LVM2_ID_LEN + 7];
+	unsigned char *buf;
+
+	buf = blkid_probe_get_buffer(pr,
+			mag->kboff << 10,
+			512 + sizeof(struct lvm2_pv_label_header));
+	if (!buf)
+		return -1;
+
+	/* buf is at 0k or 1k offset; find label inside */
+	if (memcmp(buf, "LABELONE", 8) == 0) {
+		label = (struct lvm2_pv_label_header *) buf;
+	} else if (memcmp(buf + 512, "LABELONE", 8) == 0) {
+		label = (struct lvm2_pv_label_header *)(buf + 512);
+		sector++;
+	} else {
+		return 1;
+	}
+
+	if (le64_to_cpu(label->sector_xl) != (unsigned) sector)
+		return 1;
+
+	if (lvm2_calc_crc(&label->offset_xl, LVM2_LABEL_SIZE -
+			((char *) &label->offset_xl - (char *) label)) !=
+			le32_to_cpu(label->crc_xl)) {
+		DBG(DEBUG_PROBE,
+		    printf("LVM2: label checksum incorrect at sector %d\n",
+			   sector));
+		return 1;
+	}
+
+	format_lvm_uuid(uuid, (char *) label->pv_uuid);
+	blkid_probe_sprintf_uuid(pr, label->pv_uuid, sizeof(label->pv_uuid),
+			"%s", uuid);
+
+	/* the mag->magic is the same string as label->type,
+	 * but zero terminated */
+	blkid_probe_set_version(pr, mag->magic);
+
+	/* LVM (pvcreate) wipes begin of the device -- let's remember this
+	 * to resolve conflicts bettween LVM and partition tables, ...
+	 */
+	blkid_probe_set_wiper(pr, 0, 8 * 1024);
+
+	return 0;
+}
+
+static int probe_lvm1(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct lvm1_pv_label_header *label;
+	char uuid[LVM2_ID_LEN + 7];
+	unsigned int version;
+
+	label = blkid_probe_get_sb(pr, mag, struct lvm1_pv_label_header);
+	if (!label)
+		return -1;
+
+	version = le16_to_cpu(label->version);
+	if (version != 1 && version != 2)
+		return 1;
+
+	format_lvm_uuid(uuid, (char *) label->pv_uuid);
+	blkid_probe_sprintf_uuid(pr, label->pv_uuid, sizeof(label->pv_uuid),
+			"%s", uuid);
+
+	return 0;
+}
+
+struct verity_sb {
+	uint8_t  signature[8];	/* "verity\0\0" */
+	uint32_t version;	/* superblock version */
+	uint32_t hash_type;	/* 0 - Chrome OS, 1 - normal */
+	uint8_t  uuid[16];	/* UUID of hash device */
+	uint8_t  algorithm[32];/* hash algorithm name */
+	uint32_t data_block_size; /* data block in bytes */
+	uint32_t hash_block_size; /* hash block in bytes */
+	uint64_t data_blocks;	/* number of data blocks */
+	uint16_t salt_size;	/* salt size */
+	uint8_t  _pad1[6];
+	uint8_t  salt[256];	/* salt */
+	uint8_t  _pad2[168];
+} __attribute__((packed));
+
+static int probe_verity(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct verity_sb *sb;
+	unsigned int version;
+
+	sb = blkid_probe_get_sb(pr, mag, struct verity_sb);
+	if (sb == NULL)
+		return -1;
+
+	version = le32_to_cpu(sb->version);
+	if (version != 1)
+		return 1;
+
+	blkid_probe_set_uuid(pr, sb->uuid);
+	blkid_probe_sprintf_version(pr, "%u", version);
+	return 0;
+}
+
+/* NOTE: the original libblkid uses "lvm2pv" as a name */
+const struct blkid_idinfo lvm2_idinfo =
+{
+	.name		= "LVM2_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_lvm2,
+	.magics		=
+	{
+		{ .magic = "LVM2 001", .len = 8, .sboff = 0x218 },
+		{ .magic = "LVM2 001", .len = 8, .sboff = 0x018 },
+		{ .magic = "LVM2 001", .len = 8, .kboff = 1, .sboff = 0x018 },
+		{ .magic = "LVM2 001", .len = 8, .kboff = 1, .sboff = 0x218 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo lvm1_idinfo =
+{
+	.name		= "LVM1_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_lvm1,
+	.magics		=
+	{
+		{ .magic = "HM", .len = 2 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo snapcow_idinfo =
+{
+	.name		= "DM_snapshot_cow",
+	.usage		= BLKID_USAGE_OTHER,
+	.magics		=
+	{
+		{ .magic = "SnAp", .len = 4 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo verity_hash_idinfo =
+{
+	.name		= "DM_verity_hash",
+	.usage		= BLKID_USAGE_CRYPTO,
+	.probefunc	= probe_verity,
+	.magics		=
+	{
+		{ .magic = "verity\0\0", .len = 8 },
+		{ NULL }
+	}
+};
diff --git a/src/superblocks/minix.c b/src/superblocks/minix.c
new file mode 100644
index 0000000..54e7139
--- /dev/null
+++ b/src/superblocks/minix.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <string.h>
+#include "superblocks.h"
+#include "minix.h"
+
+static int probe_minix(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	unsigned char *ext;
+	int version;
+
+	/* for more details see magic strings below */
+	switch(mag->magic[1]) {
+	case '\023':
+		version = 1;
+		break;
+	case '\044':
+		version = 2;
+		break;
+	case '\115':
+		version = 3;
+		break;
+	default:
+		return -1;
+		break;
+	}
+
+	if (version <= 2) {
+		struct minix_super_block *sb;
+		uint32_t zones;
+
+		sb = blkid_probe_get_sb(pr, mag, struct minix_super_block);
+		if (!sb || sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
+			return -1;
+
+		zones = version == 2 ? sb->s_zones : sb->s_nzones;
+
+		/* sanity checks to be sure that the FS is really minix */
+		if (sb->s_imap_blocks * MINIX_BLOCK_SIZE * 8 < sb->s_ninodes + 1)
+			return -1;
+		if (sb->s_zmap_blocks * MINIX_BLOCK_SIZE * 8 < zones - sb->s_firstdatazone + 1)
+			return -1;
+
+	} else if (version == 3) {
+		struct minix3_super_block *sb;
+
+		sb = blkid_probe_get_sb(pr, mag, struct minix3_super_block);
+		if (!sb || sb->s_imap_blocks == 0 || sb->s_zmap_blocks == 0)
+			return -1;
+
+	}
+
+	/* unfortunately, some parts of ext3 is sometimes possible to
+	 * interpreted as minix superblock. So check for extN magic
+	 * string. (For extN magic string and offsets see ext.c.)
+	 */
+	ext = blkid_probe_get_buffer(pr, 0x400 + 0x38, 2);
+	if (ext && memcmp(ext, "\123\357", 2) == 0)
+		return -1;
+
+	blkid_probe_sprintf_version(pr, "%d", version);
+	return 0;
+}
+
+const struct blkid_idinfo minix_idinfo =
+{
+	.name		= "minix",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_minix,
+	.magics		=
+	{
+		/* version 1 */
+		{ .magic = "\177\023", .len = 2, .kboff = 1, .sboff = 0x10 },
+		{ .magic = "\217\023", .len = 2, .kboff = 1, .sboff = 0x10 },
+
+		/* version 2 */
+		{ .magic = "\150\044", .len = 2, .kboff = 1, .sboff = 0x10 },
+		{ .magic = "\170\044", .len = 2, .kboff = 1, .sboff = 0x10 },
+
+		/* version 3 */
+		{ .magic = "\132\115", .len = 2, .kboff = 1, .sboff = 0x18 },
+		{ NULL }
+	}
+};
+
diff --git a/src/superblocks/netware.c b/src/superblocks/netware.c
new file mode 100644
index 0000000..7ef2162
--- /dev/null
+++ b/src/superblocks/netware.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct netware_super_block {
+	uint8_t		SBH_Signature[4];
+	uint16_t	SBH_VersionMajor;
+	uint16_t	SBH_VersionMinor;
+	uint16_t	SBH_VersionMediaMajor;
+	uint16_t	SBH_VersionMediaMinor;
+	uint32_t	SBH_ItemsMoved;
+	uint8_t		SBH_InternalID[16];
+	uint32_t	SBH_PackedSize;
+	uint32_t	SBH_Checksum;
+	uint32_t	supersyncid;
+	int64_t		superlocation[4];
+	uint32_t	physSizeUsed;
+	uint32_t	sizeUsed;
+	uint32_t	superTimeStamp;
+	uint32_t	reserved0[1];
+	int64_t		SBH_LoggedPoolDataBlk;
+	int64_t		SBH_PoolDataBlk;
+	uint8_t		SBH_OldInternalID[16];
+	uint32_t	SBH_PoolToLVStartUTC;
+	uint32_t	SBH_PoolToLVEndUTC;
+	uint16_t	SBH_VersionMediaMajorCreate;
+	uint16_t	SBH_VersionMediaMinorCreate;
+	uint32_t	SBH_BlocksMoved;
+	uint32_t	SBH_TempBTSpBlk;
+	uint32_t	SBH_TempFTSpBlk;
+	uint32_t	SBH_TempFTSpBlk1;
+	uint32_t	SBH_TempFTSpBlk2;
+	uint32_t	nssMagicNumber;
+	uint32_t	poolClassID;
+	uint32_t	poolID;
+	uint32_t	createTime;
+	int64_t		SBH_LoggedVolumeDataBlk;
+	int64_t		SBH_VolumeDataBlk;
+	int64_t		SBH_SystemBeastBlkNum;
+	uint64_t	totalblocks;
+	uint16_t	SBH_Name[64];
+	uint8_t		SBH_VolumeID[16];
+	uint8_t		SBH_PoolID[16];
+	uint8_t		SBH_PoolInternalID[16];
+	uint64_t	SBH_Lsn;
+	uint32_t	SBH_SS_Enabled;
+	uint32_t	SBH_SS_CreateTime;
+	uint8_t		SBH_SS_OriginalPoolID[16];
+	uint8_t		SBH_SS_OriginalVolumeID[16];
+	uint8_t		SBH_SS_Guid[16];
+	uint16_t	SBH_SS_OriginalName[64];
+	uint32_t	reserved2[64-(2+46)];
+} __attribute__((__packed__));
+
+static int probe_netware(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct netware_super_block *nw;
+
+	nw = blkid_probe_get_sb(pr, mag, struct netware_super_block);
+	if (!nw)
+		return -1;
+
+	blkid_probe_set_uuid(pr, nw->SBH_PoolID);
+
+	blkid_probe_sprintf_version(pr, "%u.%02u",
+		 le16_to_cpu(nw->SBH_VersionMediaMajor),
+		 le16_to_cpu(nw->SBH_VersionMediaMinor));
+
+	return 0;
+}
+
+const struct blkid_idinfo netware_idinfo =
+{
+	.name		= "nss",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_netware,
+	.magics		=
+	{
+		{ .magic = "SPB5", .len = 4, .kboff = 4 },
+		{ NULL }
+	}
+};
+
+
diff --git a/src/superblocks/nilfs.c b/src/superblocks/nilfs.c
new file mode 100644
index 0000000..1f8f3a6
--- /dev/null
+++ b/src/superblocks/nilfs.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2010 by Jiro SEKIBA <jir@unicus.jp>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License
+ */
+#include <stddef.h>
+#include <string.h>
+
+#include "superblocks.h"
+#include "crc32.h"
+
+struct nilfs_super_block {
+	uint32_t	s_rev_level;
+	uint16_t	s_minor_rev_level;
+	uint16_t	s_magic;
+
+	uint16_t	s_bytes;
+
+	uint16_t	s_flags;
+	uint32_t	s_crc_seed;
+	uint32_t	s_sum;
+
+	uint32_t	s_log_block_size;
+
+	uint64_t	s_nsegments;
+	uint64_t	s_dev_size;
+	uint64_t	s_first_data_block;
+	uint32_t	s_blocks_per_segment;
+	uint32_t	s_r_segments_percentage;
+
+	uint64_t	s_last_cno;
+	uint64_t	s_last_pseg;
+	uint64_t	s_last_seq;
+	uint64_t	s_free_blocks_count;
+
+	uint64_t	s_ctime;
+
+	uint64_t	s_mtime;
+	uint64_t	s_wtime;
+	uint16_t	s_mnt_count;
+	uint16_t	s_max_mnt_count;
+	uint16_t	s_state;
+	uint16_t	s_errors;
+	uint64_t	s_lastcheck;
+
+	uint32_t	s_checkinterval;
+	uint32_t	s_creator_os;
+	uint16_t	s_def_resuid;
+	uint16_t	s_def_resgid;
+	uint32_t	s_first_ino;
+
+	uint16_t	s_inode_size;
+	uint16_t	s_dat_entry_size;
+	uint16_t	s_checkpoint_size;
+	uint16_t	s_segment_usage_size;
+
+	uint8_t		s_uuid[16];
+	char		s_volume_name[80];
+
+	uint32_t	s_c_interval;
+	uint32_t	s_c_block_max;
+	uint32_t	s_reserved[192];
+};
+
+/* nilfs2 magic string */
+#define NILFS_SB_MAGIC		"\x34\x34"
+/* nilfs2 super block offset */
+#define NILFS_SB_OFF		0x400
+/* nilfs2 super block offset in kB */
+#define NILFS_SB_KBOFF		(NILFS_SB_OFF >> 10)
+/* nilfs2 magic string offset within super block */
+#define NILFS_MAG_OFF		6
+
+static int probe_nilfs2(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct nilfs_super_block *sb;
+	static unsigned char sum[4];
+	const int sumoff = offsetof(struct nilfs_super_block, s_sum);
+	size_t bytes;
+	uint32_t crc;
+
+	sb = blkid_probe_get_sb(pr, mag, struct nilfs_super_block);
+	if (!sb)
+		return -1;
+
+	bytes = le16_to_cpu(sb->s_bytes);
+	crc = crc32(le32_to_cpu(sb->s_crc_seed), (unsigned char *)sb, sumoff);
+	crc = crc32(crc, sum, 4);
+	crc = crc32(crc, (unsigned char *)sb + sumoff + 4, bytes - sumoff - 4);
+
+	if (crc != le32_to_cpu(sb->s_sum))
+		return -1;
+
+	if (strlen(sb->s_volume_name))
+		blkid_probe_set_label(pr, (unsigned char *) sb->s_volume_name,
+				      sizeof(sb->s_volume_name));
+
+	blkid_probe_set_uuid(pr, sb->s_uuid);
+	blkid_probe_sprintf_version(pr, "%u", le32_to_cpu(sb->s_rev_level));
+
+	return 0;
+}
+
+const struct blkid_idinfo nilfs2_idinfo =
+{
+	.name		= "nilfs2",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_nilfs2,
+	.magics		=
+	{
+		{
+			.magic = NILFS_SB_MAGIC,
+			.len = 2,
+			.kboff = NILFS_SB_KBOFF,
+			.sboff = NILFS_MAG_OFF
+		},
+		{ NULL }
+	}
+};
diff --git a/src/superblocks/ntfs.c b/src/superblocks/ntfs.c
new file mode 100644
index 0000000..41c6b9c
--- /dev/null
+++ b/src/superblocks/ntfs.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "superblocks.h"
+
+struct ntfs_bios_parameters {
+	uint16_t	sector_size;	/* Size of a sector in bytes. */
+	uint8_t		sectors_per_cluster;	/* Size of a cluster in sectors. */
+	uint16_t	reserved_sectors;	/* zero */
+	uint8_t		fats;			/* zero */
+	uint16_t	root_entries;		/* zero */
+	uint16_t	sectors;		/* zero */
+	uint8_t		media_type;		/* 0xf8 = hard disk */
+	uint16_t	sectors_per_fat;	/* zero */
+	uint16_t	sectors_per_track;	/* irrelevant */
+	uint16_t	heads;			/* irrelevant */
+	uint32_t	hidden_sectors;		/* zero */
+	uint32_t	large_sectors;		/* zero */
+} __attribute__ ((__packed__));
+
+struct ntfs_super_block {
+	uint8_t		jump[3];
+	uint8_t		oem_id[8];	/* magic string */
+
+	struct ntfs_bios_parameters	bpb;
+
+	uint16_t	unused[2];
+	uint64_t	number_of_sectors;
+	uint64_t	mft_cluster_location;
+	uint64_t	mft_mirror_cluster_location;
+	int8_t		clusters_per_mft_record;
+	uint8_t		reserved1[3];
+	int8_t		cluster_per_index_record;
+	uint8_t		reserved2[3];
+	uint64_t	volume_serial;
+	uint32_t	checksum;
+} __attribute__((packed));
+
+struct master_file_table_record {
+	uint32_t	magic;
+	uint16_t	usa_ofs;
+	uint16_t	usa_count;
+	uint64_t	lsn;
+	uint16_t	sequence_number;
+	uint16_t	link_count;
+	uint16_t	attrs_offset;
+	uint16_t	flags;
+	uint32_t	bytes_in_use;
+	uint32_t	bytes_allocated;
+} __attribute__((__packed__));
+
+struct file_attribute {
+	uint32_t	type;
+	uint32_t	len;
+	uint8_t		non_resident;
+	uint8_t		name_len;
+	uint16_t	name_offset;
+	uint16_t	flags;
+	uint16_t	instance;
+	uint32_t	value_len;
+	uint16_t	value_offset;
+} __attribute__((__packed__));
+
+#define MFT_RECORD_VOLUME	3
+#define NTFS_MAX_CLUSTER_SIZE	(64 * 1024)
+
+enum {
+	MFT_RECORD_ATTR_VOLUME_NAME		= cpu_to_le32(0x60),
+	MFT_RECORD_ATTR_END			= cpu_to_le32(0xffffffff)
+};
+
+static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct ntfs_super_block *ns;
+	struct master_file_table_record *mft;
+
+	uint32_t sectors_per_cluster, mft_record_size, attr_off;
+	uint16_t sector_size;
+	uint64_t nr_clusters, off;
+	unsigned char *buf_mft;
+
+	ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block);
+	if (!ns)
+		return -1;
+
+	/*
+	 * Check bios parameters block
+	 */
+	sector_size = le16_to_cpu(ns->bpb.sector_size);
+	sectors_per_cluster = ns->bpb.sectors_per_cluster;
+
+	if (sector_size < 256 || sector_size > 4096)
+		return 1;
+
+	switch (sectors_per_cluster) {
+	case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
+		break;
+	default:
+		return 1;
+	}
+
+	if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) *
+			ns->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE)
+		return 1;
+
+	/* Unused fields must be zero */
+	if (le16_to_cpu(ns->bpb.reserved_sectors)
+	    || le16_to_cpu(ns->bpb.root_entries)
+	    || le16_to_cpu(ns->bpb.sectors)
+	    || le16_to_cpu(ns->bpb.sectors_per_fat)
+	    || le32_to_cpu(ns->bpb.large_sectors)
+	    || ns->bpb.fats)
+		return 1;
+
+	if ((uint8_t) ns->clusters_per_mft_record < 0xe1
+	    || (uint8_t) ns->clusters_per_mft_record > 0xf7) {
+
+		switch (ns->clusters_per_mft_record) {
+		case 1: case 2: case 4: case 8: case 16: case 32: case 64:
+			break;
+		default:
+			return 1;
+		}
+	}
+
+	if (ns->clusters_per_mft_record > 0)
+		mft_record_size = ns->clusters_per_mft_record *
+				  sectors_per_cluster * sector_size;
+	else
+		mft_record_size = 1 << (0 - ns->clusters_per_mft_record);
+
+	nr_clusters = le64_to_cpu(ns->number_of_sectors) / sectors_per_cluster;
+
+	if ((le64_to_cpu(ns->mft_cluster_location) > nr_clusters) ||
+	    (le64_to_cpu(ns->mft_mirror_cluster_location) > nr_clusters))
+		return 1;
+
+
+	off = le64_to_cpu(ns->mft_cluster_location) * sector_size *
+		sectors_per_cluster;
+
+	DBG(DEBUG_LOWPROBE, printf("NTFS: sector_size=%d, mft_record_size=%d, "
+			"sectors_per_cluster=%d, nr_clusters=%ju "
+			"cluster_offset=%jd\n",
+			(int) sector_size, mft_record_size,
+			sectors_per_cluster, nr_clusters,
+			off));
+
+	buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
+	if (!buf_mft)
+		return 1;
+
+	if (memcmp(buf_mft, "FILE", 4))
+		return 1;
+
+	off += MFT_RECORD_VOLUME * mft_record_size;
+
+	buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
+	if (!buf_mft)
+		return 1;
+
+	if (memcmp(buf_mft, "FILE", 4))
+		return 1;
+
+	mft = (struct master_file_table_record *) buf_mft;
+	attr_off = le16_to_cpu(mft->attrs_offset);
+
+	while (attr_off < mft_record_size &&
+	       attr_off <= le32_to_cpu(mft->bytes_allocated)) {
+
+		uint32_t attr_len;
+		struct file_attribute *attr;
+
+		attr = (struct file_attribute *) (buf_mft + attr_off);
+		attr_len = le32_to_cpu(attr->len);
+		if (!attr_len)
+			break;
+
+		if (attr->type == MFT_RECORD_ATTR_END)
+			break;
+		if (attr->type == MFT_RECORD_ATTR_VOLUME_NAME) {
+			unsigned int val_off = le16_to_cpu(attr->value_offset);
+			unsigned int val_len = le32_to_cpu(attr->value_len);
+			unsigned char *val = ((uint8_t *) attr) + val_off;
+
+			blkid_probe_set_utf8label(pr, val, val_len, BLKID_ENC_UTF16LE);
+			break;
+		}
+
+		if (UINT_MAX - attr_len < attr_off)
+			break;
+		attr_off += attr_len;
+	}
+
+	blkid_probe_sprintf_uuid(pr,
+			(unsigned char *) &ns->volume_serial,
+			sizeof(ns->volume_serial),
+			"%016" PRIX64, le64_to_cpu(ns->volume_serial));
+	return 0;
+}
+
+
+const struct blkid_idinfo ntfs_idinfo =
+{
+	.name		= "ntfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ntfs,
+	.magics		=
+	{
+		{ .magic = "NTFS    ", .len = 8, .sboff = 3 },
+		{ NULL }
+	}
+};
+
diff --git a/src/superblocks/nvidia_raid.c b/src/superblocks/nvidia_raid.c
new file mode 100644
index 0000000..dd86cdc
--- /dev/null
+++ b/src/superblocks/nvidia_raid.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct nv_metadata {
+	uint8_t		vendor[8];
+	uint32_t	size;
+	uint32_t	chksum;
+	uint16_t	version;
+} __attribute__((packed));
+
+#define NVIDIA_SIGNATURE		"NVIDIA"
+
+static int probe_nvraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct nv_metadata *nv;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200) - 2) * 0x200;
+	nv = (struct nv_metadata *)
+		blkid_probe_get_buffer(pr,
+				off,
+				sizeof(struct nv_metadata));
+	if (!nv)
+		return -1;
+
+	if (memcmp(nv->vendor, NVIDIA_SIGNATURE, sizeof(NVIDIA_SIGNATURE)-1) != 0)
+		return -1;
+	if (blkid_probe_sprintf_version(pr, "%u", le16_to_cpu(nv->version)) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off, sizeof(nv->vendor),
+				(unsigned char *) nv->vendor))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo nvraid_idinfo = {
+	.name		= "nvidia_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_nvraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/src/superblocks/ocfs.c b/src/superblocks/ocfs.c
new file mode 100644
index 0000000..82170ac
--- /dev/null
+++ b/src/superblocks/ocfs.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 1999, 2001 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct ocfs_volume_header {
+	unsigned char	minor_version[4];
+	unsigned char	major_version[4];
+	unsigned char	signature[128];
+	char		mount[128];
+	unsigned char   mount_len[2];
+} __attribute__((packed));
+
+struct ocfs_volume_label {
+	unsigned char	disk_lock[48];
+	char		label[64];
+	unsigned char	label_len[2];
+	unsigned char   vol_id[16];
+	unsigned char   vol_id_len[2];
+} __attribute__((packed));
+
+#define ocfsmajor(o) ( (uint32_t) o.major_version[0] \
+                   + (((uint32_t) o.major_version[1]) << 8) \
+                   + (((uint32_t) o.major_version[2]) << 16) \
+                   + (((uint32_t) o.major_version[3]) << 24))
+
+#define ocfsminor(o) ( (uint32_t) o.minor_version[0] \
+                   + (((uint32_t) o.minor_version[1]) << 8) \
+                   + (((uint32_t) o.minor_version[2]) << 16) \
+                   + (((uint32_t) o.minor_version[3]) << 24))
+
+#define ocfslabellen(o)	((uint32_t)o.label_len[0] + (((uint32_t) o.label_len[1]) << 8))
+#define ocfsmountlen(o)	((uint32_t)o.mount_len[0] + (((uint32_t) o.mount_len[1]) << 8))
+
+struct ocfs2_super_block {
+	uint8_t		i_signature[8];
+	uint32_t	i_generation;
+	int16_t		i_suballoc_slot;
+	uint16_t	i_suballoc_bit;
+	uint32_t	i_reserved0;
+	uint32_t	i_clusters;
+	uint32_t	i_uid;
+	uint32_t	i_gid;
+	uint64_t	i_size;
+	uint16_t	i_mode;
+	uint16_t	i_links_count;
+	uint32_t	i_flags;
+	uint64_t	i_atime;
+	uint64_t	i_ctime;
+	uint64_t	i_mtime;
+	uint64_t	i_dtime;
+	uint64_t	i_blkno;
+	uint64_t	i_last_eb_blk;
+	uint32_t	i_fs_generation;
+	uint32_t	i_atime_nsec;
+	uint32_t	i_ctime_nsec;
+	uint32_t	i_mtime_nsec;
+	uint64_t	i_reserved1[9];
+	uint64_t	i_pad1;
+	uint16_t	s_major_rev_level;
+	uint16_t	s_minor_rev_level;
+	uint16_t	s_mnt_count;
+	int16_t		s_max_mnt_count;
+	uint16_t	s_state;
+	uint16_t	s_errors;
+	uint32_t	s_checkinterval;
+	uint64_t	s_lastcheck;
+	uint32_t	s_creator_os;
+	uint32_t	s_feature_compat;
+	uint32_t	s_feature_incompat;
+	uint32_t	s_feature_ro_compat;
+	uint64_t	s_root_blkno;
+	uint64_t	s_system_dir_blkno;
+	uint32_t	s_blocksize_bits;
+	uint32_t	s_clustersize_bits;
+	uint16_t	s_max_slots;
+	uint16_t	s_reserved1;
+	uint32_t	s_reserved2;
+	uint64_t	s_first_cluster_group;
+	uint8_t		s_label[64];
+	uint8_t		s_uuid[16];
+} __attribute__((packed));
+
+struct oracle_asm_disk_label {
+	char dummy[32];
+	char dl_tag[8];
+	char dl_id[24];
+} __attribute__((packed));
+
+static int probe_ocfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	unsigned char *buf;
+	struct ocfs_volume_header ovh;
+	struct ocfs_volume_label ovl;
+	uint32_t maj, min;
+
+	/* header */
+	buf = blkid_probe_get_buffer(pr, mag->kboff << 10,
+			sizeof(struct ocfs_volume_header));
+	if (!buf)
+		return -1;
+	memcpy(&ovh, buf, sizeof(ovh));
+
+	/* label */
+	buf = blkid_probe_get_buffer(pr, (mag->kboff << 10) + 512,
+			sizeof(struct ocfs_volume_label));
+	if (!buf)
+		return -1;
+	memcpy(&ovl, buf, sizeof(ovl));
+
+	maj = ocfsmajor(ovh);
+	min = ocfsminor(ovh);
+
+	if (maj == 1)
+		blkid_probe_set_value(pr, "SEC_TYPE",
+				(unsigned char *) "ocfs1", sizeof("ocfs1"));
+	else if (maj >= 9)
+		blkid_probe_set_value(pr, "SEC_TYPE",
+				(unsigned char *) "ntocfs", sizeof("ntocfs"));
+
+	blkid_probe_set_label(pr, (unsigned char *) ovl.label,
+				ocfslabellen(ovl));
+	blkid_probe_set_value(pr, "MOUNT", (unsigned char *) ovh.mount,
+				ocfsmountlen(ovh));
+	blkid_probe_set_uuid(pr, ovl.vol_id);
+	blkid_probe_sprintf_version(pr, "%u.%u", maj, min);
+	return 0;
+}
+
+static int probe_ocfs2(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct ocfs2_super_block *osb;
+
+	osb = blkid_probe_get_sb(pr, mag, struct ocfs2_super_block);
+	if (!osb)
+		return -1;
+
+	blkid_probe_set_label(pr, (unsigned char *) osb->s_label, sizeof(osb->s_label));
+	blkid_probe_set_uuid(pr, osb->s_uuid);
+
+	blkid_probe_sprintf_version(pr, "%u.%u",
+		le16_to_cpu(osb->s_major_rev_level),
+		le16_to_cpu(osb->s_minor_rev_level));
+
+	return 0;
+}
+
+static int probe_oracleasm(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct oracle_asm_disk_label *dl;
+
+	dl = blkid_probe_get_sb(pr, mag, struct oracle_asm_disk_label);
+	if (!dl)
+		return -1;
+
+	blkid_probe_set_label(pr, (unsigned char *) dl->dl_id, sizeof(dl->dl_id));
+	return 0;
+}
+
+
+const struct blkid_idinfo ocfs_idinfo =
+{
+	.name		= "ocfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ocfs,
+	.minsz		= 14000 * 1024,
+	.magics		=
+	{
+		{ .magic = "OracleCFS", .len = 9, .kboff = 8 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo ocfs2_idinfo =
+{
+	.name		= "ocfs2",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ocfs2,
+	.minsz		= 14000 * 1024,
+	.magics		=
+	{
+		{ .magic = "OCFSV2", .len = 6, .kboff = 1 },
+		{ .magic = "OCFSV2", .len = 6, .kboff = 2 },
+		{ .magic = "OCFSV2", .len = 6, .kboff = 4 },
+		{ .magic = "OCFSV2", .len = 6, .kboff = 8 },
+		{ NULL }
+	}
+};
+
+/* Oracle ASM (Automatic Storage Management) */
+const struct blkid_idinfo oracleasm_idinfo =
+{
+	.name		= "oracleasm",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_oracleasm,
+	.magics		=
+	{
+		{ .magic = "ORCLDISK", .len = 8, .sboff = 32 },
+		{ NULL }
+	}
+};
+
diff --git a/src/superblocks/promise_raid.c b/src/superblocks/promise_raid.c
new file mode 100644
index 0000000..221146d
--- /dev/null
+++ b/src/superblocks/promise_raid.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct promise_metadata {
+	uint8_t	sig[24];
+};
+
+#define PDC_CONFIG_OFF		0x1200
+#define PDC_SIGNATURE		"Promise Technology, Inc."
+
+static int probe_pdcraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	unsigned int i;
+	static unsigned int sectors[] = {
+		63, 255, 256, 16, 399, 0
+	};
+
+	if (pr->size < 0x40000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	for (i = 0; sectors[i] != 0; i++) {
+		uint64_t off;
+		struct promise_metadata *pdc;
+
+		off = ((pr->size / 0x200) - sectors[i]) * 0x200;
+		pdc = (struct promise_metadata *)
+				blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct promise_metadata));
+		if (!pdc)
+			return -1;
+
+		if (memcmp(pdc->sig, PDC_SIGNATURE,
+				sizeof(PDC_SIGNATURE) - 1) == 0) {
+
+			if (blkid_probe_set_magic(pr, off, sizeof(pdc->sig),
+						(unsigned char *) pdc->sig))
+				return -1;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+const struct blkid_idinfo pdcraid_idinfo = {
+	.name		= "promise_fasttrack_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_pdcraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/src/superblocks/reiserfs.c b/src/superblocks/reiserfs.c
new file mode 100644
index 0000000..152571f
--- /dev/null
+++ b/src/superblocks/reiserfs.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 1999, 2001 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct reiserfs_super_block {
+	uint32_t	rs_blocks_count;
+	uint32_t	rs_free_blocks;
+	uint32_t	rs_root_block;
+	uint32_t	rs_journal_block;
+	uint32_t	rs_journal_dev;
+	uint32_t	rs_orig_journal_size;
+	uint32_t	rs_dummy2[5];
+	uint16_t	rs_blocksize;
+	uint16_t	rs_dummy3[3];
+	unsigned char	rs_magic[12];
+	uint32_t	rs_dummy4[5];
+	unsigned char	rs_uuid[16];
+	char		rs_label[16];
+} __attribute__((packed));
+
+struct reiser4_super_block {
+	unsigned char	rs4_magic[16];
+	uint16_t	rs4_dummy[2];
+	unsigned char	rs4_uuid[16];
+	unsigned char	rs4_label[16];
+	uint64_t	rs4_dummy2;
+} __attribute__((packed));
+
+static int probe_reiser(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct reiserfs_super_block *rs;
+	unsigned int blocksize;
+
+	rs = blkid_probe_get_sb(pr, mag, struct reiserfs_super_block);
+	if (!rs)
+		return -1;
+
+	blocksize = le16_to_cpu(rs->rs_blocksize);
+
+	/* The blocksize must be at least 512B */
+	if ((blocksize >> 9) == 0)
+		return -BLKID_ERR_PARAM;
+
+	/* If the superblock is inside the journal, we have the wrong one */
+	if (mag->kboff / (blocksize >> 9) > le32_to_cpu(rs->rs_journal_block) / 2)
+		return -BLKID_ERR_BIG;
+
+	/* LABEL/UUID are only valid for later versions of Reiserfs v3.6. */
+	if (mag->magic[6] == '2' || mag->magic[6] == '3') {
+		if (*rs->rs_label)
+			blkid_probe_set_label(pr,
+					(unsigned char *) rs->rs_label,
+					sizeof(rs->rs_label));
+		blkid_probe_set_uuid(pr, rs->rs_uuid);
+	}
+
+	if (mag->magic[6] == '3')
+		blkid_probe_set_version(pr, "JR");
+	else if (mag->magic[6] == '2')
+		blkid_probe_set_version(pr, "3.6");
+	else
+		blkid_probe_set_version(pr, "3.5");
+
+	return 0;
+}
+
+static int probe_reiser4(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct reiser4_super_block *rs4;
+
+	rs4 = blkid_probe_get_sb(pr, mag, struct reiser4_super_block);
+	if (!rs4)
+		return -1;
+
+	if (*rs4->rs4_label)
+		blkid_probe_set_label(pr, rs4->rs4_label, sizeof(rs4->rs4_label));
+	blkid_probe_set_uuid(pr, rs4->rs4_uuid);
+	blkid_probe_set_version(pr, "4");
+
+	return 0;
+}
+
+
+const struct blkid_idinfo reiser_idinfo =
+{
+	.name		= "reiserfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_reiser,
+	.minsz		= 128 * 1024,
+	.magics		=
+	{
+		{ .magic = "ReIsErFs",  .len = 8, .kboff = 8,  .sboff = 0x34 },
+		{ .magic = "ReIsEr2Fs", .len = 9, .kboff = 64, .sboff = 0x34 },
+		{ .magic = "ReIsEr3Fs", .len = 9, .kboff = 64, .sboff = 0x34 },
+		{ .magic = "ReIsErFs",  .len = 8, .kboff = 64, .sboff = 0x34 },
+		{ .magic = "ReIsErFs",  .len = 8, .kboff =  8, .sboff = 20   },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo reiser4_idinfo =
+{
+	.name		= "reiser4",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_reiser4,
+	.minsz		= 128 * 1024,
+	.magics		=
+	{
+		{ .magic = "ReIsEr4", .len = 7, .kboff = 64 },
+		{ NULL }
+	}
+};
+
+
+
+
diff --git a/src/superblocks/romfs.c b/src/superblocks/romfs.c
new file mode 100644
index 0000000..91ef996
--- /dev/null
+++ b/src/superblocks/romfs.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 1999, 2001 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct romfs_super_block {
+	unsigned char	ros_magic[8];
+	uint32_t	ros_dummy1[2];
+	unsigned char	ros_volume[16];
+} __attribute__((packed));
+
+static int probe_romfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct romfs_super_block *ros;
+
+	ros = blkid_probe_get_sb(pr, mag, struct romfs_super_block);
+	if (!ros)
+		return -1;
+
+	if (strlen((char *) ros->ros_volume))
+		blkid_probe_set_label(pr, ros->ros_volume,
+				sizeof(ros->ros_volume));
+	return 0;
+}
+
+const struct blkid_idinfo romfs_idinfo =
+{
+	.name		= "romfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_romfs,
+	.magics		=
+	{
+		{ .magic = "-rom1fs-", .len = 8 },
+		{ NULL }
+	}
+};
+
diff --git a/src/superblocks/silicon_raid.c b/src/superblocks/silicon_raid.c
new file mode 100644
index 0000000..fef5801
--- /dev/null
+++ b/src/superblocks/silicon_raid.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2005 Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "superblocks.h"
+
+struct silicon_metadata {
+	uint8_t		unknown0[0x2E];
+	uint8_t		ascii_version[0x36 - 0x2E];
+	uint8_t		diskname[0x56 - 0x36];
+	uint8_t		unknown1[0x60 - 0x56];
+	uint32_t	magic;
+	uint32_t	unknown1a[0x6C - 0x64];
+	uint32_t	array_sectors_low;
+	uint32_t	array_sectors_high;
+	uint8_t		unknown2[0x78 - 0x74];
+	uint32_t	thisdisk_sectors;
+	uint8_t		unknown3[0x100 - 0x7C];
+	uint8_t		unknown4[0x104 - 0x100];
+	uint16_t	product_id;
+	uint16_t	vendor_id;
+	uint16_t	minor_ver;
+	uint16_t	major_ver;
+} __attribute__((packed));
+
+#define SILICON_MAGIC		0x2F000000
+
+
+static int probe_silraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct silicon_metadata *sil;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200) - 1) * 0x200;
+
+	sil = (struct silicon_metadata *)
+			blkid_probe_get_buffer(pr, off,
+				sizeof(struct silicon_metadata));
+	if (!sil)
+		return -1;
+
+	if (le32_to_cpu(sil->magic) != SILICON_MAGIC)
+		return -1;
+
+	if (blkid_probe_sprintf_version(pr, "%u.%u",
+				le16_to_cpu(sil->major_ver),
+				le16_to_cpu(sil->minor_ver)) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr,
+			off + offsetof(struct silicon_metadata, magic),
+			sizeof(sil->magic),
+			(unsigned char *) &sil->magic))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo silraid_idinfo = {
+	.name		= "silicon_medley_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_silraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/src/superblocks/squashfs.c b/src/superblocks/squashfs.c
new file mode 100644
index 0000000..45f1029
--- /dev/null
+++ b/src/superblocks/squashfs.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "bitops.h"	/* swab16() */
+#include "superblocks.h"
+
+struct sqsh_super_block {
+	uint32_t	s_magic;
+	uint32_t	inodes;
+	uint32_t	bytes_used_2;
+	uint32_t	uid_start_2;
+	uint32_t	guid_start_2;
+	uint32_t	inode_table_start_2;
+	uint32_t	directory_table_start_2;
+	uint16_t	s_major;
+	uint16_t	s_minor;
+} __attribute__((packed));
+
+static int probe_squashfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct sqsh_super_block *sq;
+
+	sq = blkid_probe_get_sb(pr, mag, struct sqsh_super_block);
+	if (!sq)
+		return -1;
+
+	if (strcmp(mag->magic, "sqsh") == 0 ||
+	    strcmp(mag->magic, "qshs") == 0)
+		blkid_probe_sprintf_version(pr, "%u.%u",
+				sq->s_major,
+				sq->s_minor);
+	else
+		blkid_probe_sprintf_version(pr, "%u.%u",
+				swab16(sq->s_major),
+				swab16(sq->s_minor));
+	return 0;
+}
+
+const struct blkid_idinfo squashfs_idinfo =
+{
+	.name		= "squashfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_squashfs,
+	.magics		=
+	{
+		{ .magic = "sqsh", .len = 4 },
+		{ .magic = "hsqs", .len = 4 }, /* swap */
+
+		/* LZMA version */
+		{ .magic = "qshs", .len = 4 },
+		{ .magic = "shsq", .len = 4 }, /* swap */
+		{ NULL }
+	}
+};
+
+
diff --git a/src/superblocks/superblocks.c b/src/superblocks/superblocks.c
new file mode 100644
index 0000000..879a40f
--- /dev/null
+++ b/src/superblocks/superblocks.c
@@ -0,0 +1,768 @@
+/*
+ * superblocks.c - reads information from filesystem and raid superblocks
+ *
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdarg.h>
+
+#include "superblocks.h"
+
+/**
+ * SECTION:superblocks
+ * @title: Superblocks probing
+ * @short_description: filesystems and raids superblocks probing.
+ *
+ * The library API has been originally designed for superblocks probing only.
+ * This is reason why some *deprecated* superblock specific functions don't use
+ * '_superblocks_' namespace in the function name. Please, don't use these
+ * functions in new code.
+ *
+ * The 'superblocks' probers support NAME=value (tags) interface only. The
+ * superblocks probing is enabled by default (and controlled by
+ * blkid_probe_enable_superblocks()).
+ *
+ * Currently supported tags:
+ *
+ * @TYPE: filesystem type
+ *
+ * @SEC_TYPE: secondary filesystem type
+ *
+ * @LABEL: filesystem label
+ *
+ * @LABEL_RAW: raw label from FS superblock
+ *
+ * @UUID: filesystem UUID (lower case)
+ *
+ * @UUID_SUB: subvolume uuid (e.g. btrfs)
+ *
+ * @UUID_RAW: raw UUID from FS superblock
+ *
+ * @EXT_JOURNAL: external journal UUID
+ *
+ * @USAGE:  usage string: "raid", "filesystem", ...
+ *
+ * @VERSION: filesystem version
+ *
+ * @MOUNT: cluster mount name (?) -- ocfs only
+ *
+ * @SBMAGIC: super block magic string
+ *
+ * @SBMAGIC_OFFSET: offset of SBMAGIC
+ *
+ * @FSSIZE: size of filessystem [not-implemented yet]
+ */
+
+static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn);
+static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn);
+
+static int blkid_probe_set_usage(blkid_probe pr, int usage);
+
+
+/*
+ * Superblocks chains probing functions
+ */
+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,
+
+	&drbd_idinfo,
+	&drbdproxy_datalog_idinfo,
+	&lvm2_idinfo,
+	&lvm1_idinfo,
+	&snapcow_idinfo,
+	&verity_hash_idinfo,
+	&luks_idinfo,
+	&vmfs_volume_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,
+	&ubifs_idinfo,
+	&bfs_idinfo,
+	&vmfs_fs_idinfo,
+	&befs_idinfo,
+	&nilfs2_idinfo,
+	&exfat_idinfo
+};
+
+/*
+ * Driver definition
+ */
+const struct blkid_chaindrv superblocks_drv = {
+	.id           = BLKID_CHAIN_SUBLKS,
+	.name         = "superblocks",
+	.dflt_enabled = TRUE,
+	.dflt_flags   = BLKID_SUBLKS_DEFAULT,
+	.idinfos      = idinfos,
+	.nidinfos     = ARRAY_SIZE(idinfos),
+	.has_fltr     = TRUE,
+	.probe        = superblocks_probe,
+	.safeprobe    = superblocks_safeprobe,
+};
+
+/**
+ * blkid_probe_enable_superblocks:
+ * @pr: probe
+ * @enable: TRUE/FALSE
+ *
+ * Enables/disables the superblocks probing for non-binary interface.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_enable_superblocks(blkid_probe pr, int enable)
+{
+	if (!pr)
+		return -1;
+	pr->chains[BLKID_CHAIN_SUBLKS].enabled = enable;
+	return 0;
+}
+
+/**
+ * blkid_probe_set_superblocks_flags:
+ * @pr: prober
+ * @flags: BLKID_SUBLKS_* flags
+ *
+ * Sets probing flags to the superblocks prober. This function is optional, the
+ * default are BLKID_SUBLKS_DEFAULTS flags.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_set_superblocks_flags(blkid_probe pr, int flags)
+{
+	if (!pr)
+		return -1;
+
+	pr->chains[BLKID_CHAIN_SUBLKS].flags = flags;
+	return 0;
+}
+
+/**
+ * blkid_probe_reset_superblocks_filter:
+ * @pr: prober
+ *
+ * Resets superblocks probing filter
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_reset_superblocks_filter(blkid_probe pr)
+{
+	return __blkid_probe_reset_filter(pr, BLKID_CHAIN_SUBLKS);
+}
+
+/**
+ * blkid_probe_invert_superblocks_filter:
+ * @pr: prober
+ *
+ * Inverts superblocks probing filter
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_invert_superblocks_filter(blkid_probe pr)
+{
+	return __blkid_probe_invert_filter(pr, BLKID_CHAIN_SUBLKS);
+}
+
+/**
+ * blkid_probe_filter_superblocks_type:
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @names: NULL terminated array of probing function names (e.g. "vfat").
+ *
+ *  %BLKID_FLTR_NOTIN  - probe for all items which are NOT IN @names;
+ *
+ *  %BLKID_FLTR_ONLYIN - probe for items which are IN @names
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_filter_superblocks_type(blkid_probe pr, int flag, char *names[])
+{
+	return __blkid_probe_filter_types(pr, BLKID_CHAIN_SUBLKS, flag, names);
+}
+
+/**
+ * blkid_probe_filter_superblocks_usage:
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @usage: BLKID_USAGE_* flags
+ *
+ *  %BLKID_FLTR_NOTIN  - probe for all items which are NOT IN @usage;
+ *
+ *  %BLKID_FLTR_ONLYIN - probe for items which are IN @usage
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_filter_superblocks_usage(blkid_probe pr, int flag, int usage)
+{
+	unsigned long *fltr;
+	struct blkid_chain *chn;
+	size_t i;
+
+	if (!pr)
+		return -1;
+
+	fltr = blkid_probe_get_filter(pr, BLKID_CHAIN_SUBLKS, TRUE);
+	if (!fltr)
+		return -1;
+
+	chn = &pr->chains[BLKID_CHAIN_SUBLKS];
+
+	for (i = 0; i < chn->driver->nidinfos; i++) {
+		const struct blkid_idinfo *id = chn->driver->idinfos[i];
+
+		if (id->usage & usage) {
+			if (flag & BLKID_FLTR_NOTIN)
+				blkid_bmp_set_item(chn->fltr, i);
+		} else if (flag & BLKID_FLTR_ONLYIN)
+			blkid_bmp_set_item(chn->fltr, i);
+	}
+	DBG(DEBUG_LOWPROBE, printf("a new probing usage-filter initialized\n"));
+	return 0;
+}
+
+/**
+ * blkid_known_fstype:
+ * @fstype: filesystem name
+ *
+ * Returns: 1 for known filesytems, or 0 for unknown filesystem.
+ */
+int blkid_known_fstype(const char *fstype)
+{
+	size_t i;
+
+	if (!fstype)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(idinfos); i++) {
+		const struct blkid_idinfo *id = idinfos[i];
+		if (strcmp(id->name, fstype) == 0)
+			return 1;
+	}
+	return 0;
+}
+
+/**
+ * blkid_superblocks_get_name:
+ * @idx: number >= 0
+ * @name: returns name of supported filesystem/raid (optional)
+ * @usage: returns BLKID_USAGE_* flags, (optional)
+ *
+ * Returns: -1 if @idx is out of range, or 0 on success.
+ */
+int blkid_superblocks_get_name(size_t idx, const char **name, int *usage)
+{
+	if (idx < ARRAY_SIZE(idinfos)) {
+		if (name)
+			*name = idinfos[idx]->name;
+		if (usage)
+			*usage = idinfos[idx]->usage;
+		return 0;
+	}
+	return -1;
+}
+
+/*
+ * The blkid_do_probe() backend.
+ */
+static int superblocks_probe(blkid_probe pr, struct blkid_chain *chn)
+{
+	size_t i;
+
+	if (!pr || chn->idx < -1)
+		return -1;
+	blkid_probe_chain_reset_vals(pr, chn);
+
+	DBG(DEBUG_LOWPROBE,
+		printf("--> starting probing loop [SUBLKS idx=%d]\n",
+		chn->idx));
+
+	if (pr->size <= 0 || (pr->size <= 1024 && !S_ISCHR(pr->mode)))
+		/* Ignore very very small block devices or regular files (e.g.
+		 * extended partitions). Note that size of the UBI char devices
+		 * is 1 byte */
+		goto nothing;
+
+	i = chn->idx < 0 ? 0 : chn->idx + 1U;
+
+	for ( ; i < ARRAY_SIZE(idinfos); i++) {
+		const struct blkid_idinfo *id;
+		const struct blkid_idmag *mag = NULL;
+		blkid_loff_t off = 0;
+		int rc = 0;
+
+		chn->idx = i;
+		id = idinfos[i];
+
+		if (chn->fltr && blkid_bmp_get_item(chn->fltr, i)) {
+			DBG(DEBUG_LOWPROBE, printf("filter out: %s\n", id->name));
+			continue;
+		}
+
+		if (id->minsz && id->minsz > pr->size)
+			continue;	/* the device is too small */
+
+		/* don't probe for RAIDs, swap or journal on CD/DVDs */
+		if ((id->usage & (BLKID_USAGE_RAID | BLKID_USAGE_OTHER)) &&
+		    blkid_probe_is_cdrom(pr))
+			continue;
+
+		/* don't probe for RAIDs on floppies */
+		if ((id->usage & BLKID_USAGE_RAID) && blkid_probe_is_tiny(pr))
+			continue;
+
+		DBG(DEBUG_LOWPROBE, printf("[%zd] %s:\n", i, id->name));
+
+		if (blkid_probe_get_idmag(pr, id, &off, &mag))
+			continue;
+
+		/* final check by probing function */
+		if (id->probefunc) {
+			DBG(DEBUG_LOWPROBE, printf("\tcall probefunc()\n"));
+			if (id->probefunc(pr, mag) != 0) {
+				blkid_probe_chain_reset_vals(pr, chn);
+				continue;
+			}
+		}
+
+		/* all cheks passed */
+		if (chn->flags & BLKID_SUBLKS_TYPE)
+			rc = blkid_probe_set_value(pr, "TYPE",
+				(unsigned char *) id->name,
+				strlen(id->name) + 1);
+
+		if (!rc)
+			rc = blkid_probe_set_usage(pr, id->usage);
+
+		if (!rc && mag)
+			rc = blkid_probe_set_magic(pr, off, mag->len,
+					(unsigned char *) mag->magic);
+		if (rc) {
+			blkid_probe_chain_reset_vals(pr, chn);
+			DBG(DEBUG_LOWPROBE, printf("failed to set result -- ingnore\n"));
+			continue;
+		}
+
+		DBG(DEBUG_LOWPROBE,
+			printf("<-- leaving probing loop (type=%s) [SUBLKS idx=%d]\n",
+			id->name, chn->idx));
+		return 0;
+	}
+
+nothing:
+	DBG(DEBUG_LOWPROBE,
+		printf("<-- leaving probing loop (failed) [SUBLKS idx=%d]\n",
+		chn->idx));
+	return 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.
+ *
+ * The function does not check for filesystems when a RAID or crypto signature
+ * is detected.  The function also does not check for collision between RAIDs
+ * and crypto devices. The first detected RAID or crypto device is returned.
+ *
+ * The function does not probe for ambivalent results on very small devices
+ * (e.g. floppies), on small devices the first detected filesystem is returned.
+ */
+static int superblocks_safeprobe(blkid_probe pr, struct blkid_chain *chn)
+{
+	struct blkid_prval vals[BLKID_NVALS_SUBLKS];
+	int nvals = BLKID_NVALS_SUBLKS;
+	int idx = -1;
+	int count = 0;
+	int intol = 0;
+	int rc;
+
+	while ((rc = superblocks_probe(pr, chn)) == 0) {
+
+		if (blkid_probe_is_tiny(pr) && !count)
+			/* floppy or so -- returns the first result. */
+			return 0;
+
+		count++;
+
+		if (chn->idx >= 0 &&
+		    idinfos[chn->idx]->usage & (BLKID_USAGE_RAID | BLKID_USAGE_CRYPTO))
+			break;
+
+		if (chn->idx >= 0 &&
+		    !(idinfos[chn->idx]->flags & BLKID_IDINFO_TOLERANT))
+			intol++;
+
+		if (count == 1) {
+			/* save the first result */
+			nvals = blkid_probe_chain_copy_vals(pr, chn, vals, nvals);
+			idx = chn->idx;
+		}
+	}
+
+	if (rc < 0)
+		return rc;		/* error */
+
+	if (count > 1 && intol) {
+		DBG(DEBUG_LOWPROBE,
+			printf("ERROR: superblocks chain: "
+			       "ambivalent result detected (%d filesystems)!\n",
+			       count));
+		return -2;		/* error, ambivalent result (more FS) */
+	}
+	if (!count)
+		return 1;		/* nothing detected */
+
+	if (idx != -1) {
+		/* restore the first result */
+		blkid_probe_chain_reset_vals(pr, chn);
+		blkid_probe_append_vals(pr, vals, nvals);
+		chn->idx = idx;
+	}
+
+	/*
+	 * 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 (chn->idx >= 0 && idinfos[chn->idx]->usage & BLKID_USAGE_RAID)
+		pr->prob_flags |= BLKID_PROBE_FL_IGNORE_PT;
+
+	return 0;
+}
+
+int blkid_probe_set_version(blkid_probe pr, const char *version)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	if (chn->flags & BLKID_SUBLKS_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, ...)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	int rc = 0;
+
+	if (chn->flags & BLKID_SUBLKS_VERSION) {
+		va_list 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)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	char *u = NULL;
+
+	if (!(chn->flags & BLKID_SUBLKS_USAGE))
+		return 0;
+
+	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);
+}
+
+int blkid_probe_set_label(blkid_probe pr, unsigned char *label, size_t len)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	struct blkid_prval *v;
+	if (len > BLKID_PROBVAL_BUFSIZ)
+		len = BLKID_PROBVAL_BUFSIZ;
+
+	if ((chn->flags & BLKID_SUBLKS_LABELRAW) &&
+	    blkid_probe_set_value(pr, "LABEL_RAW", label, len) < 0)
+		return -1;
+	if (!(chn->flags & BLKID_SUBLKS_LABEL))
+		return 0;
+	v = blkid_probe_assign_value(pr, "LABEL");
+	if (!v)
+		return -1;
+
+	if (len == BLKID_PROBVAL_BUFSIZ)
+		len--;				/* make a space for \0 */
+
+	memcpy(v->data, label, len);
+	v->data[len] = '\0';
+
+	v->len = blkid_rtrim_whitespace(v->data) + 1;
+	if (v->len == 1)
+		blkid_probe_reset_last_value(pr);
+	return 0;
+}
+
+int blkid_probe_set_utf8label(blkid_probe pr, unsigned char *label,
+				size_t len, int enc)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	struct blkid_prval *v;
+
+	if ((chn->flags & BLKID_SUBLKS_LABELRAW) &&
+	    blkid_probe_set_value(pr, "LABEL_RAW", label, len) < 0)
+		return -1;
+	if (!(chn->flags & BLKID_SUBLKS_LABEL))
+		return 0;
+	v = blkid_probe_assign_value(pr, "LABEL");
+	if (!v)
+		return -1;
+
+	blkid_encode_to_utf8(enc, v->data, sizeof(v->data), label, len);
+	v->len = blkid_rtrim_whitespace(v->data) + 1;
+	if (v->len == 1)
+		blkid_probe_reset_last_value(pr);
+	return 0;
+}
+
+/* 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)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++)
+		if (buf[i])
+			return 0;
+	return 1;
+}
+
+int blkid_probe_sprintf_uuid(blkid_probe pr, unsigned char *uuid,
+				size_t len, const char *fmt, ...)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(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 ((chn->flags & BLKID_SUBLKS_UUIDRAW) &&
+	    blkid_probe_set_value(pr, "UUID_RAW", uuid, len) < 0)
+		return -1;
+	if (!(chn->flags & BLKID_SUBLKS_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) {
+		size_t i;
+		struct blkid_prval *v = __blkid_probe_get_value(pr,
+						blkid_probe_numof_values(pr));
+		if (v) {
+			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;
+}
+
+/* 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)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(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 ((chn->flags & BLKID_SUBLKS_UUIDRAW) &&
+	    blkid_probe_set_value(pr, "UUID_RAW", str, len) < 0)
+		return -1;
+	if (!(chn->flags & BLKID_SUBLKS_UUID))
+		return 0;
+
+	v = blkid_probe_assign_value(pr, "UUID");
+	if (v) {
+		if (len == BLKID_PROBVAL_BUFSIZ)
+			len--;		/* make a space for \0 */
+
+		memcpy((char *) v->data, str, len);
+		v->data[len] = '\0';
+		v->len = len + 1;
+		return 0;
+	}
+	return -1;
+}
+
+/* default _set_uuid function to set DCE UUIDs */
+int blkid_probe_set_uuid_as(blkid_probe pr, unsigned char *uuid, const char *name)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+	struct blkid_prval *v;
+
+	if (uuid_is_empty(uuid, 16))
+		return 0;
+
+	if (!name) {
+		if ((chn->flags & BLKID_SUBLKS_UUIDRAW) &&
+		    blkid_probe_set_value(pr, "UUID_RAW", uuid, 16) < 0)
+			return -1;
+		if (!(chn->flags & BLKID_SUBLKS_UUID))
+			return 0;
+
+		v = blkid_probe_assign_value(pr, "UUID");
+	} else
+		v = blkid_probe_assign_value(pr, name);
+
+	blkid_unparse_uuid(uuid, (char *) v->data, sizeof(v->data));
+	v->len = 37;
+
+	return 0;
+}
+
+int blkid_probe_set_uuid(blkid_probe pr, unsigned char *uuid)
+{
+	return blkid_probe_set_uuid_as(pr, uuid, NULL);
+}
+
+/**
+ * blkid_probe_set_request:
+ * @pr: probe
+ * @flags: BLKID_PROBREQ_* (deprecated) or BLKID_SUBLKS_* flags
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_set_superblocks_flags().
+ */
+int blkid_probe_set_request(blkid_probe pr, int flags)
+{
+	return blkid_probe_set_superblocks_flags(pr, flags);
+}
+
+/**
+ * blkid_probe_reset_filter:
+ * @pr: prober
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_reset_superblocks_filter().
+ */
+int blkid_probe_reset_filter(blkid_probe pr)
+{
+	return __blkid_probe_reset_filter(pr, BLKID_CHAIN_SUBLKS);
+}
+
+/**
+ * blkid_probe_invert_filter:
+ * @pr: prober
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_invert_superblocks_filter().
+ */
+int blkid_probe_invert_filter(blkid_probe pr)
+{
+	return __blkid_probe_invert_filter(pr, BLKID_CHAIN_SUBLKS);
+}
+
+/**
+ * blkid_probe_filter_types
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @names: NULL terminated array of probing function names (e.g. "vfat").
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_filter_superblocks_types().
+ */
+int blkid_probe_filter_types(blkid_probe pr, int flag, char *names[])
+{
+	return __blkid_probe_filter_types(pr, BLKID_CHAIN_SUBLKS, flag, names);
+}
+
+/**
+ * blkid_probe_filter_usage
+ * @pr: prober
+ * @flag: filter BLKID_FLTR_{NOTIN,ONLYIN} flag
+ * @usage: BLKID_USAGE_* flags
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ *
+ * Deprecated: Use blkid_probe_filter_superblocks_usage().
+ */
+int blkid_probe_filter_usage(blkid_probe pr, int flag, int usage)
+{
+	return blkid_probe_filter_superblocks_usage(pr, flag, usage);
+}
+
+
diff --git a/src/superblocks/superblocks.h b/src/superblocks/superblocks.h
new file mode 100644
index 0000000..08f1438
--- /dev/null
+++ b/src/superblocks/superblocks.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008-2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#ifndef _BLKID_SUPERBLOCKS_H
+#define _BLKID_SUPERBLOCKS_H
+
+#include "blkidP.h"
+
+extern const struct blkid_idinfo cramfs_idinfo;
+extern const struct blkid_idinfo swap_idinfo;
+extern const struct blkid_idinfo swsuspend_idinfo;
+extern const struct blkid_idinfo adraid_idinfo;
+extern const struct blkid_idinfo ddfraid_idinfo;
+extern const struct blkid_idinfo iswraid_idinfo;
+extern const struct blkid_idinfo jmraid_idinfo;
+extern const struct blkid_idinfo lsiraid_idinfo;
+extern const struct blkid_idinfo nvraid_idinfo;
+extern const struct blkid_idinfo pdcraid_idinfo;
+extern const struct blkid_idinfo silraid_idinfo;
+extern const struct blkid_idinfo viaraid_idinfo;
+extern const struct blkid_idinfo linuxraid_idinfo;
+extern const struct blkid_idinfo ext4dev_idinfo;
+extern const struct blkid_idinfo ext4_idinfo;
+extern const struct blkid_idinfo ext3_idinfo;
+extern const struct blkid_idinfo ext2_idinfo;
+extern const struct blkid_idinfo jbd_idinfo;
+extern const struct blkid_idinfo jfs_idinfo;
+extern const struct blkid_idinfo xfs_idinfo;
+extern const struct blkid_idinfo gfs_idinfo;
+extern const struct blkid_idinfo gfs2_idinfo;
+extern const struct blkid_idinfo romfs_idinfo;
+extern const struct blkid_idinfo ocfs_idinfo;
+extern const struct blkid_idinfo ocfs2_idinfo;
+extern const struct blkid_idinfo oracleasm_idinfo;
+extern const struct blkid_idinfo reiser_idinfo;
+extern const struct blkid_idinfo reiser4_idinfo;
+extern const struct blkid_idinfo hfs_idinfo;
+extern const struct blkid_idinfo hfsplus_idinfo;
+extern const struct blkid_idinfo ntfs_idinfo;
+extern const struct blkid_idinfo iso9660_idinfo;
+extern const struct blkid_idinfo udf_idinfo;
+extern const struct blkid_idinfo vxfs_idinfo;
+extern const struct blkid_idinfo minix_idinfo;
+extern const struct blkid_idinfo vfat_idinfo;
+extern const struct blkid_idinfo ufs_idinfo;
+extern const struct blkid_idinfo hpfs_idinfo;
+extern const struct blkid_idinfo lvm2_idinfo;
+extern const struct blkid_idinfo lvm1_idinfo;
+extern const struct blkid_idinfo snapcow_idinfo;
+extern const struct blkid_idinfo verity_hash_idinfo;
+extern const struct blkid_idinfo luks_idinfo;
+extern const struct blkid_idinfo highpoint37x_idinfo;
+extern const struct blkid_idinfo highpoint45x_idinfo;
+extern const struct blkid_idinfo squashfs_idinfo;
+extern const struct blkid_idinfo netware_idinfo;
+extern const struct blkid_idinfo sysv_idinfo;
+extern const struct blkid_idinfo xenix_idinfo;
+extern const struct blkid_idinfo btrfs_idinfo;
+extern const struct blkid_idinfo ubifs_idinfo;
+extern const struct blkid_idinfo zfs_idinfo;
+extern const struct blkid_idinfo bfs_idinfo;
+extern const struct blkid_idinfo vmfs_volume_idinfo;
+extern const struct blkid_idinfo vmfs_fs_idinfo;
+extern const struct blkid_idinfo drbd_idinfo;
+extern const struct blkid_idinfo drbdproxy_datalog_idinfo;
+extern const struct blkid_idinfo befs_idinfo;
+extern const struct blkid_idinfo nilfs2_idinfo;
+extern const struct blkid_idinfo exfat_idinfo;
+
+/*
+ * superblock functions
+ */
+extern int blkid_probe_set_version(blkid_probe pr, const char *version);
+extern int blkid_probe_sprintf_version(blkid_probe pr, const char *fmt, ...)
+		__attribute__ ((__format__ (__printf__, 2, 3)));
+
+extern int blkid_probe_set_label(blkid_probe pr, unsigned char *label, size_t len);
+extern int blkid_probe_set_utf8label(blkid_probe pr, unsigned char *label,
+                size_t len, int enc);
+extern int blkid_probe_sprintf_uuid(blkid_probe pr, unsigned char *uuid,
+                size_t len, const char *fmt, ...)
+		__attribute__ ((__format__ (__printf__, 4, 5)));
+extern int blkid_probe_strncpy_uuid(blkid_probe pr, unsigned char *str, size_t len);
+
+extern int blkid_probe_set_uuid(blkid_probe pr, unsigned char *uuid);
+extern int blkid_probe_set_uuid_as(blkid_probe pr, unsigned char *uuid, const char *name);
+
+
+#endif /* _BLKID_SUPERBLOCKS_H */
diff --git a/src/superblocks/swap.c b/src/superblocks/swap.c
new file mode 100644
index 0000000..7ac119b
--- /dev/null
+++ b/src/superblocks/swap.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+/* linux-2.6/include/linux/swap.h */
+struct swap_header_v1_2 {
+     /*	char		bootbits[1024];	*/ /* Space for disklabel etc. */
+	uint32_t	version;
+	uint32_t	lastpage;
+	uint32_t	nr_badpages;
+	unsigned char	uuid[16];
+	unsigned char	volume[16];
+	uint32_t	padding[117];
+	uint32_t	badpages[1];
+} __attribute__((packed));
+
+#define PAGESIZE_MIN	0xff6	/* 4086 (arm, i386, ...) */
+#define PAGESIZE_MAX	0xfff6	/* 65526 (ia64) */
+
+#define TOI_MAGIC_STRING	"\xed\xc3\x02\xe9\x98\x56\xe5\x0c"
+#define TOI_MAGIC_STRLEN	(sizeof(TOI_MAGIC_STRING) - 1)
+
+static int swap_set_info(blkid_probe pr, const char *version)
+{
+	struct swap_header_v1_2 *hdr;
+
+	/* Swap header always located at offset of 1024 bytes */
+	hdr = (struct swap_header_v1_2 *) blkid_probe_get_buffer(pr, 1024,
+				sizeof(struct swap_header_v1_2));
+	if (!hdr)
+		return -1;
+
+	/* SWAPSPACE2 - check for wrong version or zeroed pagecount */
+	if (strcmp(version, "2") == 0 &&
+	    (hdr->version != 1 || hdr->lastpage == 0))
+		return -1;
+
+	/* arbitrary sanity check.. is there any garbage down there? */
+	if (hdr->padding[32] == 0 && hdr->padding[33] == 0) {
+		if (hdr->volume[0] && blkid_probe_set_label(pr, hdr->volume,
+				sizeof(hdr->volume)) < 0)
+			return -1;
+		if (blkid_probe_set_uuid(pr, hdr->uuid) < 0)
+			return -1;
+	}
+
+	blkid_probe_set_version(pr, version);
+	return 0;
+}
+
+static int probe_swap(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	unsigned char *buf;
+
+	if (!mag)
+		return -1;
+
+	/* TuxOnIce keeps valid swap header at the end of the 1st page */
+	buf = blkid_probe_get_buffer(pr, 0, TOI_MAGIC_STRLEN);
+	if (!buf)
+		return -1;
+
+	if (memcmp(buf, TOI_MAGIC_STRING, TOI_MAGIC_STRLEN) == 0)
+		return 1;	/* Ignore swap signature, it's TuxOnIce */
+
+	if (!memcmp(mag->magic, "SWAP-SPACE", mag->len)) {
+		/* swap v0 doesn't support LABEL or UUID */
+		blkid_probe_set_version(pr, "1");
+		return 0;
+
+	} else if (!memcmp(mag->magic, "SWAPSPACE2", mag->len))
+		return swap_set_info(pr, "2");
+
+	return -1;
+}
+
+static int probe_swsuspend(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	if (!mag)
+		return -1;
+	if (!memcmp(mag->magic, "S1SUSPEND", mag->len))
+		return swap_set_info(pr, "s1suspend");
+	if (!memcmp(mag->magic, "S2SUSPEND", mag->len))
+		return swap_set_info(pr, "s2suspend");
+	if (!memcmp(mag->magic, "ULSUSPEND", mag->len))
+		return swap_set_info(pr, "ulsuspend");
+	if (!memcmp(mag->magic, TOI_MAGIC_STRING, mag->len))
+		return swap_set_info(pr, "tuxonice");
+	if (!memcmp(mag->magic, "LINHIB0001", mag->len))
+		return swap_set_info(pr, "linhib0001");
+
+	return -1;	/* no signature detected */
+}
+
+const struct blkid_idinfo swap_idinfo =
+{
+	.name		= "swap",
+	.usage		= BLKID_USAGE_OTHER,
+	.probefunc	= probe_swap,
+	.minsz		= 10 * 4096,	/* 10 pages */
+	.magics		=
+	{
+		{ "SWAP-SPACE", 10, 0,  0xff6 },
+		{ "SWAPSPACE2", 10, 0,  0xff6 },
+		{ "SWAP-SPACE", 10, 0, 0x1ff6 },
+		{ "SWAPSPACE2", 10, 0, 0x1ff6 },
+		{ "SWAP-SPACE", 10, 0, 0x3ff6 },
+		{ "SWAPSPACE2", 10, 0, 0x3ff6 },
+		{ "SWAP-SPACE", 10, 0, 0x7ff6 },
+		{ "SWAPSPACE2", 10, 0, 0x7ff6 },
+		{ "SWAP-SPACE", 10, 0, 0xfff6 },
+		{ "SWAPSPACE2", 10, 0, 0xfff6 },
+		{ NULL }
+	}
+};
+
+
+const struct blkid_idinfo swsuspend_idinfo =
+{
+	.name		= "swsuspend",
+	.usage		= BLKID_USAGE_OTHER,
+	.probefunc	= probe_swsuspend,
+	.minsz		= 10 * 4096,	/* 10 pages */
+	.magics		=
+	{
+		{ TOI_MAGIC_STRING, TOI_MAGIC_STRLEN, 0, 0 },
+		{ "S1SUSPEND", 9, 0, 0xff6 },
+		{ "S2SUSPEND", 9, 0, 0xff6 },
+		{ "ULSUSPEND", 9, 0, 0xff6 },
+		{ "LINHIB0001",10,0, 0xff6 },
+
+		{ "S1SUSPEND", 9, 0, 0x1ff6 },
+		{ "S2SUSPEND", 9, 0, 0x1ff6 },
+		{ "ULSUSPEND", 9, 0, 0x1ff6 },
+		{ "LINHIB0001",10,0, 0x1ff6 },
+
+		{ "S1SUSPEND", 9, 0, 0x3ff6 },
+		{ "S2SUSPEND", 9, 0, 0x3ff6 },
+		{ "ULSUSPEND", 9, 0, 0x3ff6 },
+		{ "LINHIB0001",10,0, 0x3ff6 },
+
+		{ "S1SUSPEND", 9, 0, 0x7ff6 },
+		{ "S2SUSPEND", 9, 0, 0x7ff6 },
+		{ "ULSUSPEND", 9, 0, 0x7ff6 },
+		{ "LINHIB0001",10,0, 0x7ff6 },
+
+		{ "S1SUSPEND", 9, 0, 0xfff6 },
+		{ "S2SUSPEND", 9, 0, 0xfff6 },
+		{ "ULSUSPEND", 9, 0, 0xfff6 },
+		{ "LINHIB0001",10,0, 0xfff6 },
+
+		{ NULL }
+	}
+};
+
diff --git a/src/superblocks/sysv.c b/src/superblocks/sysv.c
new file mode 100644
index 0000000..80b0cc5
--- /dev/null
+++ b/src/superblocks/sysv.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This is written from sratch according to Linux kernel fs/sysv/super.c file.
+ * It seems that sysv probing code in libvolume_id and also in the original
+ * blkid is useless.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "superblocks.h"
+
+#define XENIX_NICINOD				100
+#define XENIX_NICFREE				100
+
+struct xenix_super_block {
+	uint16_t	s_isize;
+	uint32_t	s_fsize;
+	uint16_t	s_nfree;
+	uint32_t	s_free[XENIX_NICFREE];
+	uint16_t	s_ninode;
+	uint16_t	s_inode[XENIX_NICINOD];
+	uint8_t		s_flock;
+	uint8_t		s_ilock;
+	uint8_t		s_fmod;
+	uint8_t		s_ronly;
+	uint32_t	s_time;
+	uint32_t	s_tfree;
+	uint16_t	s_tinode;
+	uint16_t	s_dinfo[4];
+	uint8_t		s_fname[6];
+	uint8_t		s_fpack[6];
+	uint8_t		s_clean;
+	uint8_t		s_fill[371];
+	uint32_t	s_magic;
+	uint32_t	s_type;
+} __attribute__((packed));
+
+
+#define SYSV_NICINOD			100
+#define SYSV_NICFREE			50
+
+struct sysv_super_block
+{
+	uint16_t	s_isize;
+	uint16_t	s_pad0;
+	uint32_t	s_fsize;
+	uint16_t	s_nfree;
+	uint16_t	s_pad1;
+	uint32_t	s_free[SYSV_NICFREE];
+	uint16_t	s_ninode;
+	uint16_t	s_pad2;
+	uint16_t	s_inode[SYSV_NICINOD];
+	uint8_t		s_flock;
+	uint8_t		s_ilock;
+	uint8_t		s_fmod;
+	uint8_t		s_ronly;
+	uint32_t	s_time;
+	uint16_t	s_dinfo[4];
+	uint32_t	s_tfree;
+	uint16_t	s_tinode;
+	uint16_t	s_pad3;
+	uint8_t		s_fname[6];
+	uint8_t		s_fpack[6];
+	uint32_t	s_fill[12];
+	uint32_t	s_state;
+	uint32_t	s_magic;
+	uint32_t	s_type;
+};
+
+static int probe_xenix(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct xenix_super_block *sb;
+
+	sb = blkid_probe_get_sb(pr, mag, struct xenix_super_block);
+	if (!sb)
+		return -1;
+	blkid_probe_set_label(pr, sb->s_fname, sizeof(sb->s_fname));
+	return 0;
+}
+
+#define SYSV_BLOCK_SIZE	1024
+
+/* Note that we don't probe for Coherent FS, this FS does not have
+ * magic string. (It requires to probe fname/fpack field..)
+ */
+static int probe_sysv(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct sysv_super_block *sb;
+	int blocks[] = {0, 9, 15, 18};
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(blocks); i++) {
+		int off = blocks[i] * SYSV_BLOCK_SIZE + SYSV_BLOCK_SIZE/2;
+
+		sb = (struct sysv_super_block *)
+			blkid_probe_get_buffer(pr,
+					off,
+					sizeof(struct sysv_super_block));
+		if (!sb)
+			return -1;
+
+		if (sb->s_magic == cpu_to_le32(0xfd187e20) ||
+		    sb->s_magic == cpu_to_be32(0xfd187e20)) {
+
+			if (blkid_probe_set_label(pr, sb->s_fname,
+						sizeof(sb->s_fname)))
+				return -1;
+
+			if (blkid_probe_set_magic(pr,
+					off + offsetof(struct sysv_super_block,
+								s_magic),
+					sizeof(sb->s_magic),
+					(unsigned char *) &sb->s_magic))
+				return -1;
+
+			return 0;
+		}
+	}
+	return 1;
+}
+
+const struct blkid_idinfo xenix_idinfo =
+{
+	.name		= "xenix",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_xenix,
+	.magics		=
+	{
+		{ .magic = "\x2b\x55\x44", .len = 3, .kboff = 1, .sboff = 0x400 },
+		{ .magic = "\x44\x55\x2b", .len = 3, .kboff = 1, .sboff = 0x400 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo sysv_idinfo =
+{
+	.name		= "sysv",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_sysv,
+
+	/* SYSV is BE and LE and superblock could be on four positions. It's
+	 * simpler to probe for the magic string by .probefunc().
+	 */
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/src/superblocks/ubifs.c b/src/superblocks/ubifs.c
new file mode 100644
index 0000000..2d69c2b
--- /dev/null
+++ b/src/superblocks/ubifs.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2009 Corentin Chary <corentincj@iksaif.net>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+/*
+ * struct ubifs_ch - common header node.
+ * @magic: UBIFS node magic number (%UBIFS_NODE_MAGIC)
+ * @crc: CRC-32 checksum of the node header
+ * @sqnum: sequence number
+ * @len: full node length
+ * @node_type: node type
+ * @group_type: node group type
+ * @padding: reserved for future, zeroes
+ *
+ * Every UBIFS node starts with this common part. If the node has a key, the
+ * key always goes next.
+ */
+struct ubifs_ch {
+	uint32_t magic;
+	uint32_t crc;
+	uint64_t sqnum;
+	uint32_t len;
+	uint8_t node_type;
+	uint8_t group_type;
+	uint8_t padding[2];
+} __attribute__ ((packed));
+
+/*
+ * struct ubifs_sb_node - superblock node.
+ * @ch: common header
+ * @padding: reserved for future, zeroes
+ * @key_hash: type of hash function used in keys
+ * @key_fmt: format of the key
+ * @flags: file-system flags (%UBIFS_FLG_BIGLPT, etc)
+ * @min_io_size: minimal input/output unit size
+ * @leb_size: logical eraseblock size in bytes
+ * @leb_cnt: count of LEBs used by file-system
+ * @max_leb_cnt: maximum count of LEBs used by file-system
+ * @max_bud_bytes: maximum amount of data stored in buds
+ * @log_lebs: log size in logical eraseblocks
+ * @lpt_lebs: number of LEBs used for lprops table
+ * @orph_lebs: number of LEBs used for recording orphans
+ * @jhead_cnt: count of journal heads
+ * @fanout: tree fanout (max. number of links per indexing node)
+ * @lsave_cnt: number of LEB numbers in LPT's save table
+ * @fmt_version: UBIFS on-flash format version
+ * @default_compr: default compression algorithm (%UBIFS_COMPR_LZO, etc)
+ * @padding1: reserved for future, zeroes
+ * @rp_uid: reserve pool UID
+ * @rp_gid: reserve pool GID
+ * @rp_size: size of the reserved pool in bytes
+ * @padding2: reserved for future, zeroes
+ * @time_gran: time granularity in nanoseconds
+ * @uuid: UUID generated when the file system image was created
+ * @ro_compat_version: UBIFS R/O compatibility version
+ */
+struct ubifs_sb_node {
+	struct ubifs_ch ch;
+	uint8_t padding[2];
+	uint8_t key_hash;
+	uint8_t key_fmt;
+	uint32_t flags;
+	uint32_t min_io_size;
+	uint32_t leb_size;
+	uint32_t leb_cnt;
+	uint32_t max_leb_cnt;
+	uint64_t max_bud_bytes;
+	uint32_t log_lebs;
+	uint32_t lpt_lebs;
+	uint32_t orph_lebs;
+	uint32_t jhead_cnt;
+	uint32_t fanout;
+	uint32_t lsave_cnt;
+	uint32_t fmt_version;
+	uint16_t default_compr;
+	uint8_t padding1[2];
+	uint32_t rp_uid;
+	uint32_t rp_gid;
+	uint64_t rp_size;
+	uint32_t time_gran;
+	uint8_t uuid[16];
+	uint32_t ro_compat_version;
+	uint8_t padding2[3968];
+} __attribute__ ((packed));
+
+static int probe_ubifs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct ubifs_sb_node *sb;
+
+	sb = blkid_probe_get_sb(pr, mag, struct ubifs_sb_node);
+	if (!sb)
+		return -1;
+
+	blkid_probe_set_uuid(pr, sb->uuid);
+	blkid_probe_sprintf_version(pr, "w%dr%d",
+				    sb->fmt_version, sb->ro_compat_version);
+	return 0;
+}
+
+const struct blkid_idinfo ubifs_idinfo =
+{
+	.name		= "ubifs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ubifs,
+	.magics		=
+	{
+		{ .magic = "\x31\x18\x10\x06", .len = 4 },
+		{ NULL }
+	}
+};
diff --git a/src/superblocks/udf.c b/src/superblocks/udf.c
new file mode 100644
index 0000000..a5afc5c
--- /dev/null
+++ b/src/superblocks/udf.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct volume_descriptor {
+	struct descriptor_tag {
+		uint16_t	id;
+		uint16_t	version;
+		uint8_t		checksum;
+		uint8_t		reserved;
+		uint16_t	serial;
+		uint16_t	crc;
+		uint16_t	crc_len;
+		uint32_t	location;
+	} __attribute__((packed)) tag;
+
+	union {
+		struct anchor_descriptor {
+			uint32_t	length;
+			uint32_t	location;
+		} __attribute__((packed)) anchor;
+
+		struct primary_descriptor {
+			uint32_t	seq_num;
+			uint32_t	desc_num;
+			struct dstring {
+				uint8_t	clen;
+				uint8_t	c[31];
+			} __attribute__((packed)) ident;
+		} __attribute__((packed)) primary;
+
+	} __attribute__((packed)) type;
+
+} __attribute__((packed));
+
+struct volume_structure_descriptor {
+	uint8_t		type;
+	uint8_t		id[5];
+	uint8_t		version;
+} __attribute__((packed));
+
+#define UDF_VSD_OFFSET			0x8000LL
+
+static int probe_udf(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct volume_descriptor *vd;
+	struct volume_structure_descriptor *vsd;
+	unsigned int bs;
+	unsigned int b;
+	unsigned int type;
+	unsigned int count;
+	unsigned int loc;
+
+	/* search Volume Sequence Descriptor (VSD) to get the logical
+	 * block size of the volume */
+	for (bs = 0x800; bs < 0x8000; bs += 0x800) {
+		vsd = (struct volume_structure_descriptor *)
+			blkid_probe_get_buffer(pr,
+					UDF_VSD_OFFSET + bs,
+					sizeof(*vsd));
+		if (!vsd)
+			return 1;
+		if (vsd->id[0] != '\0')
+			goto nsr;
+	}
+	return -1;
+
+nsr:
+	/* search the list of VSDs for a NSR descriptor */
+	for (b = 0; b < 64; b++) {
+		vsd = (struct volume_structure_descriptor *)
+			blkid_probe_get_buffer(pr,
+					UDF_VSD_OFFSET + ((blkid_loff_t) b * bs),
+					sizeof(*vsd));
+		if (!vsd)
+			return -1;
+		if (vsd->id[0] == '\0')
+			return -1;
+		if (memcmp(vsd->id, "NSR02", 5) == 0)
+			goto anchor;
+		if (memcmp(vsd->id, "NSR03", 5) == 0)
+			goto anchor;
+	}
+	return -1;
+
+anchor:
+	/* read Anchor Volume Descriptor (AVDP) */
+	vd = (struct volume_descriptor *)
+		blkid_probe_get_buffer(pr, 256 * bs, sizeof(*vd));
+	if (!vd)
+		return -1;
+
+	type = le16_to_cpu(vd->tag.id);
+	if (type != 2) /* TAG_ID_AVDP */
+		return 0;
+
+	/* get desriptor list address and block count */
+	count = le32_to_cpu(vd->type.anchor.length) / bs;
+	loc = le32_to_cpu(vd->type.anchor.location);
+
+	/* pick the primary descriptor from the list */
+	for (b = 0; b < count; b++) {
+		vd = (struct volume_descriptor *)
+			blkid_probe_get_buffer(pr,
+					(blkid_loff_t) (loc + b) * bs,
+					sizeof(*vd));
+		if (!vd)
+			return -1;
+
+		type = le16_to_cpu(vd->tag.id);
+		if (type == 0)
+			break;
+		if (le32_to_cpu(vd->tag.location) != loc + b)
+			break;
+		if (type == 1) { /* TAG_ID_PVD */
+			uint8_t clen = vd->type.primary.ident.clen;
+
+			if (clen == 8)
+				blkid_probe_set_label(pr,
+						vd->type.primary.ident.c, 31);
+			else if (clen == 16)
+				blkid_probe_set_utf8label(pr,
+						vd->type.primary.ident.c,
+						31, BLKID_ENC_UTF16BE);
+
+			if (clen == 8 || clen == 16)
+				break;
+		}
+	}
+
+	return 0;
+}
+
+
+const struct blkid_idinfo udf_idinfo =
+{
+	.name		= "udf",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_udf,
+	.flags		= BLKID_IDINFO_TOLERANT,
+	.magics		=
+	{
+		{ .magic = "BEA01", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "BOOT2", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "CDW02", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "NSR02", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "NSR03", .len = 5, .kboff = 32, .sboff = 1 },
+		{ .magic = "TEA01", .len = 5, .kboff = 32, .sboff = 1 },
+		{ NULL }
+	}
+};
diff --git a/src/superblocks/ufs.c b/src/superblocks/ufs.c
new file mode 100644
index 0000000..673a528
--- /dev/null
+++ b/src/superblocks/ufs.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#include "superblocks.h"
+
+struct ufs_super_block {
+	uint32_t	fs_link;
+	uint32_t	fs_rlink;
+	uint32_t	fs_sblkno;
+	uint32_t	fs_cblkno;
+	uint32_t	fs_iblkno;
+	uint32_t	fs_dblkno;
+	uint32_t	fs_cgoffset;
+	uint32_t	fs_cgmask;
+	uint32_t	fs_time;
+	uint32_t	fs_size;
+	uint32_t	fs_dsize;
+	uint32_t	fs_ncg;
+	uint32_t	fs_bsize;
+	uint32_t	fs_fsize;
+	uint32_t	fs_frag;
+	uint32_t	fs_minfree;
+	uint32_t	fs_rotdelay;
+	uint32_t	fs_rps;
+	uint32_t	fs_bmask;
+	uint32_t	fs_fmask;
+	uint32_t	fs_bshift;
+	uint32_t	fs_fshift;
+	uint32_t	fs_maxcontig;
+	uint32_t	fs_maxbpg;
+	uint32_t	fs_fragshift;
+	uint32_t	fs_fsbtodb;
+	uint32_t	fs_sbsize;
+	uint32_t	fs_csmask;
+	uint32_t	fs_csshift;
+	uint32_t	fs_nindir;
+	uint32_t	fs_inopb;
+	uint32_t	fs_nspf;
+	uint32_t	fs_optim;
+	uint32_t	fs_npsect_state;
+	uint32_t	fs_interleave;
+	uint32_t	fs_trackskew;
+	uint32_t	fs_id[2];
+	uint32_t	fs_csaddr;
+	uint32_t	fs_cssize;
+	uint32_t	fs_cgsize;
+	uint32_t	fs_ntrak;
+	uint32_t	fs_nsect;
+	uint32_t	fs_spc;
+	uint32_t	fs_ncyl;
+	uint32_t	fs_cpg;
+	uint32_t	fs_ipg;
+	uint32_t	fs_fpg;
+	struct ufs_csum {
+		uint32_t	cs_ndir;
+		uint32_t	cs_nbfree;
+		uint32_t	cs_nifree;
+		uint32_t	cs_nffree;
+	} fs_cstotal;
+	int8_t		fs_fmod;
+	int8_t		fs_clean;
+	int8_t		fs_ronly;
+	int8_t		fs_flags;
+	union {
+		struct {
+			int8_t	fs_fsmnt[512];
+			uint32_t	fs_cgrotor;
+			uint32_t	fs_csp[31];
+			uint32_t	fs_maxcluster;
+			uint32_t	fs_cpc;
+			uint16_t	fs_opostbl[16][8];
+		} fs_u1;
+		struct {
+			int8_t		fs_fsmnt[468];
+			uint8_t		fs_volname[32];
+			uint64_t	fs_swuid;
+			int32_t		fs_pad;
+			uint32_t	fs_cgrotor;
+			uint32_t	fs_ocsp[28];
+			uint32_t	fs_contigdirs;
+			uint32_t	fs_csp;
+			uint32_t	fs_maxcluster;
+			uint32_t	fs_active;
+			int32_t		fs_old_cpc;
+			int32_t		fs_maxbsize;
+			int64_t		fs_sparecon64[17];
+			int64_t		fs_sblockloc;
+			struct ufs2_csum_total {
+				uint64_t	cs_ndir;
+				uint64_t	cs_nbfree;
+				uint64_t	cs_nifree;
+				uint64_t	cs_nffree;
+				uint64_t	cs_numclusters;
+				uint64_t	cs_spare[3];
+			} fs_cstotal;
+			struct ufs_timeval {
+				int32_t		tv_sec;
+				int32_t		tv_usec;
+			} fs_time;
+			int64_t		fs_size;
+			int64_t		fs_dsize;
+			uint64_t	fs_csaddr;
+			int64_t		fs_pendingblocks;
+			int32_t		fs_pendinginodes;
+		} __attribute__((packed)) fs_u2;
+	}  fs_u11;
+	union {
+		struct {
+			int32_t		fs_sparecon[53];
+			int32_t		fs_reclaim;
+			int32_t		fs_sparecon2[1];
+			int32_t		fs_state;
+			uint32_t	fs_qbmask[2];
+			uint32_t	fs_qfmask[2];
+		} fs_sun;
+		struct {
+			int32_t		fs_sparecon[53];
+			int32_t		fs_reclaim;
+			int32_t		fs_sparecon2[1];
+			uint32_t	fs_npsect;
+			uint32_t	fs_qbmask[2];
+			uint32_t	fs_qfmask[2];
+		} fs_sunx86;
+		struct {
+			int32_t		fs_sparecon[50];
+			int32_t		fs_contigsumsize;
+			int32_t		fs_maxsymlinklen;
+			int32_t		fs_inodefmt;
+			uint32_t	fs_maxfilesize[2];
+			uint32_t	fs_qbmask[2];
+			uint32_t	fs_qfmask[2];
+			int32_t		fs_state;
+		} fs_44;
+	} fs_u2;
+	int32_t		fs_postblformat;
+	int32_t		fs_nrpos;
+	int32_t		fs_postbloff;
+	int32_t		fs_rotbloff;
+	uint32_t	fs_magic;
+	uint8_t		fs_space[1];
+} __attribute__((packed));
+
+#define UFS_MAGIC			0x00011954
+#define UFS2_MAGIC			0x19540119
+#define UFS_MAGIC_FEA			0x00195612
+#define UFS_MAGIC_LFN			0x00095014
+#define UFS_MAGIC_SEC			0x00612195
+#define UFS_MAGIC_4GB			0x05231994
+
+static int probe_ufs(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	int offsets[] = { 0, 8, 64, 256 };
+	uint32_t mags[] = {
+		UFS2_MAGIC, UFS_MAGIC, UFS_MAGIC_FEA, UFS_MAGIC_LFN,
+		UFS_MAGIC_SEC, UFS_MAGIC_4GB
+	};
+	size_t i;
+	uint32_t magic;
+	struct ufs_super_block *ufs;
+	int is_be;
+
+	for (i = 0; i < ARRAY_SIZE(offsets); i++) {
+		uint32_t magLE, magBE;
+		size_t y;
+
+		ufs = (struct ufs_super_block *)
+				blkid_probe_get_buffer(pr,
+					offsets[i] * 1024,
+					sizeof(struct ufs_super_block));
+		if (!ufs)
+			return -1;
+
+		magBE = be32_to_cpu(ufs->fs_magic);
+		magLE = le32_to_cpu(ufs->fs_magic);
+
+		for (y = 0; y < ARRAY_SIZE(mags); y++) {
+			if (magLE == mags[y] || magBE == mags[y]) {
+				magic = mags[y];
+				is_be = (magBE == mags[y]);
+				goto found;
+			}
+		}
+	}
+
+	return 1;
+
+found:
+	if (magic == UFS2_MAGIC) {
+		blkid_probe_set_version(pr, "2");
+		blkid_probe_set_label(pr, ufs->fs_u11.fs_u2.fs_volname,
+				sizeof(ufs->fs_u11.fs_u2.fs_volname));
+	} else
+		blkid_probe_set_version(pr, "1");
+	if (ufs->fs_id[0] || ufs->fs_id[1])
+	{
+		if (is_be)
+			blkid_probe_sprintf_uuid(pr,
+					 (unsigned char *) &ufs->fs_id,
+						 sizeof(ufs->fs_id),
+						 "%08x%08x",
+						 be32_to_cpu(ufs->fs_id[0]),
+						 be32_to_cpu(ufs->fs_id[1]));
+		else
+			blkid_probe_sprintf_uuid(pr,
+					 (unsigned char *) &ufs->fs_id,
+						 sizeof(ufs->fs_id),
+						 "%08x%08x",
+						 le32_to_cpu(ufs->fs_id[0]),
+						 le32_to_cpu(ufs->fs_id[1]));
+	}
+
+	if (blkid_probe_set_magic(pr,
+			(offsets[i] * 1024) +
+				offsetof(struct ufs_super_block, fs_magic),
+			sizeof(ufs->fs_magic),
+			(unsigned char *) &ufs->fs_magic))
+		return -1;
+
+	return 0;
+}
+
+/*
+ * According to libvolume_id the UFS superblock could be on four positions.
+ * The original libblkid has checked one position (.kboff=8) only.
+ *
+ * We know four UFS magic strings and UFS could be both little-endian and
+ * big-endian. ... so we have:
+ *
+ *	4 position * 4 string * 2 version = 32 magic strings
+ *
+ * It seems simpler to check for these string in probing function that hardcode
+ * all in the .magic array.
+ */
+const struct blkid_idinfo ufs_idinfo =
+{
+	.name		= "ufs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_ufs,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/src/superblocks/vfat.c b/src/superblocks/vfat.c
new file mode 100644
index 0000000..2feb818
--- /dev/null
+++ b/src/superblocks/vfat.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+/* Yucky misaligned values */
+struct vfat_super_block {
+/* 00*/	unsigned char	vs_ignored[3];
+/* 03*/	unsigned char	vs_sysid[8];
+/* 0b*/	unsigned char	vs_sector_size[2];
+/* 0d*/	uint8_t		vs_cluster_size;
+/* 0e*/	uint16_t	vs_reserved;
+/* 10*/	uint8_t		vs_fats;
+/* 11*/	unsigned char	vs_dir_entries[2];
+/* 13*/	unsigned char	vs_sectors[2];
+/* 15*/	unsigned char	vs_media;
+/* 16*/	uint16_t	vs_fat_length;
+/* 18*/	uint16_t	vs_secs_track;
+/* 1a*/	uint16_t	vs_heads;
+/* 1c*/	uint32_t	vs_hidden;
+/* 20*/	uint32_t	vs_total_sect;
+/* 24*/	uint32_t	vs_fat32_length;
+/* 28*/	uint16_t	vs_flags;
+/* 2a*/	uint8_t		vs_version[2];
+/* 2c*/	uint32_t	vs_root_cluster;
+/* 30*/	uint16_t	vs_fsinfo_sector;
+/* 32*/	uint16_t	vs_backup_boot;
+/* 34*/	uint16_t	vs_reserved2[6];
+/* 40*/	unsigned char	vs_unknown[3];
+/* 43*/	unsigned char	vs_serno[4];
+/* 47*/	unsigned char	vs_label[11];
+/* 52*/	unsigned char   vs_magic[8];
+/* 5a*/	unsigned char	vs_dummy2[0x1fe - 0x5a];
+/*1fe*/	unsigned char	vs_pmagic[2];
+} __attribute__((packed));
+
+/* Yucky misaligned values */
+struct msdos_super_block {
+/* 00*/	unsigned char	ms_ignored[3];
+/* 03*/	unsigned char	ms_sysid[8];
+/* 0b*/	unsigned char	ms_sector_size[2];
+/* 0d*/	uint8_t		ms_cluster_size;
+/* 0e*/	uint16_t	ms_reserved;
+/* 10*/	uint8_t		ms_fats;
+/* 11*/	unsigned char	ms_dir_entries[2];
+/* 13*/	unsigned char	ms_sectors[2]; /* =0 iff V3 or later */
+/* 15*/	unsigned char	ms_media;
+/* 16*/	uint16_t	ms_fat_length; /* Sectors per FAT */
+/* 18*/	uint16_t	ms_secs_track;
+/* 1a*/	uint16_t	ms_heads;
+/* 1c*/	uint32_t	ms_hidden;
+/* V3 BPB */
+/* 20*/	uint32_t	ms_total_sect; /* iff ms_sectors == 0 */
+/* V4 BPB */
+/* 24*/	unsigned char	ms_unknown[3]; /* Phys drive no., resvd, V4 sig (0x29) */
+/* 27*/	unsigned char	ms_serno[4];
+/* 2b*/	unsigned char	ms_label[11];
+/* 36*/	unsigned char   ms_magic[8];
+/* 3e*/	unsigned char	ms_dummy2[0x1fe - 0x3e];
+/*1fe*/	unsigned char	ms_pmagic[2];
+} __attribute__((packed));
+
+struct vfat_dir_entry {
+	uint8_t		name[11];
+	uint8_t		attr;
+	uint16_t	time_creat;
+	uint16_t	date_creat;
+	uint16_t	time_acc;
+	uint16_t	date_acc;
+	uint16_t	cluster_high;
+	uint16_t	time_write;
+	uint16_t	date_write;
+	uint16_t	cluster_low;
+	uint32_t	size;
+} __attribute__((packed));
+
+struct fat32_fsinfo {
+	uint8_t signature1[4];
+	uint32_t reserved1[120];
+	uint8_t signature2[4];
+	uint32_t free_clusters;
+	uint32_t next_cluster;
+	uint32_t reserved2[4];
+} __attribute__((packed));
+
+/* maximum number of clusters */
+#define FAT12_MAX 0xFF4
+#define FAT16_MAX 0xFFF4
+#define FAT32_MAX 0x0FFFFFF6
+
+#define FAT_ATTR_VOLUME_ID		0x08
+#define FAT_ATTR_DIR			0x10
+#define FAT_ATTR_LONG_NAME		0x0f
+#define FAT_ATTR_MASK			0x3f
+#define FAT_ENTRY_FREE			0xe5
+
+static const char *no_name = "NO NAME    ";
+
+#define unaligned_le16(x) \
+		(((unsigned char *) x)[0] + (((unsigned char *) x)[1] << 8))
+
+/*
+ * Look for LABEL (name) in the FAT root directory.
+ */
+static unsigned char *search_fat_label(blkid_probe pr,
+				uint64_t offset, uint32_t entries)
+{
+	struct vfat_dir_entry *ent, *dir = NULL;
+	uint32_t i;
+
+	DBG(DEBUG_LOWPROBE,
+		printf("\tlook for label in root-dir "
+			"(entries: %d, offset: %jd)\n", entries, offset));
+
+	if (!blkid_probe_is_tiny(pr)) {
+		/* large disk, read whole root directory */
+		dir = (struct vfat_dir_entry *)
+			blkid_probe_get_buffer(pr,
+					offset,
+					(blkid_loff_t) entries *
+						sizeof(struct vfat_dir_entry));
+		if (!dir)
+			return NULL;
+	}
+
+	for (i = 0; i < entries; i++) {
+		/*
+		 * The root directory could be relatively large (4-16kB).
+		 * Fortunately, the LABEL is usually the first entry in the
+		 * directory. On tiny disks we call read() per entry.
+		 */
+		if (!dir)
+			ent = (struct vfat_dir_entry *)
+				blkid_probe_get_buffer(pr,
+					(blkid_loff_t) offset + (i *
+						sizeof(struct vfat_dir_entry)),
+					sizeof(struct vfat_dir_entry));
+		else
+			ent = &dir[i];
+
+		if (!ent || ent->name[0] == 0x00)
+			break;
+
+		if ((ent->name[0] == FAT_ENTRY_FREE) ||
+		    (ent->cluster_high != 0 || ent->cluster_low != 0) ||
+		    ((ent->attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME))
+			continue;
+
+		if ((ent->attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) ==
+		    FAT_ATTR_VOLUME_ID) {
+			DBG(DEBUG_LOWPROBE,
+				printf("\tfound fs LABEL at entry %d\n", i));
+			return ent->name;
+		}
+	}
+	return NULL;
+}
+
+static int fat_valid_superblock(const struct blkid_idmag *mag,
+			struct msdos_super_block *ms,
+			struct vfat_super_block *vs,
+			uint32_t *cluster_count, uint32_t *fat_size)
+{
+	uint16_t sector_size, dir_entries, reserved;
+	uint32_t sect_count, __fat_size, dir_size, __cluster_count, fat_length;
+	uint32_t max_count;
+
+	/* extra check for FATs without magic strings */
+	if (mag->len <= 2) {
+		/* Old floppies have a valid MBR signature */
+		if (ms->ms_pmagic[0] != 0x55 || ms->ms_pmagic[1] != 0xAA)
+			return 0;
+
+		/*
+		 * OS/2 and apparently DFSee will place a FAT12/16-like
+		 * pseudo-superblock in the first 512 bytes of non-FAT
+		 * filesystems --- at least JFS and HPFS, and possibly others.
+		 * So we explicitly check for those filesystems at the
+		 * FAT12/16 filesystem magic field identifier, and if they are
+		 * present, we rule this out as a FAT filesystem, despite the
+		 * FAT-like pseudo-header.
+		 */
+		if ((memcmp(ms->ms_magic, "JFS     ", 8) == 0) ||
+		    (memcmp(ms->ms_magic, "HPFS    ", 8) == 0))
+			return 0;
+	}
+
+	/* fat counts(Linux kernel expects at least 1 FAT table) */
+	if (!ms->ms_fats)
+		return 0;
+	if (!ms->ms_reserved)
+		return 0;
+	if (!(0xf8 <= ms->ms_media || ms->ms_media == 0xf0))
+		return 0;
+	if (!is_power_of_2(ms->ms_cluster_size))
+		return 0;
+
+	sector_size = unaligned_le16(&ms->ms_sector_size);
+	if (!is_power_of_2(sector_size) ||
+	    sector_size < 512 || sector_size > 4096)
+		return 0;
+
+	dir_entries = unaligned_le16(&ms->ms_dir_entries);
+	reserved =  le16_to_cpu(ms->ms_reserved);
+	sect_count = unaligned_le16(&ms->ms_sectors);
+
+	if (sect_count == 0)
+		sect_count = le32_to_cpu(ms->ms_total_sect);
+
+	fat_length = le16_to_cpu(ms->ms_fat_length);
+	if (fat_length == 0)
+		fat_length = le32_to_cpu(vs->vs_fat32_length);
+
+	__fat_size = fat_length * ms->ms_fats;
+	dir_size = ((dir_entries * sizeof(struct vfat_dir_entry)) +
+					(sector_size-1)) / sector_size;
+
+	__cluster_count = (sect_count - (reserved + __fat_size + dir_size)) /
+							ms->ms_cluster_size;
+	if (!ms->ms_fat_length && vs->vs_fat32_length)
+		max_count = FAT32_MAX;
+	else
+		max_count = __cluster_count > FAT12_MAX ? FAT16_MAX : FAT12_MAX;
+
+	if (__cluster_count > max_count)
+		return 0;
+
+	if (fat_size)
+		*fat_size = __fat_size;
+	if (cluster_count)
+		*cluster_count = __cluster_count;
+
+	return 1;	/* valid */
+}
+
+/*
+ * This function is used by MBR partition table parser to avoid
+ * misinterpretation of FAT filesystem.
+ */
+int blkid_probe_is_vfat(blkid_probe pr)
+{
+	struct vfat_super_block *vs;
+	struct msdos_super_block *ms;
+	const struct blkid_idmag *mag = NULL;
+
+	if (blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag) || !mag)
+		return 0;
+
+	ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
+	if (!ms)
+		return 0;
+	vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
+	if (!vs)
+		return 0;
+
+	return fat_valid_superblock(mag, ms, vs, NULL, NULL);
+}
+
+/* FAT label extraction from the root directory taken from Kay
+ * Sievers's volume_id library */
+static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct vfat_super_block *vs;
+	struct msdos_super_block *ms;
+	const unsigned char *vol_label = 0;
+	unsigned char *vol_serno = NULL, vol_label_buf[11];
+	uint16_t sector_size = 0, reserved;
+	uint32_t cluster_count, fat_size;
+	const char *version = NULL;
+
+	ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
+	if (!ms)
+		return 0;
+	vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
+	if (!vs)
+		return 0;
+	if (!fat_valid_superblock(mag, ms, vs, &cluster_count, &fat_size))
+		return 1;
+
+	sector_size = unaligned_le16(&ms->ms_sector_size);
+	reserved =  le16_to_cpu(ms->ms_reserved);
+
+	if (ms->ms_fat_length) {
+		/* the label may be an attribute in the root directory */
+		uint32_t root_start = (reserved + fat_size) * sector_size;
+		uint32_t root_dir_entries = unaligned_le16(&vs->vs_dir_entries);
+
+		vol_label = search_fat_label(pr, root_start, root_dir_entries);
+		if (vol_label) {
+			memcpy(vol_label_buf, vol_label, 11);
+			vol_label = vol_label_buf;
+		}
+
+		if (!vol_label || !memcmp(vol_label, no_name, 11))
+			vol_label = ms->ms_label;
+		vol_serno = ms->ms_serno;
+
+		blkid_probe_set_value(pr, "SEC_TYPE", (unsigned char *) "msdos",
+                              sizeof("msdos"));
+
+		if (cluster_count < FAT12_MAX)
+			version = "FAT12";
+		else if (cluster_count < FAT16_MAX)
+			version = "FAT16";
+
+	} else if (vs->vs_fat32_length) {
+		unsigned char *buf;
+		uint16_t fsinfo_sect;
+		int maxloop = 100;
+
+		/* Search the FAT32 root dir for the label attribute */
+		uint32_t buf_size = vs->vs_cluster_size * sector_size;
+		uint32_t start_data_sect = reserved + fat_size;
+		uint32_t entries = le32_to_cpu(vs->vs_fat32_length) *
+					sector_size / sizeof(uint32_t);
+		uint32_t next = le32_to_cpu(vs->vs_root_cluster);
+
+		while (next && next < entries && --maxloop) {
+			uint32_t next_sect_off;
+			uint64_t next_off, fat_entry_off;
+			int count;
+
+			next_sect_off = (next - 2) * vs->vs_cluster_size;
+			next_off = (uint64_t)(start_data_sect + next_sect_off) *
+				sector_size;
+
+			count = buf_size / sizeof(struct vfat_dir_entry);
+
+			vol_label = search_fat_label(pr, next_off, count);
+			if (vol_label) {
+				memcpy(vol_label_buf, vol_label, 11);
+				vol_label = vol_label_buf;
+				break;
+			}
+
+			/* get FAT entry */
+			fat_entry_off = ((uint64_t) reserved * sector_size) +
+				(next * sizeof(uint32_t));
+			buf = blkid_probe_get_buffer(pr, fat_entry_off, buf_size);
+			if (buf == NULL)
+				break;
+
+			/* set next cluster */
+			next = le32_to_cpu(*((uint32_t *) buf)) & 0x0fffffff;
+		}
+
+		version = "FAT32";
+
+		if (!vol_label || !memcmp(vol_label, no_name, 11))
+			vol_label = vs->vs_label;
+		vol_serno = vs->vs_serno;
+
+		/*
+		 * FAT32 should have a valid signature in the fsinfo block,
+		 * but also allow all bytes set to '\0', because some volumes
+		 * do not set the signature at all.
+		 */
+		fsinfo_sect = le16_to_cpu(vs->vs_fsinfo_sector);
+		if (fsinfo_sect) {
+			struct fat32_fsinfo *fsinfo;
+
+			buf = blkid_probe_get_buffer(pr,
+					(blkid_loff_t) fsinfo_sect * sector_size,
+					sizeof(struct fat32_fsinfo));
+			if (buf == NULL)
+				return -1;
+
+			fsinfo = (struct fat32_fsinfo *) buf;
+			if (memcmp(fsinfo->signature1, "\x52\x52\x61\x41", 4) != 0 &&
+			    memcmp(fsinfo->signature1, "\x52\x52\x64\x41", 4) != 0 &&
+			    memcmp(fsinfo->signature1, "\x00\x00\x00\x00", 4) != 0)
+				return -1;
+			if (memcmp(fsinfo->signature2, "\x72\x72\x41\x61", 4) != 0 &&
+			    memcmp(fsinfo->signature2, "\x00\x00\x00\x00", 4) != 0)
+				return -1;
+		}
+	}
+
+	if (vol_label && memcmp(vol_label, no_name, 11))
+		blkid_probe_set_label(pr, (unsigned char *) vol_label, 11);
+
+	/* We can't just print them as %04X, because they are unaligned */
+	if (vol_serno)
+		blkid_probe_sprintf_uuid(pr, vol_serno, 4, "%02X%02X-%02X%02X",
+			vol_serno[3], vol_serno[2], vol_serno[1], vol_serno[0]);
+	if (version)
+		blkid_probe_set_version(pr, version);
+
+	return 0;
+}
+
+
+const struct blkid_idinfo vfat_idinfo =
+{
+	.name		= "vfat",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_vfat,
+	.magics		=
+	{
+		{ .magic = "MSWIN",    .len = 5, .sboff = 0x52 },
+		{ .magic = "FAT32   ", .len = 8, .sboff = 0x52 },
+		{ .magic = "MSDOS",    .len = 5, .sboff = 0x36 },
+		{ .magic = "FAT16   ", .len = 8, .sboff = 0x36 },
+		{ .magic = "FAT12   ", .len = 8, .sboff = 0x36 },
+		{ .magic = "FAT     ", .len = 8, .sboff = 0x36 },
+		{ .magic = "\353",     .len = 1, },
+		{ .magic = "\351",     .len = 1, },
+		{ .magic = "\125\252", .len = 2, .sboff = 0x1fe },
+		{ NULL }
+	}
+};
+
diff --git a/src/superblocks/via_raid.c b/src/superblocks/via_raid.c
new file mode 100644
index 0000000..eba7e4b
--- /dev/null
+++ b/src/superblocks/via_raid.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * Inspired by libvolume_id by
+ *     Kay Sievers <kay.sievers@vrfy.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct via_metadata {
+	uint16_t	signature;
+	uint8_t		version_number;
+	struct via_array {
+		uint16_t	disk_bit_mask;
+		uint8_t		disk_array_ex;
+		uint32_t	capacity_low;
+		uint32_t	capacity_high;
+		uint32_t	serial_checksum;
+	} __attribute__((packed)) array;
+	uint32_t	serial_checksum[8];
+	uint8_t		checksum;
+} __attribute__((packed));
+
+#define VIA_SIGNATURE		0xAA55
+
+/* 8 bit checksum on first 50 bytes of metadata. */
+static uint8_t via_checksum(struct via_metadata *v)
+{
+	uint8_t i = 50, cs = 0;
+
+	while (i--)
+		cs += ((uint8_t*) v)[i];
+
+	return cs == v->checksum;
+}
+
+static int probe_viaraid(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t off;
+	struct via_metadata *v;
+
+	if (pr->size < 0x10000)
+		return -1;
+	if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+		return -1;
+
+	off = ((pr->size / 0x200)-1) * 0x200;
+
+	v = (struct via_metadata *)
+			blkid_probe_get_buffer(pr,
+				off,
+				sizeof(struct via_metadata));
+	if (!v)
+		return -1;
+	if (le16_to_cpu(v->signature) != VIA_SIGNATURE)
+		return -1;
+	if (v->version_number > 2)
+		return -1;
+	if (!via_checksum(v))
+		return -1;
+	if (blkid_probe_sprintf_version(pr, "%u", v->version_number) != 0)
+		return -1;
+	if (blkid_probe_set_magic(pr, off,
+				sizeof(v->signature),
+				(unsigned char *) &v->signature))
+		return -1;
+	return 0;
+}
+
+const struct blkid_idinfo viaraid_idinfo = {
+	.name		= "via_raid_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_viaraid,
+	.magics		= BLKID_NONE_MAGIC
+};
+
+
diff --git a/src/superblocks/vmfs.c b/src/superblocks/vmfs.c
new file mode 100644
index 0000000..ead09a8
--- /dev/null
+++ b/src/superblocks/vmfs.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2009 Mike Hommey <mh@glandium.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include "superblocks.h"
+
+struct vmfs_fs_info {
+	uint32_t magic;
+	uint32_t volume_version;
+	uint8_t version;
+	uint8_t uuid[16];
+	uint32_t mode;
+	char label[128];
+} __attribute__ ((__packed__));
+
+struct vmfs_volume_info {
+	uint32_t magic;
+	uint32_t ver;
+	uint8_t irrelevant[122];
+	uint8_t uuid[16];
+} __attribute__ ((__packed__));
+
+static int probe_vmfs_fs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct vmfs_fs_info *header;
+
+	header = blkid_probe_get_sb(pr, mag, struct vmfs_fs_info);
+	if (header == NULL)
+		return -1;
+
+	blkid_probe_sprintf_uuid(pr, (unsigned char *) header->uuid, 16,
+		"%02x%02x%02x%02x-%02x%02x%02x%02x-"
+		"%02x%02x-%02x%02x%02x%02x%02x%02x",
+		header->uuid[3], header->uuid[2], header->uuid[1],
+		header->uuid[0], header->uuid[7], header->uuid[6],
+		header->uuid[5], header->uuid[4], header->uuid[9],
+		header->uuid[8], header->uuid[10], header->uuid[11],
+		header->uuid[12], header->uuid[13], header->uuid[14],
+		header->uuid[15]);
+
+	blkid_probe_set_label(pr, (unsigned char *) header->label,
+					sizeof(header->label));
+	blkid_probe_sprintf_version(pr, "%u", header->version);
+	return 0;
+}
+
+static int probe_vmfs_volume(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct vmfs_volume_info *header;
+	unsigned char *lvm_uuid;
+
+	header = blkid_probe_get_sb(pr, mag, struct vmfs_volume_info);
+	if (header == NULL)
+		return -1;
+
+	blkid_probe_sprintf_value(pr, "UUID_SUB",
+		"%02x%02x%02x%02x-%02x%02x%02x%02x-"
+		"%02x%02x-%02x%02x%02x%02x%02x%02x",
+		header->uuid[3], header->uuid[2], header->uuid[1],
+		header->uuid[0], header->uuid[7], header->uuid[6],
+		header->uuid[5], header->uuid[4], header->uuid[9],
+		header->uuid[8], header->uuid[10], header->uuid[11],
+		header->uuid[12], header->uuid[13], header->uuid[14],
+		header->uuid[15]);
+	blkid_probe_sprintf_version(pr, "%u", le32_to_cpu(header->ver));
+
+	lvm_uuid = blkid_probe_get_buffer(pr,
+				1024 * 1024 /* Start of the volume info */
+				+ 512 /* Offset to lvm info */
+				+ 20 /* Offset in lvm info */, 35);
+	if (lvm_uuid)
+		blkid_probe_strncpy_uuid(pr, lvm_uuid, 35);
+
+	return 0;
+}
+
+const struct blkid_idinfo vmfs_fs_idinfo =
+{
+	.name		= "VMFS",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_vmfs_fs,
+	.magics		=
+	{
+		{ .magic = "\x5e\xf1\xab\x2f", .len = 4, .kboff = 2048 },
+		{ NULL }
+	}
+};
+
+const struct blkid_idinfo vmfs_volume_idinfo =
+{
+	.name		= "VMFS_volume_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_vmfs_volume,
+	.magics		=
+	{
+		{ .magic = "\x0d\xd0\x01\xc0", .len = 4, .kboff = 1024 },
+		{ NULL }
+	}
+};
diff --git a/src/superblocks/vxfs.c b/src/superblocks/vxfs.c
new file mode 100644
index 0000000..fdab85a
--- /dev/null
+++ b/src/superblocks/vxfs.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+
+#include "superblocks.h"
+
+struct vxfs_super_block {
+	uint32_t		vs_magic;
+	int32_t			vs_version;
+};
+
+static int probe_vxfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct vxfs_super_block *vxs;
+
+	vxs = blkid_probe_get_sb(pr, mag, struct vxfs_super_block);
+	if (!vxs)
+		return -1;
+
+	blkid_probe_sprintf_version(pr, "%u", (unsigned int) vxs->vs_version);
+	return 0;
+}
+
+
+const struct blkid_idinfo vxfs_idinfo =
+{
+	.name		= "vxfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_vxfs,
+	.magics		=
+	{
+		{ .magic = "\365\374\001\245", .len = 4, .kboff = 1 },
+		{ NULL }
+	}
+};
+
diff --git a/src/superblocks/xfs.c b/src/superblocks/xfs.c
new file mode 100644
index 0000000..1399fe1
--- /dev/null
+++ b/src/superblocks/xfs.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdint.h>
+
+#include "superblocks.h"
+
+struct xfs_super_block {
+	unsigned char	xs_magic[4];
+	uint32_t	xs_blocksize;
+	uint64_t	xs_dblocks;
+	uint64_t	xs_rblocks;
+	uint32_t	xs_dummy1[2];
+	unsigned char	xs_uuid[16];
+	uint32_t	xs_dummy2[15];
+	char		xs_fname[12];
+	uint32_t	xs_dummy3[2];
+	uint64_t	xs_icount;
+	uint64_t	xs_ifree;
+	uint64_t	xs_fdblocks;
+} __attribute__((packed));
+
+static int probe_xfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+	struct xfs_super_block *xs;
+
+	xs = blkid_probe_get_sb(pr, mag, struct xfs_super_block);
+	if (!xs)
+		return -1;
+
+	if (strlen(xs->xs_fname))
+		blkid_probe_set_label(pr, (unsigned char *) xs->xs_fname,
+				sizeof(xs->xs_fname));
+	blkid_probe_set_uuid(pr, xs->xs_uuid);
+	return 0;
+}
+
+const struct blkid_idinfo xfs_idinfo =
+{
+	.name		= "xfs",
+	.usage		= BLKID_USAGE_FILESYSTEM,
+	.probefunc	= probe_xfs,
+	.magics		=
+	{
+		{ .magic = "XFSB", .len = 4 },
+		{ NULL }
+	}
+};
+
diff --git a/src/superblocks/zfs.c b/src/superblocks/zfs.c
new file mode 100644
index 0000000..b96c5df
--- /dev/null
+++ b/src/superblocks/zfs.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2009-2010 by Andreas Dilger <adilger@sun.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <inttypes.h>
+
+#include "superblocks.h"
+
+#define VDEV_LABEL_UBERBLOCK	(128 * 1024ULL)
+#define VDEV_LABEL_NVPAIR	( 16 * 1024ULL)
+#define VDEV_LABEL_SIZE		(256 * 1024ULL)
+
+/* #include <sys/uberblock_impl.h> */
+#define UBERBLOCK_MAGIC         0x00bab10c              /* oo-ba-bloc!  */
+struct zfs_uberblock {
+	uint64_t	ub_magic;	/* UBERBLOCK_MAGIC		*/
+	uint64_t	ub_version;	/* SPA_VERSION			*/
+	uint64_t	ub_txg;		/* txg of last sync		*/
+	uint64_t	ub_guid_sum;	/* sum of all vdev guids	*/
+	uint64_t	ub_timestamp;	/* UTC time of last sync	*/
+	char		ub_rootbp;	/* MOS objset_phys_t		*/
+} __attribute__((packed));
+
+#define ZFS_TRIES	64
+#define ZFS_WANT	 4
+
+#define DATA_TYPE_UINT64 8
+#define DATA_TYPE_STRING 9
+
+struct nvpair {
+	uint32_t	nvp_size;
+	uint32_t	nvp_unkown;
+	uint32_t	nvp_namelen;
+	char		nvp_name[0]; /* aligned to 4 bytes */
+	/* aligned ptr array for string arrays */
+	/* aligned array of data for value */
+};
+
+struct nvstring {
+	uint32_t	nvs_type;
+	uint32_t	nvs_elem;
+	uint32_t	nvs_strlen;
+	unsigned char	nvs_string[0];
+};
+
+struct nvuint64 {
+	uint32_t	nvu_type;
+	uint32_t	nvu_elem;
+	uint64_t	nvu_value;
+};
+
+struct nvlist {
+	uint32_t	nvl_unknown[3];
+	struct nvpair	nvl_nvpair;
+};
+
+#define nvdebug(fmt, ...)	do { } while(0)
+/*#define nvdebug(fmt, a...)	printf(fmt, ##a)*/
+
+static void zfs_extract_guid_name(blkid_probe pr, loff_t offset)
+{
+	struct nvlist *nvl;
+	struct nvpair *nvp;
+	size_t left = 4096;
+	int found = 0;
+
+	offset = (offset & ~(VDEV_LABEL_SIZE - 1)) + VDEV_LABEL_NVPAIR;
+
+	/* Note that we currently assume that the desired fields are within
+	 * the first 4k (left) of the nvlist.  This is true for all pools
+	 * I've seen, and simplifies this code somewhat, because we don't
+	 * have to handle an nvpair crossing a buffer boundary. */
+	nvl = (struct nvlist *)blkid_probe_get_buffer(pr, offset, left);
+	if (nvl == NULL)
+		return;
+
+	nvdebug("zfs_extract: nvlist offset %llu\n", offset);
+
+	nvp = &nvl->nvl_nvpair;
+	while (left > sizeof(*nvp) && nvp->nvp_size != 0 && found < 3) {
+		int avail;   /* tracks that name/value data fits in nvp_size */
+		int namesize;
+
+		nvp->nvp_size = be32_to_cpu(nvp->nvp_size);
+		nvp->nvp_namelen = be32_to_cpu(nvp->nvp_namelen);
+		avail = nvp->nvp_size - nvp->nvp_namelen - sizeof(*nvp);
+
+		nvdebug("left %zd nvp_size %u\n", left, nvp->nvp_size);
+		if (left < nvp->nvp_size || avail < 0)
+			break;
+
+		namesize = (nvp->nvp_namelen + 3) & ~3;
+
+		nvdebug("nvlist: size %u, namelen %u, name %*s\n",
+			nvp->nvp_size, nvp->nvp_namelen, nvp->nvp_namelen,
+			nvp->nvp_name);
+		if (strncmp(nvp->nvp_name, "name", nvp->nvp_namelen) == 0) {
+			struct nvstring *nvs = (void *)(nvp->nvp_name+namesize);
+
+			nvs->nvs_type = be32_to_cpu(nvs->nvs_type);
+			nvs->nvs_strlen = be32_to_cpu(nvs->nvs_strlen);
+			avail -= nvs->nvs_strlen + sizeof(*nvs);
+			nvdebug("nvstring: type %u string %*s\n", nvs->nvs_type,
+				nvs->nvs_strlen, nvs->nvs_string);
+			if (nvs->nvs_type == DATA_TYPE_STRING && avail >= 0)
+				blkid_probe_set_label(pr, nvs->nvs_string,
+						      nvs->nvs_strlen);
+			found++;
+		} else if (strncmp(nvp->nvp_name, "guid",
+				   nvp->nvp_namelen) == 0) {
+			struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
+			uint64_t nvu_value;
+
+			memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
+			nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
+			nvu_value = be64_to_cpu(nvu_value);
+			avail -= sizeof(*nvu);
+			nvdebug("nvuint64: type %u value %"PRIu64"\n",
+				nvu->nvu_type, nvu_value);
+			if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
+				blkid_probe_sprintf_value(pr, "UUID_SUB",
+							  "%"PRIu64, nvu_value);
+			found++;
+		} else if (strncmp(nvp->nvp_name, "pool_guid",
+				   nvp->nvp_namelen) == 0) {
+			struct nvuint64 *nvu = (void *)(nvp->nvp_name+namesize);
+			uint64_t nvu_value;
+
+			memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
+			nvu->nvu_type = be32_to_cpu(nvu->nvu_type);
+			nvu_value = be64_to_cpu(nvu_value);
+			avail -= sizeof(*nvu);
+			nvdebug("nvuint64: type %u value %"PRIu64"\n",
+				nvu->nvu_type, nvu_value);
+			if (nvu->nvu_type == DATA_TYPE_UINT64 && avail >= 0)
+				blkid_probe_sprintf_uuid(pr, (unsigned char *)
+							 &nvu_value,
+							 sizeof(nvu_value),
+							 "%"PRIu64, nvu_value);
+			found++;
+		}
+		if (left > nvp->nvp_size)
+			left -= nvp->nvp_size;
+		else
+			left = 0;
+		nvp = (struct nvpair *)((char *)nvp + nvp->nvp_size);
+	}
+}
+
+#define zdebug(fmt, ...)	do {} while(0)
+/*#define zdebug(fmt, a...)	printf(fmt, ##a)*/
+
+/* ZFS has 128x1kB host-endian root blocks, stored in 2 areas at the start
+ * of the disk, and 2 areas at the end of the disk.  Check only some of them...
+ * #4 (@ 132kB) is the first one written on a new filesystem. */
+static int probe_zfs(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	uint64_t swab_magic = swab64(UBERBLOCK_MAGIC);
+	struct zfs_uberblock *ub;
+	int swab_endian;
+	loff_t offset;
+	int tried;
+	int found;
+
+	zdebug("probe_zfs\n");
+	/* Look for at least 4 uberblocks to ensure a positive match */
+	for (tried = found = 0, offset = VDEV_LABEL_UBERBLOCK;
+	     tried < ZFS_TRIES && found < ZFS_WANT;
+	     tried++, offset += 4096) {
+		/* also try the second uberblock copy */
+		if (tried == (ZFS_TRIES / 2))
+			offset = VDEV_LABEL_SIZE + VDEV_LABEL_UBERBLOCK;
+
+		ub = (struct zfs_uberblock *)
+			blkid_probe_get_buffer(pr, offset,
+					       sizeof(struct zfs_uberblock));
+		if (ub == NULL)
+			return -1;
+
+		if (ub->ub_magic == UBERBLOCK_MAGIC)
+			found++;
+
+		if ((swab_endian = (ub->ub_magic == swab_magic)))
+			found++;
+
+		zdebug("probe_zfs: found %s-endian uberblock at %llu\n",
+		       swab_endian ? "big" : "little", offset >> 10);
+	}
+
+	if (found < 4)
+		return -1;
+
+	/* If we found the 4th uberblock, then we will have exited from the
+	 * scanning loop immediately, and ub will be a valid uberblock. */
+	blkid_probe_sprintf_version(pr, "%" PRIu64, swab_endian ?
+				    swab64(ub->ub_version) : ub->ub_version);
+
+	zfs_extract_guid_name(pr, offset);
+
+	if (blkid_probe_set_magic(pr, offset,
+				sizeof(ub->ub_magic),
+				(unsigned char *) &ub->ub_magic))
+		return -1;
+
+	return 0;
+}
+
+const struct blkid_idinfo zfs_idinfo =
+{
+	.name		= "zfs_member",
+	.usage		= BLKID_USAGE_RAID,
+	.probefunc	= probe_zfs,
+	.minsz		= 64 * 1024 * 1024,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/src/tag.c b/src/tag.c
new file mode 100644
index 0000000..efe67cb
--- /dev/null
+++ b/src/tag.c
@@ -0,0 +1,474 @@
+/*
+ * tag.c - allocation/initialization/free routines for tag structs
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ * Copyright (C) 2003 Theodore Ts'o
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blkidP.h"
+
+static blkid_tag blkid_new_tag(void)
+{
+	blkid_tag tag;
+
+	if (!(tag = (blkid_tag) calloc(1, sizeof(struct blkid_struct_tag))))
+		return NULL;
+
+	INIT_LIST_HEAD(&tag->bit_tags);
+	INIT_LIST_HEAD(&tag->bit_names);
+
+	return tag;
+}
+
+#ifdef CONFIG_BLKID_DEBUG
+void blkid_debug_dump_tag(blkid_tag tag)
+{
+	if (!tag) {
+		printf("    tag: NULL\n");
+		return;
+	}
+
+	printf("    tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val);
+}
+#endif
+
+void blkid_free_tag(blkid_tag tag)
+{
+	if (!tag)
+		return;
+
+	DBG(DEBUG_TAG, printf("    freeing tag %s=%s\n", tag->bit_name,
+		   tag->bit_val ? tag->bit_val : "(NULL)"));
+	DBG(DEBUG_TAG, blkid_debug_dump_tag(tag));
+
+	list_del(&tag->bit_tags);	/* list of tags for this device */
+	list_del(&tag->bit_names);	/* list of tags with this type */
+
+	free(tag->bit_name);
+	free(tag->bit_val);
+
+	free(tag);
+}
+
+/*
+ * Find the desired tag on a device.  If value is NULL, then the
+ * first such tag is returned, otherwise return only exact tag if found.
+ */
+blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type)
+{
+	struct list_head *p;
+
+	if (!dev || !type)
+		return NULL;
+
+	list_for_each(p, &dev->bid_tags) {
+		blkid_tag tmp = list_entry(p, struct blkid_struct_tag,
+					   bit_tags);
+
+		if (!strcmp(tmp->bit_name, type))
+			return tmp;
+	}
+	return NULL;
+}
+
+extern int blkid_dev_has_tag(blkid_dev dev, const char *type,
+			     const char *value)
+{
+	blkid_tag		tag;
+
+	if (!dev || !type)
+		return -1;
+
+	tag = blkid_find_tag_dev(dev, type);
+	if (!value)
+		return (tag != NULL);
+	if (!tag || strcmp(tag->bit_val, value))
+		return 0;
+	return 1;
+}
+
+/*
+ * Find the desired tag type in the cache.
+ * We return the head tag for this tag type.
+ */
+static blkid_tag blkid_find_head_cache(blkid_cache cache, const char *type)
+{
+	blkid_tag head = NULL, tmp;
+	struct list_head *p;
+
+	if (!cache || !type)
+		return NULL;
+
+	list_for_each(p, &cache->bic_tags) {
+		tmp = list_entry(p, struct blkid_struct_tag, bit_tags);
+		if (!strcmp(tmp->bit_name, type)) {
+			DBG(DEBUG_TAG,
+			    printf("    found cache tag head %s\n", type));
+			head = tmp;
+			break;
+		}
+	}
+	return head;
+}
+
+/*
+ * Set a tag on an existing device.
+ *
+ * If value is NULL, then delete the tagsfrom the device.
+ */
+int blkid_set_tag(blkid_dev dev, const char *name,
+		  const char *value, const int vlength)
+{
+	blkid_tag	t = 0, head = 0;
+	char		*val = 0;
+	char		**dev_var = 0;
+
+	if (!dev || !name)
+		return -BLKID_ERR_PARAM;
+
+	if (value && !(val = blkid_strndup(value, vlength)))
+		return -BLKID_ERR_MEM;
+
+	/*
+	 * Certain common tags are linked directly to the device struct
+	 * We need to know what they are before we do anything else because
+	 * the function name parameter might get freed later on.
+	 */
+	if (!strcmp(name, "TYPE"))
+		dev_var = &dev->bid_type;
+	else if (!strcmp(name, "LABEL"))
+		dev_var = &dev->bid_label;
+	else if (!strcmp(name, "UUID"))
+		dev_var = &dev->bid_uuid;
+
+	t = blkid_find_tag_dev(dev, name);
+	if (!value) {
+		if (t)
+			blkid_free_tag(t);
+	} else if (t) {
+		if (!strcmp(t->bit_val, val)) {
+			/* Same thing, exit */
+			free(val);
+			return 0;
+		}
+		free(t->bit_val);
+		t->bit_val = val;
+	} else {
+		/* Existing tag not present, add to device */
+		if (!(t = blkid_new_tag()))
+			goto errout;
+		t->bit_name = blkid_strdup(name);
+		t->bit_val = val;
+		t->bit_dev = dev;
+
+		list_add_tail(&t->bit_tags, &dev->bid_tags);
+
+		if (dev->bid_cache) {
+			head = blkid_find_head_cache(dev->bid_cache,
+						     t->bit_name);
+			if (!head) {
+				head = blkid_new_tag();
+				if (!head)
+					goto errout;
+
+				DBG(DEBUG_TAG,
+				    printf("    creating new cache tag head %s\n", name));
+				head->bit_name = blkid_strdup(name);
+				if (!head->bit_name)
+					goto errout;
+				list_add_tail(&head->bit_tags,
+					      &dev->bid_cache->bic_tags);
+			}
+			list_add_tail(&t->bit_names, &head->bit_names);
+		}
+	}
+
+	/* Link common tags directly to the device struct */
+	if (dev_var)
+		*dev_var = val;
+
+	if (dev->bid_cache)
+		dev->bid_cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+	return 0;
+
+errout:
+	if (t)
+		blkid_free_tag(t);
+	else
+		free(val);
+	if (head)
+		blkid_free_tag(head);
+	return -BLKID_ERR_MEM;
+}
+
+
+/*
+ * Parse a "NAME=value" string.  This is slightly different than
+ * parse_token, because that will end an unquoted value at a space, while
+ * this will assume that an unquoted value is the rest of the token (e.g.
+ * if we are passed an already quoted string from the command-line we don't
+ * have to both quote and escape quote so that the quotes make it to
+ * us).
+ *
+ * Returns 0 on success, and -1 on failure.
+ */
+int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val)
+{
+	char *name, *value, *cp;
+
+	DBG(DEBUG_TAG, printf("trying to parse '%s' as a tag\n", token));
+
+	if (!token || !(cp = strchr(token, '=')))
+		return -1;
+
+	name = blkid_strdup(token);
+	if (!name)
+		return -1;
+	value = name + (cp - token);
+	*value++ = '\0';
+	if (*value == '"' || *value == '\'') {
+		char c = *value++;
+		if (!(cp = strrchr(value, c)))
+			goto errout; /* missing closing quote */
+		*cp = '\0';
+	}
+	value = blkid_strdup(value);
+	if (!value)
+		goto errout;
+
+	*ret_type = name;
+	*ret_val = value;
+
+	return 0;
+
+errout:
+	free(name);
+	return -1;
+}
+
+/*
+ * Tag iteration routines for the public libblkid interface.
+ *
+ * These routines do not expose the list.h implementation, which are a
+ * contamination of the namespace, and which force us to reveal far, far
+ * too much of our internal implemenation.  I'm not convinced I want
+ * to keep list.h in the long term, anyway.  It's fine for kernel
+ * programming, but performance is not the #1 priority for this
+ * library, and I really don't like the tradeoff of type-safety for
+ * performance for this application.  [tytso:20030125.2007EST]
+ */
+
+/*
+ * This series of functions iterate over all tags in a device
+ */
+#define TAG_ITERATE_MAGIC	0x01a5284c
+
+struct blkid_struct_tag_iterate {
+	int			magic;
+	blkid_dev		dev;
+	struct list_head	*p;
+};
+
+extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev)
+{
+	blkid_tag_iterate	iter;
+
+	iter = malloc(sizeof(struct blkid_struct_tag_iterate));
+	if (iter) {
+		iter->magic = TAG_ITERATE_MAGIC;
+		iter->dev = dev;
+		iter->p	= dev->bid_tags.next;
+	}
+	return (iter);
+}
+
+/*
+ * Return 0 on success, -1 on error
+ */
+extern int blkid_tag_next(blkid_tag_iterate iter,
+			  const char **type, const char **value)
+{
+	blkid_tag tag;
+
+	*type = 0;
+	*value = 0;
+	if (!iter || iter->magic != TAG_ITERATE_MAGIC ||
+	    iter->p == &iter->dev->bid_tags)
+		return -1;
+	tag = list_entry(iter->p, struct blkid_struct_tag, bit_tags);
+	*type = tag->bit_name;
+	*value = tag->bit_val;
+	iter->p = iter->p->next;
+	return 0;
+}
+
+extern void blkid_tag_iterate_end(blkid_tag_iterate iter)
+{
+	if (!iter || iter->magic != TAG_ITERATE_MAGIC)
+		return;
+	iter->magic = 0;
+	free(iter);
+}
+
+/*
+ * This function returns a device which matches a particular
+ * type/value pair.  If there is more than one device that matches the
+ * search specification, it returns the one with the highest priority
+ * value.  This allows us to give preference to EVMS or LVM devices.
+ */
+extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache,
+					 const char *type,
+					 const char *value)
+{
+	blkid_tag	head;
+	blkid_dev	dev;
+	int		pri;
+	struct list_head *p;
+	int		probe_new = 0;
+
+	if (!cache || !type || !value)
+		return NULL;
+
+	blkid_read_cache(cache);
+
+	DBG(DEBUG_TAG, printf("looking for %s=%s in cache\n", type, value));
+
+try_again:
+	pri = -1;
+	dev = 0;
+	head = blkid_find_head_cache(cache, type);
+
+	if (head) {
+		list_for_each(p, &head->bit_names) {
+			blkid_tag tmp = list_entry(p, struct blkid_struct_tag,
+						   bit_names);
+
+			if (!strcmp(tmp->bit_val, value) &&
+			    (tmp->bit_dev->bid_pri > pri) &&
+			    !access(tmp->bit_dev->bid_name, F_OK)) {
+				dev = tmp->bit_dev;
+				pri = dev->bid_pri;
+			}
+		}
+	}
+	if (dev && !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
+		dev = blkid_verify(cache, dev);
+		if (!dev || (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED)))
+			goto try_again;
+	}
+
+	if (!dev && !probe_new) {
+		if (blkid_probe_all_new(cache) < 0)
+			return NULL;
+		probe_new++;
+		goto try_again;
+	}
+
+	if (!dev && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) {
+		if (blkid_probe_all(cache) < 0)
+			return NULL;
+		goto try_again;
+	}
+	return dev;
+}
+
+#ifdef TEST_PROGRAM
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+void __attribute__((__noreturn__)) usage(char *prog)
+{
+	fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask] device "
+		"[type value]\n",
+		prog);
+	fprintf(stderr, "\tList all tags for a device and exit\n");
+	exit(1);
+}
+
+int main(int argc, char **argv)
+{
+	blkid_tag_iterate	iter;
+	blkid_cache 		cache = NULL;
+	blkid_dev		dev;
+	int			c, ret, found;
+	int			flags = BLKID_DEV_FIND;
+	char			*tmp;
+	char			*file = NULL;
+	char			*devname = NULL;
+	char			*search_type = NULL;
+	char			*search_value = NULL;
+	const char		*type, *value;
+
+	while ((c = getopt (argc, argv, "m:f:")) != EOF)
+		switch (c) {
+		case 'f':
+			file = optarg;
+			break;
+		case 'm':
+		{
+			int mask = strtoul (optarg, &tmp, 0);
+			if (*tmp) {
+				fprintf(stderr, "Invalid debug mask: %s\n",
+					optarg);
+				exit(1);
+			}
+			blkid_init_debug(mask);
+			break;
+		}
+		case '?':
+			usage(argv[0]);
+		}
+	if (argc > optind)
+		devname = argv[optind++];
+	if (argc > optind)
+		search_type = argv[optind++];
+	if (argc > optind)
+		search_value = argv[optind++];
+	if (!devname || (argc != optind))
+		usage(argv[0]);
+
+	if ((ret = blkid_get_cache(&cache, file)) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+
+	dev = blkid_get_dev(cache, devname, flags);
+	if (!dev) {
+		fprintf(stderr, "%s: Can not find device in blkid cache\n",
+			devname);
+		exit(1);
+	}
+	if (search_type) {
+		found = blkid_dev_has_tag(dev, search_type, search_value);
+		printf("Device %s: (%s, %s) %s\n", blkid_dev_devname(dev),
+		       search_type, search_value ? search_value : "NULL",
+		       found ? "FOUND" : "NOT FOUND");
+		return(!found);
+	}
+	printf("Device %s...\n", blkid_dev_devname(dev));
+
+	iter = blkid_tag_iterate_begin(dev);
+	while (blkid_tag_next(iter, &type, &value) == 0) {
+		printf("\tTag %s has value %s\n", type, value);
+	}
+	blkid_tag_iterate_end(iter);
+
+	blkid_put_cache(cache);
+	return (0);
+}
+#endif
diff --git a/src/topology/dm.c b/src/topology/dm.c
new file mode 100644
index 0000000..72ec9bd
--- /dev/null
+++ b/src/topology/dm.c
@@ -0,0 +1,138 @@
+/*
+ * device-mapper (dm) topology
+ * -- this is fallback for old systems where the topology information is not
+ *    exported by sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "topology.h"
+
+static int is_dm_device(dev_t devno)
+{
+	return blkid_driver_has_major("device-mapper", major(devno));
+}
+
+static int probe_dm_tp(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	const char *paths[] = {
+		"/usr/local/sbin/dmsetup",
+		"/usr/sbin/dmsetup",
+		"/sbin/dmsetup"
+	};
+	int dmpipe[] = { -1, -1 }, stripes, stripesize;
+	char *cmd = NULL;
+	FILE *stream = NULL;
+	long long  offset, size;
+	size_t i;
+	dev_t devno = blkid_probe_get_devno(pr);
+
+	if (!devno)
+		goto nothing;		/* probably not a block device */
+	if (!is_dm_device(devno))
+		goto nothing;
+
+	for (i = 0; i < ARRAY_SIZE(paths); i++) {
+		struct stat sb;
+		if (stat(paths[i], &sb) == 0) {
+			cmd = (char *) paths[i];
+			break;
+		}
+	}
+
+	if (!cmd)
+		goto nothing;
+	if (pipe(dmpipe) < 0) {
+		DBG(DEBUG_LOWPROBE,
+			printf("Failed to open pipe: errno=%d", errno));
+		goto nothing;
+	}
+
+	switch (fork()) {
+	case 0:
+	{
+		char *dmargv[7], maj[16], min[16];
+
+		/* Plumbing */
+		close(dmpipe[0]);
+
+		if (dmpipe[1] != STDOUT_FILENO)
+			dup2(dmpipe[1], STDOUT_FILENO);
+
+		/* The libblkid library could linked with setuid programs */
+		if (setgid(getgid()) < 0)
+			 exit(1);
+		if (setuid(getuid()) < 0)
+			 exit(1);
+
+		snprintf(maj, sizeof(maj), "%d", major(devno));
+		snprintf(min, sizeof(min), "%d", minor(devno));
+
+		dmargv[0] = cmd;
+		dmargv[1] = "table";
+		dmargv[2] = "-j";
+		dmargv[3] = maj;
+		dmargv[4] = "-m";
+		dmargv[5] = min;
+		dmargv[6] = NULL;
+
+		execv(dmargv[0], dmargv);
+
+		DBG(DEBUG_LOWPROBE,
+			printf("Failed to execute %s: errno=%d", cmd, errno));
+		exit(1);
+	}
+	case -1:
+		DBG(DEBUG_LOWPROBE,
+			printf("Failed to forking: errno=%d", errno));
+		goto nothing;
+	default:
+		break;
+	}
+
+	stream = fdopen(dmpipe[0], "r");
+	if (!stream)
+		goto nothing;
+
+	if (fscanf(stream, "%lld %lld striped %d %d ",
+			&offset, &size, &stripes, &stripesize) != 0)
+		goto nothing;
+
+	blkid_topology_set_minimum_io_size(pr, stripesize << 9);
+	blkid_topology_set_optimal_io_size(pr, (stripes * stripesize) << 9);
+
+	fclose(stream);
+	close(dmpipe[1]);
+	return 0;
+
+nothing:
+	if (stream)
+		fclose(stream);
+	else if (dmpipe[0] != -1)
+		close(dmpipe[0]);
+	if (dmpipe[1] != -1)
+		close(dmpipe[1]);
+	return 1;
+}
+
+const struct blkid_idinfo dm_tp_idinfo =
+{
+	.name		= "dm",
+	.probefunc	= probe_dm_tp,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/src/topology/evms.c b/src/topology/evms.c
new file mode 100644
index 0000000..7a4fd55
--- /dev/null
+++ b/src/topology/evms.c
@@ -0,0 +1,77 @@
+/*
+ * Evms topology
+ * -- this is fallback for old systems where the toplogy information is not
+ *    exported by sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "topology.h"
+
+#define EVMS_MAJOR		117
+
+#ifndef _IOT__IOTBASE_u_int32_t
+#define _IOT__IOTBASE_u_int32_t IOT_SIMPLE(uint32_t)
+#endif
+#define _IOT_evms_stripe_info _IOT (_IOTS(uint32_t), 2, 0, 0, 0, 0)
+#define EVMS_GET_STRIPE_INFO	_IOR(EVMS_MAJOR, 0xF0, struct evms_stripe_info)
+
+struct evms_stripe_info {
+	uint32_t	size;		/* stripe unit 512-byte blocks */
+	uint32_t	width;		/* the number of stripe members or RAID data disks */
+} evms_stripe_info;
+
+static int is_evms_device(dev_t devno)
+{
+	if (major(devno) == EVMS_MAJOR)
+		return 1;
+	return blkid_driver_has_major("evms", major(devno));
+}
+
+static int probe_evms_tp(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	struct evms_stripe_info evms;
+	dev_t devno = blkid_probe_get_devno(pr);
+
+	if (!devno)
+		goto nothing;		/* probably not a block device */
+
+	if (!is_evms_device(devno))
+		goto nothing;
+
+	memset(&evms, 0, sizeof(evms));
+
+	if (ioctl(pr->fd, EVMS_GET_STRIPE_INFO, &evms))
+		goto nothing;
+
+	blkid_topology_set_minimum_io_size(pr, evms.size << 9);
+	blkid_topology_set_optimal_io_size(pr, (evms.size * evms.width) << 9);
+
+	return 0;
+
+nothing:
+	return 1;
+}
+
+const struct blkid_idinfo evms_tp_idinfo =
+{
+	.name		= "evms",
+	.probefunc	= probe_evms_tp,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/src/topology/ioctl.c b/src/topology/ioctl.c
new file mode 100644
index 0000000..3aba09e
--- /dev/null
+++ b/src/topology/ioctl.c
@@ -0,0 +1,74 @@
+/*
+ * ioctl based topology -- gathers topology information
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "topology.h"
+
+/*
+ * ioctl topology values
+ */
+static struct topology_val {
+
+	long  ioc;
+
+	/* functions to set probing result */
+	int (*set_ulong)(blkid_probe, unsigned long);
+	int (*set_int)(blkid_probe, int);
+
+} topology_vals[] = {
+	{ BLKALIGNOFF, NULL, blkid_topology_set_alignment_offset },
+	{ BLKIOMIN, blkid_topology_set_minimum_io_size },
+	{ BLKIOOPT, blkid_topology_set_optimal_io_size },
+	{ BLKPBSZGET, blkid_topology_set_physical_sector_size }
+	/* we read BLKSSZGET in topology.c */
+};
+
+static int probe_ioctl_tp(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(topology_vals); i++) {
+		struct topology_val *val = &topology_vals[i];
+		int rc = 1;
+		unsigned int data;
+
+		if (ioctl(pr->fd, val->ioc, &data) == -1)
+			goto nothing;
+
+		if (val->set_int)
+			rc = val->set_int(pr, (int) data);
+		else
+			rc = val->set_ulong(pr, (unsigned long) data);
+		if (rc)
+			goto err;
+	}
+
+	return 0;
+nothing:
+	return 1;
+err:
+	return -1;
+}
+
+const struct blkid_idinfo ioctl_tp_idinfo =
+{
+	.name		= "ioctl",
+	.probefunc	= probe_ioctl_tp,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/src/topology/lvm.c b/src/topology/lvm.c
new file mode 100644
index 0000000..632c42b
--- /dev/null
+++ b/src/topology/lvm.c
@@ -0,0 +1,150 @@
+/*
+ * lvm topology
+ * -- this is fallback for old systems where the topology information is not
+ *    exported by sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "topology.h"
+
+#ifndef LVM_BLK_MAJOR
+# define LVM_BLK_MAJOR     58
+#endif
+
+static int is_lvm_device(dev_t devno)
+{
+	if (major(devno) == LVM_BLK_MAJOR)
+		return 1;
+	return blkid_driver_has_major("lvm", major(devno));
+}
+
+static int probe_lvm_tp(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	const char *paths[] = {
+		"/usr/local/sbin/lvdisplay",
+		"/usr/sbin/lvdisplay",
+		"/sbin/lvdisplay"
+	};
+	int lvpipe[] = { -1, -1 }, stripes = 0, stripesize = 0;
+	FILE *stream = NULL;
+	char *cmd = NULL, *devname = NULL, buf[1024];
+	size_t i;
+	dev_t devno = blkid_probe_get_devno(pr);
+
+	if (!devno)
+		goto nothing;		/* probably not a block device */
+	if (!is_lvm_device(devno))
+		goto nothing;
+
+	for (i = 0; i < ARRAY_SIZE(paths); i++) {
+		struct stat sb;
+		if (stat(paths[i], &sb) == 0) {
+			cmd = (char *) paths[i];
+			break;
+		}
+	}
+
+	if (!cmd)
+		goto nothing;
+
+	devname = blkid_devno_to_devname(devno);
+	if (!devname)
+		goto nothing;
+
+	if (pipe(lvpipe) < 0) {
+		DBG(DEBUG_LOWPROBE,
+			printf("Failed to open pipe: errno=%d", errno));
+		goto nothing;
+	}
+
+	switch (fork()) {
+	case 0:
+	{
+		char *lvargv[3];
+
+		/* Plumbing */
+		close(lvpipe[0]);
+
+		if (lvpipe[1] != STDOUT_FILENO)
+			dup2(lvpipe[1], STDOUT_FILENO);
+
+		/* The libblkid library could linked with setuid programs */
+		if (setgid(getgid()) < 0)
+			 exit(1);
+		if (setuid(getuid()) < 0)
+			 exit(1);
+
+		lvargv[0] = cmd;
+		lvargv[1] = devname;
+		lvargv[2] = NULL;
+
+		execv(lvargv[0], lvargv);
+
+		DBG(DEBUG_LOWPROBE,
+			printf("Failed to execute %s: errno=%d", cmd, errno));
+		exit(1);
+	}
+	case -1:
+		DBG(DEBUG_LOWPROBE,
+			printf("Failed to forking: errno=%d", errno));
+		goto nothing;
+	default:
+		break;
+	}
+
+	stream = fdopen(lvpipe[0], "r");
+	if (!stream)
+		goto nothing;
+
+	while (fgets(buf, sizeof(buf), stream) != NULL) {
+		if (!strncmp(buf, "Stripes", 7))
+			sscanf(buf, "Stripes %d", &stripes);
+
+		if (!strncmp(buf, "Stripe size", 11))
+			sscanf(buf, "Stripe size (KByte) %d", &stripesize);
+	}
+
+	if (!stripes)
+		goto nothing;
+
+	blkid_topology_set_minimum_io_size(pr, stripesize << 10);
+	blkid_topology_set_optimal_io_size(pr, (stripes * stripesize) << 10);
+
+	free(devname);
+	fclose(stream);
+	close(lvpipe[1]);
+	return 0;
+
+nothing:
+	free(devname);
+	if (stream)
+		fclose(stream);
+	else if (lvpipe[0] != -1)
+		close(lvpipe[0]);
+	if (lvpipe[1] != -1)
+		close(lvpipe[1]);
+	return 1;
+}
+
+const struct blkid_idinfo lvm_tp_idinfo =
+{
+	.name		= "lvm",
+	.probefunc	= probe_lvm_tp,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/src/topology/md.c b/src/topology/md.c
new file mode 100644
index 0000000..a24fbac
--- /dev/null
+++ b/src/topology/md.c
@@ -0,0 +1,154 @@
+/*
+ * Linux Software RAID (md) topology
+ * -- this is fallback for old systems where the topology information is not
+ *    exported by sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "topology.h"
+
+#ifndef MD_MAJOR
+#define MD_MAJOR	9
+#endif
+
+#ifndef _IOT__IOTBASE_uint32_t
+#define _IOT__IOTBASE_uint32_t IOT_SIMPLE(uint32_t)
+#endif
+#define _IOT_md_array_info _IOT (_IOTS(uint32_t), 18, 0, 0, 0, 0)
+#define GET_ARRAY_INFO          _IOR (MD_MAJOR, 0x11, struct md_array_info)
+
+struct md_array_info {
+	/*
+	 * Generic constant information
+	 */
+	uint32_t major_version;
+	uint32_t minor_version;
+	uint32_t patch_version;
+	uint32_t ctime;
+	uint32_t level;
+	uint32_t size;
+	uint32_t nr_disks;
+	uint32_t raid_disks;
+	uint32_t md_minor;
+	uint32_t not_persistent;
+
+	/*
+	 * Generic state information
+	 */
+	uint32_t utime;	  /*  0 Superblock update time		  */
+	uint32_t state;	  /*  1 State bits (clean, ...)		  */
+	uint32_t active_disks;  /*  2 Number of currently active disks  */
+	uint32_t working_disks; /*  3 Number of working disks		  */
+	uint32_t failed_disks;  /*  4 Number of failed disks		  */
+	uint32_t spare_disks;	  /*  5 Number of spare disks		  */
+
+	/*
+	 * Personality information
+	 */
+	uint32_t layout;	  /*  0 the array's physical layout	  */
+	uint32_t chunk_size;	  /*  1 chunk size in bytes		  */
+
+};
+
+static int is_md_device(dev_t devno)
+{
+	if (major(devno) == MD_MAJOR)
+		return 1;
+	return blkid_driver_has_major("md", major(devno));
+}
+
+static int probe_md_tp(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	int fd = -1;
+	dev_t disk = 0;
+	dev_t devno = blkid_probe_get_devno(pr);
+	struct md_array_info md;
+
+	if (!devno)
+		goto nothing;		/* probably not a block device */
+
+	if (!is_md_device(devno))
+		goto nothing;
+
+	if (blkid_devno_to_wholedisk(devno, NULL, 0, &disk))
+		goto nothing;
+
+	if (disk == devno)
+		fd = pr->fd;
+	else {
+		char *diskpath = blkid_devno_to_devname(disk);
+
+		if (!diskpath)
+			goto nothing;
+
+		fd = open(diskpath, O_RDONLY);
+		free(diskpath);
+
+                if (fd == -1)
+			goto nothing;
+	}
+
+	memset(&md, 0, sizeof(md));
+
+	if (ioctl(fd, GET_ARRAY_INFO, &md))
+		goto nothing;
+
+	if (fd >= 0 && fd != pr->fd) {
+		close(fd);
+		fd = -1;
+	}
+
+	/*
+	 * Ignore levels we don't want aligned (e.g. linear)
+	 * and deduct disk(s) from stripe width on RAID4/5/6
+	 */
+	switch (md.level) {
+	case 6:
+		md.raid_disks--;
+		/* fallthrough */
+	case 5:
+	case 4:
+		md.raid_disks--;
+		/* fallthrough */
+	case 1:
+	case 0:
+	case 10:
+		break;
+	default:
+		goto nothing;
+	}
+
+	blkid_topology_set_minimum_io_size(pr, md.chunk_size);
+	blkid_topology_set_optimal_io_size(pr, md.chunk_size * md.raid_disks);
+
+	return 0;
+
+nothing:
+	if (fd >= 0 && fd != pr->fd)
+		close(fd);
+	return 1;
+}
+
+const struct blkid_idinfo md_tp_idinfo =
+{
+	.name		= "md",
+	.probefunc	= probe_md_tp,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/src/topology/sysfs.c b/src/topology/sysfs.c
new file mode 100644
index 0000000..a04b20a
--- /dev/null
+++ b/src/topology/sysfs.c
@@ -0,0 +1,119 @@
+/*
+ * sysfs based topology -- gathers topology information from Linux sysfs
+ *
+ * Copyright (C) 2009 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * For more information see Linux kernel Documentation/ABI/testing/sysfs-block.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "sysfs.h"
+#include "topology.h"
+
+/*
+ * Sysfs topology values (since 2.6.31, May 2009).
+ */
+static struct topology_val {
+
+	/* /sys/dev/block/<maj>:<min>/<ATTR> */
+	const char *attr;
+
+	/* functions to set probing resut */
+	int (*set_ulong)(blkid_probe, unsigned long);
+	int (*set_int)(blkid_probe, int);
+
+} topology_vals[] = {
+	{ "alignment_offset", NULL, blkid_topology_set_alignment_offset },
+	{ "queue/minimum_io_size", blkid_topology_set_minimum_io_size },
+	{ "queue/optimal_io_size", blkid_topology_set_optimal_io_size },
+	{ "queue/physical_block_size", blkid_topology_set_physical_sector_size },
+};
+
+static int probe_sysfs_tp(blkid_probe pr,
+		const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+	dev_t dev, disk = 0;
+	int rc;
+	struct sysfs_cxt sysfs = UL_SYSFSCXT_EMPTY,
+			 parent = UL_SYSFSCXT_EMPTY;
+	size_t i, count = 0;
+
+	dev = blkid_probe_get_devno(pr);
+	if (!dev || sysfs_init(&sysfs, dev, NULL) != 0)
+		return 1;
+
+	rc = 1;		/* nothing (default) */
+
+	for (i = 0; i < ARRAY_SIZE(topology_vals); i++) {
+		struct topology_val *val = &topology_vals[i];
+		int ok = sysfs_has_attribute(&sysfs, val->attr);
+
+		rc = 1;	/* nothing */
+
+		if (!ok) {
+			if (!disk) {
+				/*
+				 * Read atrributes from "disk" if the current
+				 * device is a partition.
+				 */
+				disk = blkid_probe_get_wholedisk_devno(pr);
+				if (disk && disk != dev) {
+					if (sysfs_init(&parent, disk, NULL) != 0)
+						goto done;
+
+					sysfs.parent = &parent;
+					ok = sysfs_has_attribute(&sysfs,
+								 val->attr);
+				}
+			}
+			if (!ok)
+				continue;	/* attribute does not exist */
+		}
+
+		if (val->set_ulong) {
+			uint64_t data;
+
+			if (sysfs_read_u64(&sysfs, val->attr, &data) != 0)
+				continue;
+			rc = val->set_ulong(pr, (unsigned long) data);
+
+		} else if (val->set_int) {
+			int64_t data;
+
+			if (sysfs_read_s64(&sysfs, val->attr, &data) != 0)
+				continue;
+			rc = val->set_int(pr, (int) data);
+		}
+
+		if (rc < 0)
+			goto done;	/* error */
+		if (rc == 0)
+			count++;
+	}
+
+done:
+	sysfs_deinit(&sysfs);
+	sysfs_deinit(&parent);
+
+	if (count)
+		return 0;		/* success */
+	return rc;			/* error or nothing */
+}
+
+const struct blkid_idinfo sysfs_tp_idinfo =
+{
+	.name		= "sysfs",
+	.probefunc	= probe_sysfs_tp,
+	.magics		= BLKID_NONE_MAGIC
+};
+
diff --git a/src/topology/topology.c b/src/topology/topology.c
new file mode 100644
index 0000000..73a397a
--- /dev/null
+++ b/src/topology/topology.c
@@ -0,0 +1,366 @@
+/*
+ * topology - gathers information about device topology
+ *
+ * Copyright 2009 Red Hat, Inc.  All rights reserved.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+#include "topology.h"
+
+/**
+ * SECTION:topology
+ * @title: Topology information
+ * @short_description: block device topology information.
+ *
+ * The topology chain provides details about Linux block devices, for more
+ * information see:
+ *
+ *      Linux kernel Documentation/ABI/testing/sysfs-block
+ *
+ * NAME=value (tags) interface is enabled by blkid_probe_enable_topology(),
+ * and provides:
+ *
+ * @LOGICAL_SECTOR_SIZE: this is the smallest unit the storage device can
+ *                       address. It is typically 512 bytes.
+ *
+ * @PHYSICAL_SECTOR_SIZE: this is the smallest unit a physical storage device
+ *                        can write atomically. It is usually the same as the
+ *                        logical sector size but may be bigger.
+ *
+ * @MINIMUM_IO_SIZE: minimum size which is the device's preferred unit of I/O.
+ *                   For RAID arrays it is often the stripe chunk size.
+ *
+ * @OPTIMAL_IO_SIZE: usually the stripe width for RAID or zero. For RAID arrays
+ *                   it is usually the stripe width or the internal track size.
+ *
+ * @ALIGNMENT_OFFSET: indicates how many bytes the beginning of the device is
+ *                    offset from the disk's natural alignment.
+ *
+ * The NAME=value tags are not defined when the corresponding topology value
+ * is zero. The MINIMUM_IO_SIZE should be always defined if kernel provides
+ * topology information.
+ *
+ * Binary interface:
+ *
+ * blkid_probe_get_topology()
+ *
+ * blkid_topology_get_'VALUENAME'()
+ */
+static int topology_probe(blkid_probe pr, struct blkid_chain *chn);
+static void topology_free(blkid_probe pr, void *data);
+static int topology_is_complete(blkid_probe pr);
+static int topology_set_logical_sector_size(blkid_probe pr);
+
+/*
+ * Binary interface
+ */
+struct blkid_struct_topology {
+	unsigned long	alignment_offset;
+	unsigned long	minimum_io_size;
+	unsigned long	optimal_io_size;
+	unsigned long	logical_sector_size;
+	unsigned long	physical_sector_size;
+};
+
+/*
+ * Topology chain probing functions
+ */
+static const struct blkid_idinfo *idinfos[] =
+{
+#ifdef __linux__
+	&ioctl_tp_idinfo,
+	&sysfs_tp_idinfo,
+	&md_tp_idinfo,
+	&dm_tp_idinfo,
+	&lvm_tp_idinfo,
+	&evms_tp_idinfo
+#endif
+};
+
+
+/*
+ * Driver definition
+ */
+const struct blkid_chaindrv topology_drv = {
+	.id           = BLKID_CHAIN_TOPLGY,
+	.name         = "topology",
+	.dflt_enabled = FALSE,
+	.idinfos      = idinfos,
+	.nidinfos     = ARRAY_SIZE(idinfos),
+	.probe        = topology_probe,
+	.safeprobe    = topology_probe,
+	.free_data    = topology_free
+};
+
+/**
+ * blkid_probe_enable_topology:
+ * @pr: probe
+ * @enable: TRUE/FALSE
+ *
+ * Enables/disables the topology probing for non-binary interface.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_enable_topology(blkid_probe pr, int enable)
+{
+	if (!pr)
+		return -1;
+	pr->chains[BLKID_CHAIN_TOPLGY].enabled = enable;
+	return 0;
+}
+
+/**
+ * blkid_probe_get_topology:
+ * @pr: probe
+ *
+ * This is a binary interface for topology values. See also blkid_topology_*
+ * functions.
+ *
+ * This function is independent on blkid_do_[safe,full]probe() and
+ * blkid_probe_enable_topology() calls.
+ *
+ * WARNING: the returned object will be overwritten by the next
+ *          blkid_probe_get_topology() call for the same @pr. If you want to
+ *          use more blkid_topopogy objects in the same time you have to create
+ *          more blkid_probe handlers (see blkid_new_probe()).
+ *
+ * Returns: blkid_topopogy, or NULL in case of error.
+ */
+blkid_topology blkid_probe_get_topology(blkid_probe pr)
+{
+	return (blkid_topology) blkid_probe_get_binary_data(pr,
+			&pr->chains[BLKID_CHAIN_TOPLGY]);
+}
+
+/*
+ * The blkid_do_probe() backend.
+ */
+static int topology_probe(blkid_probe pr, struct blkid_chain *chn)
+{
+	size_t i;
+
+	if (!pr || chn->idx < -1)
+		return -1;
+
+	if (!S_ISBLK(pr->mode))
+		return -1;	/* nothing, works with block devices only */
+
+	if (chn->binary) {
+		DBG(DEBUG_LOWPROBE, printf("initialize topology binary data\n"));
+
+		if (chn->data)
+			/* reset binary data */
+			memset(chn->data, 0,
+					sizeof(struct blkid_struct_topology));
+		else {
+			chn->data = calloc(1,
+					sizeof(struct blkid_struct_topology));
+			if (!chn->data)
+				return -1;
+		}
+	}
+
+	blkid_probe_chain_reset_vals(pr, chn);
+
+	DBG(DEBUG_LOWPROBE,
+		printf("--> starting probing loop [TOPOLOGY idx=%d]\n",
+		chn->idx));
+
+	i = chn->idx < 0 ? 0 : chn->idx + 1U;
+
+	for ( ; i < ARRAY_SIZE(idinfos); i++) {
+		const struct blkid_idinfo *id = idinfos[i];
+
+		chn->idx = i;
+
+		if (id->probefunc) {
+			DBG(DEBUG_LOWPROBE, printf(
+				"%s: call probefunc()\n", id->name));
+			if (id->probefunc(pr, NULL) != 0)
+				continue;
+		}
+
+		if (!topology_is_complete(pr))
+			continue;
+
+		/* generic for all probing drivers */
+		topology_set_logical_sector_size(pr);
+
+		DBG(DEBUG_LOWPROBE,
+			printf("<-- leaving probing loop (type=%s) [TOPOLOGY idx=%d]\n",
+			id->name, chn->idx));
+		return 0;
+	}
+
+	DBG(DEBUG_LOWPROBE,
+		printf("<-- leaving probing loop (failed) [TOPOLOGY idx=%d]\n",
+		chn->idx));
+	return 1;
+}
+
+static void topology_free(blkid_probe pr __attribute__((__unused__)),
+			  void *data)
+{
+	free(data);
+}
+
+static int topology_set_value(blkid_probe pr, const char *name,
+				size_t structoff, unsigned long data)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	if (!chn)
+		return -1;
+	if (!data)
+		return 0;	/* ignore zeros */
+
+	if (chn->binary) {
+		memcpy(chn->data + structoff, &data, sizeof(data));
+		return 0;
+	}
+	return blkid_probe_sprintf_value(pr, name, "%lu", data);
+}
+
+
+/* the topology info is complete when we have at least "minimum_io_size" which
+ * is provided by all blkid topology drivers */
+static int topology_is_complete(blkid_probe pr)
+{
+	struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+	if (!chn)
+		return FALSE;
+
+	if (chn->binary && chn->data) {
+		blkid_topology tp = (blkid_topology) chn->data;
+		if (tp->minimum_io_size)
+			return TRUE;
+	}
+
+	return __blkid_probe_lookup_value(pr, "MINIMUM_IO_SIZE") ? TRUE : FALSE;
+}
+
+int blkid_topology_set_alignment_offset(blkid_probe pr, int val)
+{
+	unsigned long xval;
+
+	/* Welcome to Hell. The kernel is able to return -1 as an
+	 * alignment_offset if no compatible sizes and alignments
+	 * exist for stacked devices.
+	 *
+	 * There is no way how libblkid caller can respond to the value -1, so
+	 * we will hide this corner case...
+	 *
+	 * (TODO: maybe we can export an extra boolean value 'misaligned' rather
+	 *  then complete hide this problem.)
+	 */
+	xval = val < 0 ? 0 : val;
+
+	return topology_set_value(pr,
+			"ALIGNMENT_OFFSET",
+			offsetof(struct blkid_struct_topology, alignment_offset),
+			xval);
+}
+
+int blkid_topology_set_minimum_io_size(blkid_probe pr, unsigned long val)
+{
+	return topology_set_value(pr,
+			"MINIMUM_IO_SIZE",
+			offsetof(struct blkid_struct_topology, minimum_io_size),
+			val);
+}
+
+int blkid_topology_set_optimal_io_size(blkid_probe pr, unsigned long val)
+{
+	return topology_set_value(pr,
+			"OPTIMAL_IO_SIZE",
+			offsetof(struct blkid_struct_topology, optimal_io_size),
+			val);
+}
+
+/* BLKSSZGET is provided on all systems since 2.3.3 -- so we don't have to
+ * waste time with sysfs.
+ */
+static int topology_set_logical_sector_size(blkid_probe pr)
+{
+	unsigned long val = blkid_probe_get_sectorsize(pr);
+
+	if (!val)
+		return -1;
+
+	return topology_set_value(pr,
+			"LOGICAL_SECTOR_SIZE",
+			offsetof(struct blkid_struct_topology, logical_sector_size),
+			val);
+}
+
+int blkid_topology_set_physical_sector_size(blkid_probe pr, unsigned long val)
+{
+	return topology_set_value(pr,
+			"PHYSICAL_SECTOR_SIZE",
+			offsetof(struct blkid_struct_topology, physical_sector_size),
+			val);
+}
+
+/**
+ * blkid_topology_get_alignment_offset:
+ * @tp: topology
+ *
+ * Returns: alignment offset in bytes or 0.
+ */
+unsigned long blkid_topology_get_alignment_offset(blkid_topology tp)
+{
+	return tp ? tp->alignment_offset : 0;
+}
+
+/**
+ * blkid_topology_get_minimum_io_size:
+ * @tp: topology
+ *
+ * Returns: minimum io size in bytes or 0.
+ */
+unsigned long blkid_topology_get_minimum_io_size(blkid_topology tp)
+{
+	return tp ? tp->minimum_io_size : 0;
+}
+
+/**
+ * blkid_topology_get_optimal_io_size
+ * @tp: topology
+ *
+ * Returns: optimal io size in bytes or 0.
+ */
+unsigned long blkid_topology_get_optimal_io_size(blkid_topology tp)
+{
+	return tp ? tp->optimal_io_size : 0;
+}
+
+/**
+ * blkid_topology_get_logical_sector_size
+ * @tp: topology
+ *
+ * Returns: logical sector size (BLKSSZGET ioctl) in bytes or 0.
+ */
+unsigned long blkid_topology_get_logical_sector_size(blkid_topology tp)
+{
+	return tp ? tp->logical_sector_size : 0;
+}
+
+/**
+ * blkid_topology_get_physical_sector_size
+ * @tp: topology
+ *
+ * Returns: logical sector size (BLKSSZGET ioctl) in bytes or 0.
+ */
+unsigned long blkid_topology_get_physical_sector_size(blkid_topology tp)
+{
+	return tp ? tp->physical_sector_size : 0;
+}
+
diff --git a/src/topology/topology.h b/src/topology/topology.h
new file mode 100644
index 0000000..6d2f433
--- /dev/null
+++ b/src/topology/topology.h
@@ -0,0 +1,24 @@
+#ifndef BLKID_TOPOLOGY_H
+#define BLKID_TOPOLOGY_H
+
+#include "blkidP.h"
+
+extern int blkid_topology_set_alignment_offset(blkid_probe pr, int val);
+extern int blkid_topology_set_minimum_io_size(blkid_probe pr, unsigned long val);
+extern int blkid_topology_set_optimal_io_size(blkid_probe pr, unsigned long val);
+extern int blkid_topology_set_physical_sector_size(blkid_probe pr, unsigned long val);
+
+/*
+ * topology probers
+ */
+#ifdef __linux__
+extern const struct blkid_idinfo ioctl_tp_idinfo;
+extern const struct blkid_idinfo md_tp_idinfo;
+extern const struct blkid_idinfo evms_tp_idinfo;
+extern const struct blkid_idinfo sysfs_tp_idinfo;
+extern const struct blkid_idinfo dm_tp_idinfo;
+extern const struct blkid_idinfo lvm_tp_idinfo;
+#endif
+
+#endif /* BLKID_TOPOLOGY_H */
+
diff --git a/src/verify.c b/src/verify.c
new file mode 100644
index 0000000..4bab4de
--- /dev/null
+++ b/src/verify.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "blkidP.h"
+
+static void blkid_probe_to_tags(blkid_probe pr, blkid_dev dev)
+{
+	const char *data;
+	const char *name;
+	int nvals, n;
+	size_t len;
+
+	nvals = blkid_probe_numof_values(pr);
+
+	for (n = 0; n < nvals; n++) {
+		if (blkid_probe_get_value(pr, n, &name, &data, &len) != 0)
+			continue;
+		if (strncmp(name, "PART_ENTRY_", 11) == 0) {
+			if (strcmp(name, "PART_ENTRY_UUID") == 0)
+				blkid_set_tag(dev, "PARTUUID", data, len);
+			else if (strcmp(name, "PART_ENTRY_NAME") == 0)
+				blkid_set_tag(dev, "PARTLABEL", data, len);
+		} else {
+			/* superblock UUID, LABEL, ... */
+			blkid_set_tag(dev, name, data, len);
+		}
+	}
+
+	/*
+	 * remove obsolete tags
+	 */
+	if (!nvals || !blkid_probe_has_value(pr, "LABEL"))
+		blkid_set_tag(dev, "LABEL", NULL, 0);
+	if (!nvals || !blkid_probe_has_value(pr, "UUID"))
+		blkid_set_tag(dev, "UUID", NULL, 0);
+	if (!nvals || !blkid_probe_has_value(pr, "PART_ENTRY_UUID"))
+		blkid_set_tag(dev, "PARTUUID", NULL, 0);
+	if (!nvals || !blkid_probe_has_value(pr, "PART_ENTRY_NAME"))
+		blkid_set_tag(dev, "PARTLABEL", NULL, 0);
+	if (!nvals || !blkid_probe_has_value(pr, "TYPE"))
+		blkid_set_tag(dev, "TYPE", NULL, 0);
+	if (!nvals || !blkid_probe_has_value(pr, "SEC_TYPE"))
+		blkid_set_tag(dev, "SEC_TYPE", NULL, 0);
+	if (!nvals || !blkid_probe_has_value(pr, "EXT_JOURNAL"))	/* extN */
+		blkid_set_tag(dev, "EXT_JOURNAL", NULL, 0);
+	if (!nvals || !blkid_probe_has_value(pr, "MOUNT"))		/* ocfs */
+		blkid_set_tag(dev, "MOUNT", NULL, 0);
+}
+
+/*
+ * Verify that the data in dev is consistent with what is on the actual
+ * block device (using the devname field only).  Normally this will be
+ * called when finding items in the cache, but for long running processes
+ * is also desirable to revalidate an item before use.
+ *
+ * If we are unable to revalidate the data, we return the old data and
+ * do not set the BLKID_BID_FL_VERIFIED flag on it.
+ */
+blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev)
+{
+	struct stat st;
+	time_t diff, now;
+	char *fltr[2];
+	int fd;
+
+	if (!dev)
+		return NULL;
+
+	now = time(0);
+	diff = now - dev->bid_time;
+
+	if (stat(dev->bid_name, &st) < 0) {
+		DBG(DEBUG_PROBE,
+		    printf("blkid_verify: error %m (%d) while "
+			   "trying to stat %s\n", errno,
+			   dev->bid_name));
+	open_err:
+		if ((errno == EPERM) || (errno == EACCES) || (errno == ENOENT)) {
+			/* We don't have read permission, just return cache data. */
+			DBG(DEBUG_PROBE, printf("returning unverified data for %s\n",
+						dev->bid_name));
+			return dev;
+		}
+		blkid_free_dev(dev);
+		return NULL;
+	}
+
+	if (now >= dev->bid_time &&
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+	    (st.st_mtime < dev->bid_time ||
+	        (st.st_mtime == dev->bid_time &&
+		 st.st_mtim.tv_nsec / 1000 <= dev->bid_utime)) &&
+#else
+	    st.st_mtime <= dev->bid_time &&
+#endif
+	    (diff < BLKID_PROBE_MIN ||
+		(dev->bid_flags & BLKID_BID_FL_VERIFIED &&
+		 diff < BLKID_PROBE_INTERVAL)))
+		return dev;
+
+#ifndef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+	DBG(DEBUG_PROBE,
+	    printf("need to revalidate %s (cache time %lu, stat time %lu,\n\t"
+		   "time since last check %lu)\n",
+		   dev->bid_name, (unsigned long)dev->bid_time,
+		   (unsigned long)st.st_mtime, (unsigned long)diff));
+#else
+	DBG(DEBUG_PROBE,
+	    printf("need to revalidate %s (cache time %lu.%lu, stat time %lu.%lu,\n\t"
+		   "time since last check %lu)\n",
+		   dev->bid_name,
+		   (unsigned long)dev->bid_time, (unsigned long)dev->bid_utime,
+		   (unsigned long)st.st_mtime, (unsigned long)st.st_mtim.tv_nsec / 1000,
+		   (unsigned long)diff));
+#endif
+
+	if (!cache->probe) {
+		cache->probe = blkid_new_probe();
+		if (!cache->probe) {
+			blkid_free_dev(dev);
+			return NULL;
+		}
+	}
+
+	fd = open(dev->bid_name, O_RDONLY);
+	if (fd < 0) {
+		DBG(DEBUG_PROBE, printf("blkid_verify: error %m (%d) while "
+					"opening %s\n", errno,
+					dev->bid_name));
+		goto open_err;
+	}
+
+	if (blkid_probe_set_device(cache->probe, fd, 0, 0)) {
+		/* failed to read the device */
+		close(fd);
+		blkid_free_dev(dev);
+		return NULL;
+	}
+
+	blkid_probe_enable_superblocks(cache->probe, TRUE);
+
+	blkid_probe_set_superblocks_flags(cache->probe,
+		BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
+		BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE);
+
+	/*
+	 * If we already know the type, then try that first.
+	 */
+	if (dev->bid_type) {
+		blkid_tag_iterate iter;
+		const char *type, *value;
+
+		fltr[0] = dev->bid_type;
+		fltr[1] = NULL;
+
+		blkid_probe_filter_superblocks_type(cache->probe,
+				BLKID_FLTR_ONLYIN, fltr);
+
+		if (blkid_do_probe(cache->probe) == 0) {
+			/*
+			 * Cool, we found FS type, let's also read PART{UUID,LABEL}
+			 */
+			blkid_probe_enable_superblocks(cache->probe, FALSE);
+			blkid_probe_enable_partitions(cache->probe, TRUE);
+			blkid_probe_set_partitions_flags(cache->probe, BLKID_PARTS_ENTRY_DETAILS);
+			if (blkid_do_probe(cache->probe) == 0)
+				goto found_type;
+		}
+
+		blkid_probe_enable_superblocks(cache->probe, TRUE);
+		blkid_probe_invert_superblocks_filter(cache->probe);
+
+		/*
+		 * Zap the device filesystem information and try again
+		 */
+		DBG(DEBUG_PROBE,
+		    printf("previous fs type %s not valid, "
+			   "trying full probe\n", dev->bid_type));
+		iter = blkid_tag_iterate_begin(dev);
+		while (blkid_tag_next(iter, &type, &value) == 0)
+			blkid_set_tag(dev, type, 0, 0);
+		blkid_tag_iterate_end(iter);
+	}
+
+	blkid_probe_enable_partitions(cache->probe, TRUE);
+	blkid_probe_set_partitions_flags(cache->probe, BLKID_PARTS_ENTRY_DETAILS);
+
+	/*
+	 * Probe for all types.
+	 */
+	if (blkid_do_safeprobe(cache->probe)) {
+		/* found nothing or error */
+		blkid_free_dev(dev);
+		dev = NULL;
+	}
+
+found_type:
+	if (dev) {
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+		struct timeval tv;
+		if (!gettimeofday(&tv, NULL)) {
+			dev->bid_time = tv.tv_sec;
+			dev->bid_utime = tv.tv_usec;
+		} else
+#endif
+			dev->bid_time = time(0);
+
+		dev->bid_devno = st.st_rdev;
+		dev->bid_flags |= BLKID_BID_FL_VERIFIED;
+		cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+
+		blkid_probe_to_tags(cache->probe, dev);
+
+		DBG(DEBUG_PROBE, printf("%s: devno 0x%04llx, type %s\n",
+			   dev->bid_name, (long long)st.st_rdev, dev->bid_type));
+	}
+
+	blkid_reset_probe(cache->probe);
+	blkid_probe_reset_superblocks_filter(cache->probe);
+	close(fd);
+	return dev;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	blkid_dev dev;
+	blkid_cache cache;
+	int ret;
+
+	if (argc != 2) {
+		fprintf(stderr, "Usage: %s device\n"
+			"Probe a single device to determine type\n", argv[0]);
+		exit(1);
+	}
+	if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
+		fprintf(stderr, "%s: error creating cache (%d)\n",
+			argv[0], ret);
+		exit(1);
+	}
+	dev = blkid_get_dev(cache, argv[1], BLKID_DEV_NORMAL);
+	if (!dev) {
+		printf("%s: %s has an unsupported type\n", argv[0], argv[1]);
+		return (1);
+	}
+	printf("TYPE='%s'\n", dev->bid_type ? dev->bid_type : "(null)");
+	if (dev->bid_label)
+		printf("LABEL='%s'\n", dev->bid_label);
+	if (dev->bid_uuid)
+		printf("UUID='%s'\n", dev->bid_uuid);
+
+	blkid_free_dev(dev);
+	return (0);
+}
+#endif
diff --git a/src/version.c b/src/version.c
new file mode 100644
index 0000000..7f77631
--- /dev/null
+++ b/src/version.c
@@ -0,0 +1,62 @@
+/*
+ * version.c --- Return the version of the blkid library
+ *
+ * Copyright (C) 2004 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Lesser General
+ * Public License.
+ * %End-Header%
+ */
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "blkid.h"
+
+/* LIBBLKID_* defined in the global config.h */
+static const char *lib_version = LIBBLKID_VERSION;	/* release version */
+static const char *lib_date = LIBBLKID_DATE;
+
+/**
+ * blkid_parse_version_string:
+ * @ver_string:  version string (e.g. "2.16.0")
+ *
+ * Returns: release version code.
+ */
+int blkid_parse_version_string(const char *ver_string)
+{
+	const char *cp;
+	int version = 0;
+
+	for (cp = ver_string; *cp; cp++) {
+		if (*cp == '.')
+			continue;
+		if (!isdigit(*cp))
+			break;
+		version = (version * 10) + (*cp - '0');
+	}
+	return version;
+}
+
+/**
+ * blkid_get_library_version:
+ * @ver_string: returns relese version (!= SONAME version)
+ * @date_string: returns date
+ *
+ * Returns: release version code.
+ */
+int blkid_get_library_version(const char **ver_string,
+			       const char **date_string)
+{
+	if (ver_string)
+		*ver_string = lib_version;
+	if (date_string)
+		*date_string = lib_date;
+
+	return blkid_parse_version_string(lib_version);
+}
diff --git a/usr/lib/libblkid.so.1 b/usr/lib/libblkid.so.1
deleted file mode 120000
index e4f69c7..0000000
--- a/usr/lib/libblkid.so.1
+++ /dev/null
@@ -1 +0,0 @@
-libblkid.so.1.1.0
\ No newline at end of file
diff --git a/usr/lib/libblkid.so.1.1.0 b/usr/lib/libblkid.so.1.1.0
deleted file mode 100755
index 9c08aab..0000000
--- a/usr/lib/libblkid.so.1.1.0
+++ /dev/null
Binary files differ