summaryrefslogtreecommitdiffstats
path: root/libblkid/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:10:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 19:10:49 +0000
commitcfe5e3905201349e9cf3f95d52ff4bd100bde37d (patch)
treed0baf160cbee3195249d095f85e52d20c21acf02 /libblkid/src
parentInitial commit. (diff)
downloadutil-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.tar.xz
util-linux-cfe5e3905201349e9cf3f95d52ff4bd100bde37d.zip
Adding upstream version 2.39.3.upstream/2.39.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libblkid/src')
-rw-r--r--libblkid/src/Makemodule.am253
-rw-r--r--libblkid/src/blkid.h.in499
-rw-r--r--libblkid/src/blkidP.h570
-rw-r--r--libblkid/src/cache.c223
-rw-r--r--libblkid/src/config.c202
-rw-r--r--libblkid/src/dev.c279
-rw-r--r--libblkid/src/devname.c647
-rw-r--r--libblkid/src/devno.c368
-rw-r--r--libblkid/src/encode.c263
-rw-r--r--libblkid/src/evaluate.c325
-rw-r--r--libblkid/src/fuzz.c52
-rw-r--r--libblkid/src/getsize.c34
-rw-r--r--libblkid/src/init.c72
-rw-r--r--libblkid/src/libblkid.sym189
-rw-r--r--libblkid/src/partitions/aix.c57
-rw-r--r--libblkid/src/partitions/aix.h7
-rw-r--r--libblkid/src/partitions/atari.c312
-rw-r--r--libblkid/src/partitions/bsd.c205
-rw-r--r--libblkid/src/partitions/dos.c375
-rw-r--r--libblkid/src/partitions/gpt.c473
-rw-r--r--libblkid/src/partitions/mac.c200
-rw-r--r--libblkid/src/partitions/minix.c102
-rw-r--r--libblkid/src/partitions/partitions.c1532
-rw-r--r--libblkid/src/partitions/partitions.h74
-rw-r--r--libblkid/src/partitions/sgi.c87
-rw-r--r--libblkid/src/partitions/solaris_x86.c154
-rw-r--r--libblkid/src/partitions/sun.c125
-rw-r--r--libblkid/src/partitions/ultrix.c99
-rw-r--r--libblkid/src/partitions/unixware.c197
-rw-r--r--libblkid/src/probe.c2413
-rw-r--r--libblkid/src/read.c455
-rw-r--r--libblkid/src/resolve.c129
-rw-r--r--libblkid/src/save.c244
-rw-r--r--libblkid/src/superblocks/adaptec_raid.c116
-rw-r--r--libblkid/src/superblocks/apfs.c84
-rw-r--r--libblkid/src/superblocks/bcache.c425
-rw-r--r--libblkid/src/superblocks/befs.c550
-rw-r--r--libblkid/src/superblocks/bfs.c23
-rw-r--r--libblkid/src/superblocks/bitlocker.c191
-rw-r--r--libblkid/src/superblocks/bluestore.c53
-rw-r--r--libblkid/src/superblocks/btrfs.c326
-rw-r--r--libblkid/src/superblocks/cramfs.c112
-rw-r--r--libblkid/src/superblocks/cs_fvault2.c104
-rw-r--r--libblkid/src/superblocks/ddf_raid.c141
-rw-r--r--libblkid/src/superblocks/drbd.c231
-rw-r--r--libblkid/src/superblocks/drbdmanage.c87
-rw-r--r--libblkid/src/superblocks/drbdproxy_datalog.c55
-rw-r--r--libblkid/src/superblocks/erofs.c109
-rw-r--r--libblkid/src/superblocks/exfat.c259
-rw-r--r--libblkid/src/superblocks/exfs.c192
-rw-r--r--libblkid/src/superblocks/ext.c395
-rw-r--r--libblkid/src/superblocks/f2fs.c136
-rw-r--r--libblkid/src/superblocks/gfs.c141
-rw-r--r--libblkid/src/superblocks/hfs.c341
-rw-r--r--libblkid/src/superblocks/highpoint_raid.c87
-rw-r--r--libblkid/src/superblocks/hpfs.c124
-rw-r--r--libblkid/src/superblocks/iso9660.c348
-rw-r--r--libblkid/src/superblocks/isw_raid.c67
-rw-r--r--libblkid/src/superblocks/jfs.c75
-rw-r--r--libblkid/src/superblocks/jmicron_raid.c114
-rw-r--r--libblkid/src/superblocks/linux_raid.c295
-rw-r--r--libblkid/src/superblocks/lsi_raid.c60
-rw-r--r--libblkid/src/superblocks/luks.c134
-rw-r--r--libblkid/src/superblocks/lvm.c264
-rw-r--r--libblkid/src/superblocks/minix.c190
-rw-r--r--libblkid/src/superblocks/mpool.c62
-rw-r--r--libblkid/src/superblocks/netware.c97
-rw-r--r--libblkid/src/superblocks/nilfs.c176
-rw-r--r--libblkid/src/superblocks/ntfs.c260
-rw-r--r--libblkid/src/superblocks/nvidia_raid.c78
-rw-r--r--libblkid/src/superblocks/ocfs.c220
-rw-r--r--libblkid/src/superblocks/promise_raid.c77
-rw-r--r--libblkid/src/superblocks/refs.c26
-rw-r--r--libblkid/src/superblocks/reiserfs.c138
-rw-r--r--libblkid/src/superblocks/romfs.c84
-rw-r--r--libblkid/src/superblocks/silicon_raid.c134
-rw-r--r--libblkid/src/superblocks/squashfs.c121
-rw-r--r--libblkid/src/superblocks/stratis.c127
-rw-r--r--libblkid/src/superblocks/superblocks.c951
-rw-r--r--libblkid/src/superblocks/superblocks.h136
-rw-r--r--libblkid/src/superblocks/swap.c196
-rw-r--r--libblkid/src/superblocks/sysv.c154
-rw-r--r--libblkid/src/superblocks/ubi.c63
-rw-r--r--libblkid/src/superblocks/ubifs.c137
-rw-r--r--libblkid/src/superblocks/udf.c596
-rw-r--r--libblkid/src/superblocks/ufs.c269
-rw-r--r--libblkid/src/superblocks/vdo.c48
-rw-r--r--libblkid/src/superblocks/vfat.c462
-rw-r--r--libblkid/src/superblocks/via_raid.c91
-rw-r--r--libblkid/src/superblocks/vmfs.c101
-rw-r--r--libblkid/src/superblocks/vxfs.c61
-rw-r--r--libblkid/src/superblocks/xfs.c370
-rw-r--r--libblkid/src/superblocks/zfs.c333
-rw-r--r--libblkid/src/superblocks/zonefs.c101
-rw-r--r--libblkid/src/tag.c468
-rw-r--r--libblkid/src/topology/dm.c134
-rw-r--r--libblkid/src/topology/evms.c77
-rw-r--r--libblkid/src/topology/ioctl.c83
-rw-r--r--libblkid/src/topology/lvm.c144
-rw-r--r--libblkid/src/topology/md.c154
-rw-r--r--libblkid/src/topology/sysfs.c132
-rw-r--r--libblkid/src/topology/topology.c423
-rw-r--r--libblkid/src/topology/topology.h26
-rw-r--r--libblkid/src/verify.c226
-rw-r--r--libblkid/src/version.c62
105 files changed, 24643 insertions, 0 deletions
diff --git a/libblkid/src/Makemodule.am b/libblkid/src/Makemodule.am
new file mode 100644
index 0000000..bc90eb8
--- /dev/null
+++ b/libblkid/src/Makemodule.am
@@ -0,0 +1,253 @@
+
+# blkid.h is generated, so it's store in builddir!
+blkidincdir = $(includedir)/blkid
+nodist_blkidinc_HEADERS = libblkid/src/blkid.h
+
+usrlib_exec_LTLIBRARIES += libblkid.la
+libblkid_la_SOURCES = \
+ include/list.h \
+ \
+ libblkid/src/blkidP.h \
+ libblkid/src/init.c \
+ 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/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 \
+ \
+ libblkid/src/partitions/aix.c \
+ libblkid/src/partitions/aix.h \
+ libblkid/src/partitions/atari.c \
+ libblkid/src/partitions/bsd.c \
+ libblkid/src/partitions/dos.c \
+ 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/apfs.c \
+ libblkid/src/superblocks/bcache.c \
+ libblkid/src/superblocks/befs.c \
+ libblkid/src/superblocks/bfs.c \
+ libblkid/src/superblocks/bitlocker.c \
+ libblkid/src/superblocks/bluestore.c \
+ libblkid/src/superblocks/btrfs.c \
+ libblkid/src/superblocks/cs_fvault2.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/drbdmanage.c \
+ libblkid/src/superblocks/exfat.c \
+ libblkid/src/superblocks/exfs.c \
+ libblkid/src/superblocks/ext.c \
+ libblkid/src/superblocks/f2fs.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/mpool.c \
+ libblkid/src/superblocks/netware.c \
+ libblkid/src/superblocks/nilfs.c \
+ libblkid/src/superblocks/ntfs.c \
+ libblkid/src/superblocks/refs.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/stratis.c \
+ libblkid/src/superblocks/superblocks.c \
+ libblkid/src/superblocks/superblocks.h \
+ libblkid/src/superblocks/swap.c \
+ libblkid/src/superblocks/sysv.c \
+ libblkid/src/superblocks/ubi.c \
+ libblkid/src/superblocks/ubifs.c \
+ libblkid/src/superblocks/udf.c \
+ libblkid/src/superblocks/ufs.c \
+ libblkid/src/superblocks/vdo.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/superblocks/zonefs.c \
+ libblkid/src/superblocks/erofs.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
+
+libblkid_la_LIBADD = libcommon.la
+
+EXTRA_libblkid_la_DEPENDENCIES = \
+ libblkid/src/libblkid.sym
+
+libblkid_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(SOLIB_CFLAGS) \
+ -I$(ul_libblkid_incdir) \
+ -I$(top_srcdir)/libblkid/src
+
+libblkid_la_LDFLAGS = $(SOLIB_LDFLAGS)
+if HAVE_VSCRIPT
+libblkid_la_LDFLAGS += $(VSCRIPT_LDFLAGS),$(top_srcdir)/libblkid/src/libblkid.sym
+endif
+libblkid_la_LDFLAGS += -version-info $(LIBBLKID_VERSION_INFO)
+
+EXTRA_DIST += \
+ libblkid/src/libblkid.sym
+
+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 = $(LDADD) libblkid.la
+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)
+
+if FUZZING_ENGINE
+check_PROGRAMS += test_blkid_fuzz
+
+test_blkid_fuzz_SOURCES = libblkid/src/fuzz.c
+
+# https://google.github.io/oss-fuzz/getting-started/new-project-guide/#Requirements
+nodist_EXTRA_test_blkid_fuzz_SOURCES = dummy.cxx
+
+test_blkid_fuzz_CFLAGS = $(blkid_tests_cflags) -DFUZZ_TARGET
+test_blkid_fuzz_LDFLAGS = $(blkid_tests_ldflags) -lpthread
+test_blkid_fuzz_LDADD = $(blkid_tests_ldadd) $(LIB_FUZZING_ENGINE)
+endif
+
+check_PROGRAMS += test_blkid_fuzz_sample
+
+test_blkid_fuzz_sample_SOURCES = libblkid/src/fuzz.c
+
+test_blkid_fuzz_sample_CFLAGS = $(blkid_tests_cflags)
+test_blkid_fuzz_sample_LDFLAGS = $(blkid_tests_ldflags)
+test_blkid_fuzz_sample_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)" -a -f "$(DESTDIR)$(usrlib_execdir)/libblkid.so"; 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/libblkid/src/blkid.h.in b/libblkid/src/blkid.h.in
new file mode 100644
index 0000000..c232d72
--- /dev/null
+++ b/libblkid/src/blkid.h.in
@@ -0,0 +1,499 @@
+/*
+ * 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)
+
+
+#ifndef __GNUC_PREREQ
+# if defined __GNUC__ && defined __GNUC_MINOR__
+# define __GNUC_PREREQ(maj, min) ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+# else
+# define __GNUC_PREREQ(maj, min) 0
+# endif
+#endif
+
+#ifndef __ul_attribute__
+# if __GNUC_PREREQ (3, 4)
+# define __ul_attribute__(_a_) __attribute__(_a_)
+# else
+# define __ul_attribute__(_a_)
+# endif
+#endif
+
+/* init.c */
+extern void blkid_init_debug(int mask);
+
+/* 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)
+ __ul_attribute__((warn_unused_result));
+
+extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache);
+extern int blkid_dev_set_search(blkid_dev_iterate iter,
+ const char *search_type, const 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)
+ __ul_attribute__((warn_unused_result));
+extern int blkid_devno_to_wholedisk(dev_t dev, char *diskname,
+ size_t len, dev_t *diskdevno)
+ __ul_attribute__((warn_unused_result));
+
+/* 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)
+ __ul_attribute__((warn_unused_result));
+extern char *blkid_get_devname(blkid_cache cache, const char *token,
+ const char *value)
+ __ul_attribute__((warn_unused_result));
+
+/* 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)
+ __ul_attribute__((nonnull));
+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)
+ __ul_attribute__((warn_unused_result));
+extern char *blkid_evaluate_spec(const char *spec, blkid_cache *cache)
+ __ul_attribute__((warn_unused_result));
+
+/* probe.c */
+extern blkid_probe blkid_new_probe(void)
+ __ul_attribute__((warn_unused_result));
+extern blkid_probe blkid_new_probe_from_filename(const char *filename)
+ __ul_attribute__((warn_unused_result))
+ __ul_attribute__((nonnull));
+extern void blkid_free_probe(blkid_probe pr);
+
+extern void blkid_reset_probe(blkid_probe pr);
+extern int blkid_probe_reset_buffers(blkid_probe pr);
+extern int blkid_probe_hide_range(blkid_probe pr, uint64_t off, uint64_t len);
+
+extern int blkid_probe_set_device(blkid_probe pr, int fd,
+ blkid_loff_t off, blkid_loff_t size)
+ __ul_attribute__((nonnull));
+
+extern dev_t blkid_probe_get_devno(blkid_probe pr)
+ __ul_attribute__((nonnull));
+
+extern dev_t blkid_probe_get_wholedisk_devno(blkid_probe pr)
+ __ul_attribute__((nonnull));
+
+extern int blkid_probe_is_wholedisk(blkid_probe pr)
+ __ul_attribute__((nonnull));
+
+extern blkid_loff_t blkid_probe_get_size(blkid_probe pr)
+ __ul_attribute__((nonnull));
+extern blkid_loff_t blkid_probe_get_offset(blkid_probe pr)
+ __ul_attribute__((nonnull));
+extern unsigned int blkid_probe_get_sectorsize(blkid_probe pr)
+ __ul_attribute__((nonnull));
+extern int blkid_probe_set_sectorsize(blkid_probe pr, unsigned int sz)
+ __ul_attribute__((nonnull));
+extern blkid_loff_t blkid_probe_get_sectors(blkid_probe pr)
+ __ul_attribute__((nonnull));
+
+extern int blkid_probe_get_fd(blkid_probe pr)
+ __ul_attribute__((nonnull));
+
+extern int blkid_probe_set_hint(blkid_probe pr, const char *name, uint64_t value)
+ __ul_attribute__((nonnull));
+extern void blkid_probe_reset_hints(blkid_probe pr)
+ __ul_attribute__((nonnull));
+
+/*
+ * superblocks probing
+ */
+extern int blkid_known_fstype(const char *fstype)
+ __ul_attribute__((nonnull));
+
+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)
+ __ul_attribute__((nonnull));
+
+#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_BADCSUM (1 << 10) /* allow a bad checksum */
+#define BLKID_SUBLKS_FSINFO (1 << 11) /* read and define fs properties from superblock */
+
+#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)
+ __ul_attribute__((nonnull));
+extern int blkid_probe_reset_superblocks_filter(blkid_probe pr)
+ __ul_attribute__((nonnull));
+extern int blkid_probe_invert_superblocks_filter(blkid_probe pr)
+ __ul_attribute__((nonnull));
+
+/**
+ * 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[])
+ __ul_attribute__((nonnull));
+
+#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)
+ __ul_attribute__((nonnull));
+
+/*
+ * topology probing
+ */
+extern int blkid_probe_enable_topology(blkid_probe pr, int enable)
+ __ul_attribute__((nonnull));
+
+/* binary interface */
+extern blkid_topology blkid_probe_get_topology(blkid_probe pr)
+ __ul_attribute__((nonnull));
+
+extern unsigned long blkid_topology_get_alignment_offset(blkid_topology tp)
+ __ul_attribute__((nonnull));
+extern unsigned long blkid_topology_get_minimum_io_size(blkid_topology tp)
+ __ul_attribute__((nonnull));
+extern unsigned long blkid_topology_get_optimal_io_size(blkid_topology tp)
+ __ul_attribute__((nonnull));
+extern unsigned long blkid_topology_get_logical_sector_size(blkid_topology tp)
+ __ul_attribute__((nonnull));
+extern unsigned long blkid_topology_get_physical_sector_size(blkid_topology tp)
+ __ul_attribute__((nonnull));
+extern unsigned long blkid_topology_get_dax(blkid_topology tp)
+ __ul_attribute__((nonnull));
+extern uint64_t blkid_topology_get_diskseq(blkid_topology tp)
+ __ul_attribute__((nonnull));
+
+/*
+ * partitions probing
+ */
+extern int blkid_known_pttype(const char *pttype);
+extern int blkid_partitions_get_name(const size_t idx, const char **name);
+
+extern int blkid_probe_enable_partitions(blkid_probe pr, int enable)
+ __ul_attribute__((nonnull));
+
+extern int blkid_probe_reset_partitions_filter(blkid_probe pr)
+ __ul_attribute__((nonnull));
+extern int blkid_probe_invert_partitions_filter(blkid_probe pr)
+ __ul_attribute__((nonnull));
+extern int blkid_probe_filter_partitions_type(blkid_probe pr, int flag, char *names[])
+ __ul_attribute__((nonnull));
+
+/* 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)
+ __ul_attribute__((nonnull));
+
+/* binary interface */
+extern blkid_partlist blkid_probe_get_partitions(blkid_probe pr)
+ __ul_attribute__((nonnull));
+
+extern int blkid_partlist_numof_partitions(blkid_partlist ls)
+ __ul_attribute__((nonnull));
+extern blkid_parttable blkid_partlist_get_table(blkid_partlist ls)
+ __ul_attribute__((nonnull));
+extern blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n)
+ __ul_attribute__((nonnull));
+extern blkid_partition blkid_partlist_get_partition_by_partno(blkid_partlist ls, int n)
+ __ul_attribute__((nonnull));
+extern blkid_partition blkid_partlist_devno_to_partition(blkid_partlist ls, dev_t devno)
+ __ul_attribute__((nonnull));
+extern blkid_parttable blkid_partition_get_table(blkid_partition par)
+ __ul_attribute__((nonnull));
+
+extern const char *blkid_partition_get_name(blkid_partition par)
+ __ul_attribute__((nonnull));
+extern const char *blkid_partition_get_uuid(blkid_partition par)
+ __ul_attribute__((nonnull));
+extern int blkid_partition_get_partno(blkid_partition par)
+ __ul_attribute__((nonnull));
+extern blkid_loff_t blkid_partition_get_start(blkid_partition par)
+ __ul_attribute__((nonnull));
+extern blkid_loff_t blkid_partition_get_size(blkid_partition par)
+ __ul_attribute__((nonnull));
+
+extern int blkid_partition_get_type(blkid_partition par)
+ __ul_attribute__((nonnull));
+extern const char *blkid_partition_get_type_string(blkid_partition par)
+ __ul_attribute__((nonnull));
+extern unsigned long long blkid_partition_get_flags(blkid_partition par)
+ __ul_attribute__((nonnull));
+
+extern int blkid_partition_is_logical(blkid_partition par)
+ __ul_attribute__((nonnull));
+extern int blkid_partition_is_extended(blkid_partition par)
+ __ul_attribute__((nonnull));
+extern int blkid_partition_is_primary(blkid_partition par)
+ __ul_attribute__((nonnull));
+
+extern const char *blkid_parttable_get_type(blkid_parttable tab)
+ __ul_attribute__((nonnull));
+extern const char *blkid_parttable_get_id(blkid_parttable tab)
+ __ul_attribute__((nonnull));
+extern blkid_loff_t blkid_parttable_get_offset(blkid_parttable tab)
+ __ul_attribute__((nonnull));
+extern blkid_partition blkid_parttable_get_parent(blkid_parttable tab)
+ __ul_attribute__((nonnull));
+
+/*
+ * NAME=value low-level interface
+ */
+extern int blkid_do_probe(blkid_probe pr)
+ __ul_attribute__((nonnull));
+extern int blkid_do_safeprobe(blkid_probe pr)
+ __ul_attribute__((nonnull));
+extern int blkid_do_fullprobe(blkid_probe pr)
+ __ul_attribute__((nonnull));
+
+/**
+ * BLKID_PROBE_OK:
+ *
+ * probing return value; superblock (RAID, partiton table, ...) succesfully detected
+ */
+#define BLKID_PROBE_OK 0
+/**
+ * BLKID_PROBE_NONE:
+ *
+ * probing return value; found nothing
+ */
+#define BLKID_PROBE_NONE 1
+/**
+ * BLKID_PROBE_ERROR:
+ *
+ * probing return value; probing ends with en error (see errno for more details)
+ */
+#define BLKID_PROBE_ERROR -1
+/**
+ * BLKID_PROBE_AMBIGUOUS:
+ *
+ * probing return value; more than one probing result, in this case, it's not
+ * safe to use the device automaticaly and user intervention is recommended
+ */
+#define BLKID_PROBE_AMBIGUOUS -2
+
+extern int blkid_probe_numof_values(blkid_probe pr)
+ __ul_attribute__((nonnull));
+extern int blkid_probe_get_value(blkid_probe pr, int num, const char **name,
+ const char **data, size_t *len)
+ __ul_attribute__((nonnull(1)));
+extern int blkid_probe_lookup_value(blkid_probe pr, const char *name,
+ const char **data, size_t *len)
+ __ul_attribute__((nonnull(1, 2)));
+extern int blkid_probe_has_value(blkid_probe pr, const char *name)
+ __ul_attribute__((nonnull));
+extern int blkid_do_wipe(blkid_probe pr, int dryrun)
+ __ul_attribute__((nonnull));
+extern int blkid_probe_step_back(blkid_probe pr)
+ __ul_attribute__((nonnull));
+
+/*
+ * 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)
+ __ul_attribute__((deprecated));
+
+extern int blkid_probe_filter_usage(blkid_probe pr, int flag, int usage)
+ __ul_attribute__((deprecated));
+
+extern int blkid_probe_filter_types(blkid_probe pr, int flag, char *names[])
+ __ul_attribute__((deprecated));
+
+extern int blkid_probe_invert_filter(blkid_probe pr)
+ __ul_attribute__((deprecated));
+
+extern int blkid_probe_reset_filter(blkid_probe pr)
+ __ul_attribute__((deprecated));
+
+#endif /* BLKID_DISABLE_DEPRECATED */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BLKID_BLKID_H */
diff --git a/libblkid/src/blkidP.h b/libblkid/src/blkidP.h
new file mode 100644
index 0000000..007cc35
--- /dev/null
+++ b/libblkid/src/blkidP.h
@@ -0,0 +1,570 @@
+/*
+ * 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
+
+/* 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>
+
+#ifndef UUID_STR_LEN
+# define UUID_STR_LEN 37
+#endif
+
+#include "c.h"
+#include "bitops.h" /* $(top_srcdir)/include/ */
+#include "blkdev.h"
+
+#include "debug.h"
+#include "blkid.h"
+#include "list.h"
+#include "encode.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 real path (as used in cache) */
+ char *bid_xname; /* Device path as used by application (maybe symlink..) */
+ 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 *);
+};
+
+/* chains */
+extern const struct blkid_chaindrv superblocks_drv;
+extern const struct blkid_chaindrv topology_drv;
+extern const struct blkid_chaindrv partitions_drv;
+
+/*
+ * Low-level probe result
+ */
+struct blkid_prval
+{
+ const char *name; /* value name */
+ unsigned char *data; /* value data */
+ size_t len; /* length of value data */
+
+ struct blkid_chain *chain; /* owner */
+ struct list_head prvals; /* list of results */
+};
+
+/*
+ * Filesystem / Raid magic strings
+ */
+struct blkid_idmag
+{
+ const char *magic; /* magic string */
+ unsigned int len; /* length of magic */
+
+ const char *hoff; /* hint which contains byte offset to kboff */
+ long kboff; /* kilobyte offset of superblock */
+ unsigned int sboff; /* byte offset within superblock */
+
+ int is_zoned; /* indicate magic location is calculated based on zone position */
+ long zonenum; /* zone number which has superblock */
+ long kboff_inzone; /* kilobyte offset of superblock in a zone */
+};
+
+/*
+ * 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;
+ uint64_t off;
+ uint64_t len;
+ struct list_head bufs; /* list of buffers */
+};
+
+/*
+ * Probing hint
+ */
+struct blkid_hint {
+ char *name;
+ uint64_t value;
+ struct list_head hints;
+};
+
+/*
+ * Low-level probing control struct
+ */
+struct blkid_struct_probe
+{
+ int fd; /* device file descriptor */
+ uint64_t off; /* begin of data on the device */
+ uint64_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 */
+ uint64_t zone_size; /* zone size (BLKGETZONESZ ioctl) */
+
+ int flags; /* private library flags */
+ int prob_flags; /* always zeroized by blkid_do_*() */
+
+ uint64_t wipe_off; /* begin of the wiped area */
+ uint64_t wipe_size; /* size of the wiped area */
+ struct blkid_chain *wipe_chain; /* superblock, partition, ... */
+
+ struct list_head buffers; /* list of buffers */
+ struct list_head hints;
+
+ struct blkid_chain chains[BLKID_NCHAINS]; /* array of chains */
+ struct blkid_chain *cur_chain; /* current chain */
+
+ struct list_head values; /* results */
+
+ 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 */
+#define BLKID_FL_NOSCAN_DEV (1 << 4) /* do not scan this device */
+#define BLKID_FL_MODIF_BUFF (1 << 5) /* cached buffers has been modified */
+#define BLKID_FL_OPAL_LOCKED (1 << 6) /* OPAL device is locked (I/O errors) */
+
+/* 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)
+ __ul_attribute__((warn_unused_result));
+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 */
+
+/* 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_SYSFS 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
+
+#define BLKID_DEBUG_HELP (1 << 0)
+#define BLKID_DEBUG_INIT (1 << 1)
+#define BLKID_DEBUG_CACHE (1 << 2)
+#define BLKID_DEBUG_CONFIG (1 << 3)
+#define BLKID_DEBUG_DEV (1 << 4)
+#define BLKID_DEBUG_DEVNAME (1 << 5)
+#define BLKID_DEBUG_DEVNO (1 << 6)
+#define BLKID_DEBUG_EVALUATE (1 << 7)
+#define BLKID_DEBUG_LOWPROBE (1 << 8)
+#define BLKID_DEBUG_PROBE (1 << 9)
+#define BLKID_DEBUG_READ (1 << 10)
+#define BLKID_DEBUG_SAVE (1 << 11)
+#define BLKID_DEBUG_TAG (1 << 12)
+#define BLKID_DEBUG_BUFFER (1 << 13)
+#define BLKID_DEBUG_ALL 0xFFFF /* (1 << 16) aka FFFF is expected by API */
+
+UL_DEBUG_DECLARE_MASK(libblkid);
+#define DBG(m, x) __UL_DBG(libblkid, BLKID_DEBUG_, m, x)
+#define ON_DBG(m, x) __UL_DBG_CALL(libblkid, BLKID_DEBUG_, m, x)
+
+#define UL_DEBUG_CURRENT_MASK UL_DEBUG_MASK(libblkid)
+#include "debugobj.h"
+
+extern void blkid_debug_dump_dev(blkid_dev dev);
+
+
+/* devno.c */
+struct dir_list {
+ char *name;
+ struct dir_list *next;
+};
+extern void blkid__scan_dir(char *, dev_t, struct dir_list **, char **)
+ __attribute__((nonnull(1,4)));
+extern int blkid_driver_has_major(const char *drvname, int drvmaj)
+ __attribute__((warn_unused_result));
+
+/* read.c */
+extern void blkid_read_cache(blkid_cache cache)
+ __attribute__((nonnull));
+
+/* save.c */
+extern int blkid_flush_cache(blkid_cache cache)
+ __attribute__((nonnull));
+
+/* cache */
+extern char *blkid_safe_getenv(const char *arg)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+
+extern char *blkid_get_cache_filename(struct blkid_config *conf)
+ __attribute__((warn_unused_result));
+/*
+ * 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)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+
+extern int blkid_set_tag(blkid_dev dev, const char *name,
+ const char *value, const int vlength)
+ __attribute__((nonnull(1,2)));
+
+/*
+ * Functions to create and find a specific tag type: dev.c
+ */
+extern blkid_dev blkid_new_dev(void)
+ __attribute__((warn_unused_result));
+extern void blkid_free_dev(blkid_dev dev);
+
+/* probe.c */
+extern int blkid_probe_is_tiny(blkid_probe pr)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+extern int blkid_probe_is_cdrom(blkid_probe pr)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+extern int blkdid_probe_is_opal_locked(blkid_probe pr)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+
+extern unsigned char *blkid_probe_get_buffer(blkid_probe pr,
+ uint64_t off, uint64_t len)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+
+extern unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+
+extern int blkid_probe_get_dimension(blkid_probe pr,
+ uint64_t *off, uint64_t *size)
+ __attribute__((nonnull));
+
+extern int blkid_probe_set_dimension(blkid_probe pr,
+ uint64_t off, uint64_t size)
+ __attribute__((nonnull));
+
+extern int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
+ uint64_t *offset, const struct blkid_idmag **res)
+ __attribute__((nonnull(1)));
+
+/* returns superblock according to 'struct blkid_idmag' */
+extern unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size);
+#define blkid_probe_get_sb(_pr, _mag, type) \
+ ((type *) blkid_probe_get_sb_buffer((_pr), _mag, sizeof(type)))
+
+extern blkid_partlist blkid_probe_get_partlist(blkid_probe pr)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+
+extern int blkid_probe_is_covered_by_pt(blkid_probe pr,
+ uint64_t offset, uint64_t size)
+ __attribute__((warn_unused_result));
+
+extern void blkid_probe_chain_reset_values(blkid_probe pr, struct blkid_chain *chn)
+ __attribute__((nonnull));
+extern int blkid_probe_chain_save_values(blkid_probe pr,
+ struct blkid_chain *chn,
+ struct list_head *vals)
+ __attribute__((nonnull));
+
+extern struct blkid_prval *blkid_probe_assign_value(blkid_probe pr,
+ const char *name)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+
+extern void blkid_probe_free_value(struct blkid_prval *v);
+
+
+extern void blkid_probe_append_values_list(blkid_probe pr,
+ struct list_head *vals)
+ __attribute__((nonnull));
+
+extern void blkid_probe_free_values_list(struct list_head *vals);
+
+extern struct blkid_chain *blkid_probe_get_chain(blkid_probe pr)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+
+extern struct blkid_prval *__blkid_probe_get_value(blkid_probe pr, int num)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+
+extern struct blkid_prval *__blkid_probe_lookup_value(blkid_probe pr, const char *name)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+
+extern unsigned long *blkid_probe_get_filter(blkid_probe pr, int chain, int create)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+
+extern int __blkid_probe_invert_filter(blkid_probe pr, int chain)
+ __attribute__((nonnull));
+extern int __blkid_probe_reset_filter(blkid_probe pr, int chain)
+ __attribute__((nonnull));
+extern int __blkid_probe_filter_types(blkid_probe pr, int chain, int flag, char *names[])
+ __attribute__((nonnull));
+
+extern void *blkid_probe_get_binary_data(blkid_probe pr, struct blkid_chain *chn)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+
+extern struct blkid_prval *blkid_probe_new_val(void)
+ __attribute__((warn_unused_result));
+extern int blkid_probe_set_value(blkid_probe pr, const char *name,
+ const unsigned char *data, size_t len)
+ __attribute__((nonnull));
+extern int blkid_probe_value_set_data(struct blkid_prval *v,
+ const unsigned char *data, size_t len)
+ __attribute__((nonnull));
+
+extern int blkid_probe_vsprintf_value(blkid_probe pr, const char *name,
+ const char *fmt, va_list ap)
+ __attribute__((nonnull));
+
+extern int blkid_probe_sprintf_value(blkid_probe pr, const char *name,
+ const char *fmt, ...)
+ __attribute__((nonnull))
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+extern int blkid_probe_set_magic(blkid_probe pr, uint64_t offset,
+ size_t len, const unsigned char *magic)
+ __attribute__((nonnull));
+
+extern int blkid_probe_verify_csum(blkid_probe pr, uint64_t csum, uint64_t expected)
+ __attribute__((nonnull));
+extern int blkid_probe_verify_csum_buf(blkid_probe pr, size_t n, const void *csum,
+ const void *expected) __attribute__((nonnull));
+
+extern void blkid_unparse_uuid(const unsigned char *uuid, char *str, size_t len)
+ __attribute__((nonnull));
+extern int blkid_uuid_is_empty(const unsigned char *buf, size_t len);
+
+extern size_t blkid_rtrim_whitespace(unsigned char *str)
+ __attribute__((nonnull));
+extern size_t blkid_ltrim_whitespace(unsigned char *str)
+ __attribute__((nonnull));
+
+extern void blkid_probe_set_wiper(blkid_probe pr, uint64_t off,
+ uint64_t size)
+ __attribute__((nonnull));
+extern int blkid_probe_is_wiped(blkid_probe pr, struct blkid_chain **chn,
+ uint64_t off, uint64_t size)
+ __attribute__((nonnull))
+ __attribute__((warn_unused_result));
+extern void blkid_probe_use_wiper(blkid_probe pr, uint64_t off, uint64_t size)
+ __attribute__((nonnull));
+
+extern int blkid_probe_get_hint(blkid_probe pr, const char *name, uint64_t *value)
+ __attribute__((nonnull(1,2)))
+ __attribute__((warn_unused_result));
+
+extern int blkid_probe_get_partitions_flags(blkid_probe pr)
+ __attribute__((nonnull));
+
+/* 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))
+
+#endif /* _BLKID_BLKIDP_H */
diff --git a/libblkid/src/cache.c b/libblkid/src/cache.c
new file mode 100644
index 0000000..5f59873
--- /dev/null
+++ b/libblkid/src/cache.c
@@ -0,0 +1,223 @@
+/*
+ * 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"
+
+/**
+ * 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.
+ */
+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 = strdup(filename);
+ else if (conf)
+ filename = conf->cachefile ? strdup(conf->cachefile) : NULL;
+ else {
+ struct blkid_config *c = blkid_read_config(NULL);
+ if (!c)
+ filename = 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 initializes library 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;
+
+ if (!ret_cache)
+ return -BLKID_ERR_PARAM;
+
+ if (!(cache = calloc(1, sizeof(struct blkid_struct_cache))))
+ return -BLKID_ERR_MEM;
+
+ DBG(CACHE, ul_debugobj(cache, "alloc (from %s)", filename ? filename : "default cache"));
+ INIT_LIST_HEAD(&cache->bic_devs);
+ INIT_LIST_HEAD(&cache->bic_tags);
+
+ if (filename && !*filename)
+ filename = NULL;
+ if (filename)
+ cache->bic_filename = 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(CACHE, ul_debugobj(cache, "freeing cache struct"));
+
+ /* DBG(CACHE, ul_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);
+ }
+
+ DBG(CACHE, ul_debugobj(cache, "freeing cache tag heads"));
+ 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(CACHE, ul_debugobj(cache, "warning: unfreed tag %s=%s",
+ 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(CACHE, ul_debugobj(cache, "freeing non-existing %s", dev->bid_name));
+ blkid_free_dev(dev);
+ cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+ } else {
+ DBG(CACHE, ul_debug("Device %s exists", dev->bid_name));
+ }
+ }
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+ blkid_cache cache = NULL;
+ int ret;
+
+ blkid_init_debug(BLKID_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/libblkid/src/config.c b/libblkid/src/config.c
new file mode 100644
index 0000000..d3f5eea
--- /dev/null
+++ b/libblkid/src/config.c
@@ -0,0 +1,202 @@
+/*
+ * 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(CONFIG, ul_debug(
+ "config file: unknown evaluation method '%s'.", 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(CONFIG, ul_debug(
+ "config file: missing newline at line '%s'.",
+ 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 += 12;
+ if (*s && !strcasecmp(s, "yes"))
+ conf->uevent = TRUE;
+ else if (*s)
+ conf->uevent = FALSE;
+ } else if (!strncmp(s, "CACHE_FILE=", 11)) {
+ s += 11;
+ free(conf->cachefile);
+ if (*s)
+ conf->cachefile = strdup(s);
+ else
+ conf->cachefile = NULL;
+ } else if (!strncmp(s, "EVALUATE=", 9)) {
+ s += 9;
+ if (*s && parse_evaluate(conf, s) == -1)
+ return -1;
+ } else {
+ DBG(CONFIG, ul_debug(
+ "config file: unknown option '%s'.", 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 = calloc(1, sizeof(*conf));
+ if (!conf)
+ return NULL;
+ conf->uevent = -1;
+
+ DBG(CONFIG, ul_debug("reading config file: %s.", filename));
+
+ f = fopen(filename, "r" UL_CLOEXECSTR);
+ if (!f) {
+ DBG(CONFIG, ul_debug("%s: does not exist, using built-in default", filename));
+ goto dflt;
+ }
+ while (!feof(f)) {
+ if (parse_next(f, conf)) {
+ DBG(CONFIG, ul_debug("%s: parse error", 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 = strdup(BLKID_CACHE_FILE);
+ if (conf->uevent == -1)
+ conf->uevent = TRUE;
+ if (f)
+ fclose(f);
+ return conf;
+err:
+ free(conf->cachefile);
+ 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(BLKID_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/libblkid/src/dev.c b/libblkid/src/dev.c
new file mode 100644
index 0000000..c38ec3d
--- /dev/null
+++ b/libblkid/src/dev.c
@@ -0,0 +1,279 @@
+/*
+ * 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 = calloc(1, sizeof(struct blkid_struct_dev))))
+ return NULL;
+
+ DBG(DEV, ul_debugobj(dev, "alloc"));
+ 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(DEV, ul_debugobj(dev, "freeing (%s)", dev->bid_name));
+
+ 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_xname);
+ free(dev->bid_name);
+ free(dev);
+}
+
+/*
+ * Given a blkid device, return its name. The function returns the name
+ * previously used for blkid_get_dev(). This name does not have to be canonical
+ * (real path) name, but for example symlink.
+ */
+const char *blkid_dev_devname(blkid_dev dev)
+{
+ if (!dev)
+ return NULL;
+ if (dev->bid_xname)
+ return dev->bid_xname;
+ return dev->bid_name;
+}
+
+void blkid_debug_dump_dev(blkid_dev dev)
+{
+ struct list_head *p;
+
+ if (!dev) {
+ printf(" dev: NULL\n");
+ return;
+ }
+
+ fprintf(stderr, " dev: name = %s\n", dev->bid_name);
+ fprintf(stderr, " dev: DEVNO=\"0x%0lx\"\n", (unsigned long)dev->bid_devno);
+ fprintf(stderr, " dev: TIME=\"%lld.%lld\"\n", (long long)dev->bid_time, (long long)dev->bid_utime);
+ fprintf(stderr, " dev: PRI=\"%d\"\n", dev->bid_pri);
+ fprintf(stderr, " 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)
+ fprintf(stderr, " tag: %s=\"%s\"\n", tag->bit_name,
+ tag->bit_val);
+ else
+ fprintf(stderr, " tag: NULL\n");
+ }
+}
+
+/*
+ * 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 implementation. 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 trade-off 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;
+};
+
+blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache)
+{
+ blkid_dev_iterate iter;
+
+ if (!cache) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ 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 = NULL;
+ iter->search_value = NULL;
+ }
+ return iter;
+}
+
+int blkid_dev_set_search(blkid_dev_iterate iter,
+ const char *search_type, const 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
+ */
+int blkid_dev_next(blkid_dev_iterate iter,
+ blkid_dev *ret_dev)
+{
+ blkid_dev dev;
+
+ if (!ret_dev || !iter || iter->magic != DEV_ITERATE_MAGIC)
+ return -1;
+ *ret_dev = NULL;
+ 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;
+}
+
+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
+
+static 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/libblkid/src/devname.c b/libblkid/src/devname.c
new file mode 100644
index 0000000..3f48032
--- /dev/null
+++ b/libblkid/src/devname.c
@@ -0,0 +1,647 @@
+/*
+ * 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 "fileutils.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;
+ char *cn = NULL;
+
+ if (!cache || !devname)
+ return NULL;
+
+ /* search by name */
+ list_for_each(p, &cache->bic_devs) {
+ tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
+ if (strcmp(tmp->bid_name, devname) != 0)
+ continue;
+ dev = tmp;
+ break;
+ }
+
+ /* try canonicalize the name */
+ if (!dev && (cn = canonicalize_path(devname))) {
+ if (strcmp(cn, devname) != 0) {
+ DBG(DEVNAME, ul_debug("search canonical %s", cn));
+ list_for_each(p, &cache->bic_devs) {
+ tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
+ if (strcmp(tmp->bid_name, cn) != 0)
+ continue;
+ dev = tmp;
+
+ /* update name returned by blkid_dev_devname() */
+ free(dev->bid_xname);
+ dev->bid_xname = strdup(devname);
+ break;
+ }
+ } else {
+ free(cn);
+ cn = NULL;
+ }
+ }
+
+ if (!dev && (flags & BLKID_DEV_CREATE)) {
+ if (access(devname, F_OK) < 0)
+ goto done;
+ dev = blkid_new_dev();
+ if (!dev)
+ goto done;
+ dev->bid_time = (uintmax_t)1 << (sizeof(time_t) * 8 - 1);
+ if (cn) {
+ dev->bid_name = cn;
+ dev->bid_xname = strdup(devname);
+ cn = NULL; /* see free() below */
+ } else
+ dev->bid_name = 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))
+ goto done;
+ /*
+ * 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) != 0)
+ continue;
+ if (dev->bid_label && dev2->bid_label &&
+ strcmp(dev->bid_label, dev2->bid_label) != 0)
+ continue;
+ if (dev->bid_uuid && dev2->bid_uuid &&
+ strcmp(dev->bid_uuid, dev2->bid_uuid) != 0)
+ 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);
+ }
+ }
+done:
+ if (dev)
+ DBG(DEVNAME, ul_debug("%s requested, found %s in cache", devname, dev->bid_name));
+ free(cn);
+ return dev;
+}
+
+/* Directories where we will try to search for device names */
+static const char *dirlist[] = { "/dev", "/devfs", "/devices", NULL };
+
+/*
+ * Return 1 if the device is a device-mapper 'leaf' node
+ * not holding any other devices in its hierarchy.
+ */
+static int is_dm_leaf(const char *devname)
+{
+ DIR *dir;
+ char path[NAME_MAX + 18 + 1];
+ int ret;
+
+ snprintf(path, sizeof(path), "/sys/block/%s/holders", devname);
+ if ((dir = opendir(path)) == NULL)
+ return 0;
+
+ ret = xreaddir(dir) == NULL ? 1 : 0; /* 'leaf' has no entries */
+
+ 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 = NULL;
+ }
+ }
+ 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, NULL, &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];
+
+ snprintf(device, sizeof(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 = strdup(device);
+ goto get_dev;
+ }
+ }
+ /* Do a short-cut scan of /dev/mapper first */
+ if (!devname)
+ blkid__scan_dir("/dev/mapper", devno, NULL, &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/", 12)) {
+ 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;
+ }
+}
+
+#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(DEVNAME, ul_debug("opening %s", lvm_device));
+ if ((lvf = fopen(lvm_device, "r" UL_CLOEXECSTR)) == NULL) {
+ DBG(DEVNAME, ul_debug("%s: (%d) %m", 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(DEVNAME, ul_debug("probing LVM devices under %s", VG_DIR));
+
+ while ((vg_iter = readdir(vg_list)) != NULL) {
+ DIR *lv_list;
+ char *vdirname;
+ char *vg_name;
+ struct dirent *lv_iter;
+ size_t len;
+
+ vg_name = vg_iter->d_name;
+ if (!strcmp(vg_name, ".") || !strcmp(vg_name, ".."))
+ continue;
+ len = vg_len + strlen(vg_name) + 8;
+ vdirname = malloc(len);
+ if (!vdirname)
+ goto exit;
+ snprintf(vdirname, len, "%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;
+
+ len = vg_len + strlen(vg_name) + strlen(lv_name) + 8;
+ lvm_device = malloc(len);
+ if (!lvm_device) {
+ closedir(lv_list);
+ goto exit;
+ }
+ snprintf(lvm_device, len, "%s/%s/LVs/%s", VG_DIR, vg_name,
+ lv_name);
+ dev = lvm_get_devno(lvm_device);
+ snprintf(lvm_device, len, "%s/%s", vg_name, lv_name);
+ DBG(DEVNAME, ul_debug("Probe LVM dev %s: devno 0x%04X",
+ 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
+
+static void
+ubi_probe_all(blkid_cache cache, int only_if_new)
+{
+ const char **dirname;
+
+ for (dirname = dirlist; *dirname; dirname++) {
+ DIR *dir;
+ struct dirent *iter;
+
+ DBG(DEVNAME, ul_debug("probing UBI volumes under %s",
+ *dirname));
+
+ 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 (fstatat(dirfd(dir), name, &st, 0))
+ continue;
+
+ dev = st.st_rdev;
+
+ if (!S_ISCHR(st.st_mode) || !minor(dev))
+ continue;
+ DBG(DEVNAME, ul_debug("Probe UBI vol %s/%s: devno 0x%04X",
+ *dirname, name, (int) dev));
+ probe_one(cache, name, dev, BLKID_PRI_UBI, only_if_new, 0);
+ }
+ closedir(dir);
+ }
+}
+
+/*
+ * This function uses /sys to read all block devices in way compatible with
+ * /proc/partitions (like the original libblkid implementation)
+ */
+static int
+sysfs_probe_all(blkid_cache cache, int only_if_new, int only_removable)
+{
+ DIR *sysfs;
+ struct dirent *dev;
+
+ sysfs = opendir(_PATH_SYS_BLOCK);
+ if (!sysfs)
+ return -BLKID_ERR_SYSFS;
+
+ DBG(DEVNAME, ul_debug(" probe /sys/block"));
+
+ /* scan /sys/block */
+ while ((dev = xreaddir(sysfs))) {
+ DIR *dir = NULL;
+ dev_t devno;
+ size_t nparts = 0;
+ unsigned int maxparts = 0, removable = 0;
+ struct dirent *part;
+ struct path_cxt *pc = NULL;
+ uint64_t size = 0;
+
+ DBG(DEVNAME, ul_debug("checking %s", dev->d_name));
+
+ devno = sysfs_devname_to_devno(dev->d_name);
+ if (!devno)
+ goto next;
+ pc = ul_new_sysfs_path(devno, NULL, NULL);
+ if (!pc)
+ goto next;
+
+ if (ul_path_read_u64(pc, &size, "size") != 0)
+ size = 0;
+ if (ul_path_read_u32(pc, &removable, "removable") != 0)
+ removable = 0;
+
+ /* ignore empty devices */
+ if (!size)
+ goto next;
+
+ /* accept removable if only removable requested */
+ if (only_removable) {
+ if (!removable)
+ goto next;
+
+ /* emulate /proc/partitions
+ * -- ignore empty devices and non-partitionable removable devices */
+ } else {
+ if (ul_path_read_u32(pc, &maxparts, "ext_range") != 0)
+ maxparts = 0;
+ if (!maxparts && removable)
+ goto next;
+ }
+
+ DBG(DEVNAME, ul_debug("read device name %s", dev->d_name));
+
+ dir = ul_path_opendir(pc, NULL);
+ if (!dir)
+ goto next;
+
+ /* read /sys/block/<name>/ do get partitions */
+ while ((part = xreaddir(dir))) {
+ dev_t partno;
+
+ if (!sysfs_blkdev_is_partition_dirent(dir, part, dev->d_name))
+ continue;
+
+ /* ignore extended partitions
+ * -- recount size to blocks like /proc/partitions */
+ if (ul_path_readf_u64(pc, &size, "%s/size", part->d_name) == 0
+ && (size >> 1) == 1)
+ continue;
+ partno = __sysfs_devname_to_devno(NULL, part->d_name, dev->d_name);
+ if (!partno)
+ continue;
+
+ DBG(DEVNAME, ul_debug(" Probe partition dev %s, devno 0x%04X",
+ part->d_name, (unsigned int) partno));
+ nparts++;
+ probe_one(cache, part->d_name, partno, 0, only_if_new, 0);
+ }
+
+ if (!nparts) {
+ /* add non-partitioned whole disk to cache */
+ DBG(DEVNAME, ul_debug(" Probe whole dev %s, devno 0x%04X",
+ dev->d_name, (unsigned int) devno));
+ probe_one(cache, dev->d_name, devno, 0, only_if_new, 0);
+ } else {
+ /* remove partitioned whole-disk from cache */
+ struct list_head *p, *pnext;
+
+ 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) {
+ DBG(DEVNAME, ul_debug(" freeing %s", tmp->bid_name));
+ blkid_free_dev(tmp);
+ cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+ break;
+ }
+ }
+ }
+ next:
+ if (dir)
+ closedir(dir);
+ if (pc)
+ ul_unref_path(pc);
+ }
+
+ closedir(sysfs);
+ return 0;
+}
+
+/*
+ * Read the device data for all available block devices in the system.
+ */
+static int probe_all(blkid_cache cache, int only_if_new, int update_interval)
+{
+ int rc;
+
+ if (!cache)
+ return -BLKID_ERR_PARAM;
+
+ if (cache->bic_flags & BLKID_BIC_FL_PROBED &&
+ time(NULL) - cache->bic_time < BLKID_PROBE_INTERVAL) {
+ DBG(PROBE, ul_debug("don't re-probe [delay < %d]", BLKID_PROBE_INTERVAL));
+ return 0;
+ }
+
+ blkid_read_cache(cache);
+#ifdef VG_DIR
+ lvm_probe_all(cache, only_if_new);
+#endif
+ ubi_probe_all(cache, only_if_new);
+
+ rc = sysfs_probe_all(cache, only_if_new, 0);
+
+ /* Don't mark the change as "probed" if /sys not avalable */
+ if (update_interval && rc == 0) {
+ cache->bic_time = time(NULL);
+ cache->bic_flags |= BLKID_BIC_FL_PROBED;
+ }
+
+ blkid_flush_cache(cache);
+ 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(PROBE, ul_debug("Begin blkid_probe_all()"));
+ ret = probe_all(cache, 0, 1);
+ DBG(PROBE, ul_debug("End blkid_probe_all() [rc=%d]", ret));
+ 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(PROBE, ul_debug("Begin blkid_probe_all_new()"));
+ ret = probe_all(cache, 1, 0);
+ DBG(PROBE, ul_debug("End blkid_probe_all_new() [rc=%d]", ret));
+ 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(PROBE, ul_debug("Begin blkid_probe_all_removable()"));
+ ret = sysfs_probe_all(cache, 0, 1);
+ DBG(PROBE, ul_debug("End blkid_probe_all_removable() [rc=%d]", ret));
+ return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+ blkid_cache cache = NULL;
+ int ret;
+
+ blkid_init_debug(BLKID_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/libblkid/src/devno.c b/libblkid/src/devno.c
new file mode 100644
index 0000000..a1ab543
--- /dev/null
+++ b/libblkid/src/devno.c
@@ -0,0 +1,368 @@
+/*
+ * 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 "sysfs.h"
+#include "strutils.h"
+
+static 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)
+ p = mempcpy(p, a, al);
+ if (bl)
+ p = mempcpy(p, b, bl);
+ if (cl)
+ p = mempcpy(p, c, 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) :
+ dir ? strdup(dir) : NULL;
+
+ 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)) != NULL) {
+#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 (fstatat(dirfd(dir), dp->d_name, &st, 0))
+ continue;
+
+ if (S_ISBLK(st.st_mode) && st.st_rdev == devno) {
+ *devname = blkid_strconcat(dirname, "/", dp->d_name);
+ DBG(DEVNO, ul_debug("found 0x%llx at %s", (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 (fstatat(dirfd(dir), dp->d_name, &st, AT_SYMLINK_NOFOLLOW) ||
+ !S_ISDIR(st.st_mode))
+ continue; /* symlink or fstatat() 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);
+}
+
+/* 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(DEVNO, ul_debug("directory %s", 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;
+ 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(DEVNO, ul_debug("blkid: couldn't find devno 0x%04lx",
+ (unsigned long) devno));
+ } else {
+ DBG(DEVNO, ul_debug("found devno 0x%04llx as %s", (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 drvmaj)
+{
+ FILE *f;
+ char buf[128];
+ int match = 0;
+
+ f = fopen(_PATH_PROC_DEVICES, "r" UL_CLOEXECSTR);
+ 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 == drvmaj && strcmp(name, drvname) == 0) {
+ match = 1;
+ break;
+ }
+ }
+
+ fclose(f);
+
+ DBG(DEVNO, ul_debug("major %d %s associated with '%s' driver",
+ drvmaj, match ? "is" : "is NOT", drvname));
+ return match;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+ char *devname, *tmp;
+ char diskname[PATH_MAX];
+ int devmaj, devmin;
+ dev_t devno, disk_devno;
+ const char *errmsg = "Couldn't parse %s: %s\n";
+
+ blkid_init_debug(BLKID_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 {
+ devmaj = strtoul(argv[1], &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, errmsg, "major number", argv[1]);
+ exit(1);
+ }
+ devmin = strtoul(argv[2], &tmp, 0);
+ if (*tmp) {
+ fprintf(stderr, errmsg, "minor number", argv[2]);
+ exit(1);
+ }
+ devno = makedev(devmaj, devmin);
+ }
+ 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);
+ if (blkid_devno_to_wholedisk(devno, diskname,
+ sizeof(diskname), &disk_devno) == 0)
+ printf("found devno 0x%04llx as /dev/%s\n", (long long) disk_devno, diskname);
+
+ return 0;
+}
+#endif
diff --git a/libblkid/src/encode.c b/libblkid/src/encode.c
new file mode 100644
index 0000000..8213873
--- /dev/null
+++ b/libblkid/src/encode.c
@@ -0,0 +1,263 @@
+
+/*
+ * 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"
+#include "strutils.h"
+
+/**
+ * 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 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;
+}
+
+/**
+ * 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 || !str_enc || !len)
+ 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
+ *
+ * Processing whitespace characters. Allows valid ascii,valid utf8.
+ * Replace everything else with'_'
+ *
+ * Returns: 0 on success or -1 in case of error.
+ */
+int blkid_safe_string(const char *str, char *str_safe, size_t len)
+{
+ size_t i = 0;
+
+ if (!str || !str_safe || !len)
+ return -1;
+
+ __normalize_whitespace(
+ (const unsigned char *) str, strnlen(str, len),
+ (unsigned char *) str_safe, len);
+
+ while (i < len && str_safe[i] != '\0') {
+ int seqsz;
+
+ /* accept ASCII from ' ' to '~' */
+ if (str_safe[i] > 0x20 && str_safe[i] <= 0x7E)
+ i++;
+
+ /* accept hex encoding */
+ else if (str_safe[i] == '\\' && str_safe[i+1] == 'x')
+ i += 2;
+
+ /* replace whitespace */
+ else if (isspace(str_safe[i]))
+ str_safe[i++] = '_';
+
+ /* accept valid utf8 */
+ else if ((seqsz = utf8_encoded_valid_unichar(&str_safe[i])) >= 1)
+ i += seqsz;
+
+ /* everything else is replaced with '_' */
+ else
+ str_safe[i++] = '_';
+ }
+
+ str_safe[len - 1] = '\0';
+ return 0;
+}
diff --git a/libblkid/src/evaluate.c b/libblkid/src/evaluate.c
new file mode 100644
index 0000000..cd2f3a3
--- /dev/null
+++ b/libblkid/src/evaluate.c
@@ -0,0 +1,325 @@
+/*
+ * 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 "closestream.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;
+
+ if (strcmp(token, "ID") == 0)
+ return 0; /* non-content tag */
+
+ 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|O_CLOEXEC|O_NONBLOCK);
+ 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(EVALUATE, ul_debug("%s: %s verification %s",
+ 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(EVALUATE, ul_debug("%s: uevent '%s' requested", 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" UL_CLOEXECSTR);
+ if (f) {
+ rc = 0;
+ if (fputs(action, f) >= 0)
+ rc = 0;
+ if (close_stream(f) != 0)
+ DBG(EVALUATE, ul_debug("write failed: %s", uevent));
+ }
+ DBG(EVALUATE, ul_debug("%s: send uevent %s",
+ uevent, rc == 0 ? "SUCCESS" : "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(EVALUATE, ul_debug("evaluating by udev %s=%s", 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 if (!strcmp(token, "ID"))
+ strcpy(dev, _PATH_DEV_BYID "/");
+ else {
+ DBG(EVALUATE, ul_debug("unsupported token %s", token));
+ return NULL; /* unsupported tag */
+ }
+
+ len = strlen(dev);
+ if (blkid_encode_string(value, &dev[len], sizeof(dev) - len) != 0)
+ return NULL;
+
+ DBG(EVALUATE, ul_debug("expected udev link: %s", 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(EVALUATE, ul_debug("failed to evaluate by udev"));
+
+ 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(EVALUATE, ul_debug("evaluating by blkid scan %s=%s", token, value));
+
+ if (!c) {
+ char *cachefile = blkid_get_cache_filename(conf);
+ int rc = blkid_get_cache(&c, cachefile);
+ free(cachefile);
+ if (rc < 0)
+ return NULL;
+ }
+ 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;
+
+ DBG(EVALUATE, ul_debug("evaluating %s%s%s", token, value ? "=" : "",
+ value ? value : ""));
+
+ if (!value) {
+ if (!strchr(token, '=')) {
+ ret = strdup(token);
+ goto out;
+ }
+ if (blkid_parse_tag_string(token, &t, &v) != 0 || !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(EVALUATE, ul_debug("%s=%s evaluated as %s", 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;
+ }
+
+ 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/libblkid/src/fuzz.c b/libblkid/src/fuzz.c
new file mode 100644
index 0000000..772340b
--- /dev/null
+++ b/libblkid/src/fuzz.c
@@ -0,0 +1,52 @@
+#include "blkidP.h"
+#include "fuzz.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+static int process_file(const char *name)
+{
+ int rc = -1;
+ blkid_probe pr = blkid_new_probe_from_filename(name);
+ if (pr != NULL) {
+ blkid_probe_enable_partitions(pr, TRUE);
+ blkid_probe_set_partitions_flags(pr, FALSE);
+ blkid_probe_enable_superblocks(pr, TRUE);
+ blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_DEFAULT | BLKID_SUBLKS_FSINFO | BLKID_SUBLKS_MAGIC | BLKID_SUBLKS_VERSION | BLKID_SUBLKS_BADCSUM);
+ rc = blkid_do_safeprobe(pr) == -1 ? -1 : 0;
+ }
+ blkid_free_probe(pr);
+ return rc;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ int fd;
+ char name[] = "/tmp/test-script-fuzz.XXXXXX";
+
+ fd = mkostemp(name, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
+ if (fd == -1)
+ err(EXIT_FAILURE, "mkostemp() failed");
+
+ if (write(fd, data, size) != (ssize_t)size)
+ goto out;
+
+ process_file(name);
+out:
+ close(fd);
+ unlink(name);
+ return 0;
+}
+
+#ifndef FUZZ_TARGET
+int main(int argc, char **argv)
+{
+ for (int i = 1; i < argc; i++) {
+ printf("%s ", argv[i]);
+ if (process_file(argv[i]) == 0)
+ printf(" OK\n");
+ else
+ printf(" FAILED\n");
+
+ }
+}
+#endif
diff --git a/libblkid/src/getsize.c b/libblkid/src/getsize.c
new file mode 100644
index 0000000..abe6ebc
--- /dev/null
+++ b/libblkid/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/libblkid/src/init.c b/libblkid/src/init.c
new file mode 100644
index 0000000..172225f
--- /dev/null
+++ b/libblkid/src/init.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008-2013 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/**
+ * SECTION: init
+ * @title: Library initialization
+ * @short_description: initialize debugging
+ */
+
+#include <stdarg.h>
+
+#include "blkidP.h"
+
+UL_DEBUG_DEFINE_MASK(libblkid);
+UL_DEBUG_DEFINE_MASKNAMES(libblkid) =
+{
+ { "all", BLKID_DEBUG_ALL, "info about all subsystems" },
+ { "cache", BLKID_DEBUG_CACHE, "blkid tags cache" },
+ { "config", BLKID_DEBUG_CONFIG, "config file utils" },
+ { "dev", BLKID_DEBUG_DEV, "device utils" },
+ { "devname", BLKID_DEBUG_DEVNAME, "/proc/partitions evaluation" },
+ { "devno", BLKID_DEBUG_DEVNO, "conversions to device name" },
+ { "evaluate", BLKID_DEBUG_EVALUATE, "tags resolving" },
+ { "help", BLKID_DEBUG_HELP, "this help" },
+ { "lowprobe", BLKID_DEBUG_LOWPROBE, "superblock/raids/partitions probing" },
+ { "buffer", BLKID_DEBUG_BUFFER, "low-probing buffers" },
+ { "probe", BLKID_DEBUG_PROBE, "devices verification" },
+ { "read", BLKID_DEBUG_READ, "cache parsing" },
+ { "save", BLKID_DEBUG_SAVE, "cache writing" },
+ { "tag", BLKID_DEBUG_TAG, "tags utils" },
+ { NULL, 0, NULL }
+};
+
+/**
+ * blkid_init_debug:
+ * @mask: debug mask (0xffff to enable full debugging)
+ *
+ * If the @mask is not specified then this function reads
+ * LIBBLKID_DEBUG environment variable to get the mask.
+ *
+ * Already initialized debugging stuff cannot be changed. It does not
+ * have effect to call this function twice.
+ */
+void blkid_init_debug(int mask)
+{
+ if (libblkid_debug_mask)
+ return;
+
+ __UL_INIT_DEBUG_FROM_ENV(libblkid, BLKID_DEBUG_, mask, LIBBLKID_DEBUG);
+
+ if (libblkid_debug_mask != BLKID_DEBUG_INIT
+ && libblkid_debug_mask != (BLKID_DEBUG_HELP|BLKID_DEBUG_INIT)) {
+ const char *ver = NULL;
+ const char *date = NULL;
+
+ blkid_get_library_version(&ver, &date);
+ DBG(INIT, ul_debug("library debug mask: 0x%04x", libblkid_debug_mask));
+ DBG(INIT, ul_debug("library version: %s [%s]", ver, date));
+
+ }
+ ON_DBG(HELP, ul_debug_print_masks("LIBBLKID_DEBUG",
+ UL_DEBUG_MASKNAMES(libblkid)));
+}
+
+static void __attribute__ ((constructor)) blkid_init_default_debug(void)
+{
+ blkid_init_debug(0);
+}
diff --git a/libblkid/src/libblkid.sym b/libblkid/src/libblkid.sym
new file mode 100644
index 0000000..775b024
--- /dev/null
+++ b/libblkid/src/libblkid.sym
@@ -0,0 +1,189 @@
+/*
+ * The symbol versioning ensures that a new application requiring symbol 'foo'
+ * can't run with old library.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.
+ *
+ * Copyright (C) 2009-2014 Karel Zak <kzak@redhat.com>
+ */
+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;
+
+/*
+ * symbols since util-linux 2.23
+ */
+BLKID_2.23 {
+global:
+ blkid_probe_step_back;
+ blkid_parttable_get_id;
+ blkid_init_debug;
+} BLKID_2.21;
+
+/*
+ * symbols since util-linux 2.25
+ */
+BLKID_2.25 {
+ blkid_partlist_get_partition_by_partno;
+} BLKID_2.23;
+
+BLKID_2.30 {
+ blkid_probe_set_sectorsize;
+ blkid_partitions_get_name;
+} BLKID_2.25;
+
+BLKID_2_31 {
+ blkid_probe_reset_buffers;
+ blkid_probe_hide_range;
+} BLKID_2.30;
+
+BLKID_2_36 {
+ blkid_topology_get_dax;
+} BLKID_2_31;
+
+BLKID_2_37 {
+ blkid_probe_set_hint;
+ blkid_probe_reset_hints;
+} BLKID_2_36;
+
+BLKID_2_39 {
+ blkid_topology_get_diskseq;
+} BLKID_2_37;
diff --git a/libblkid/src/partitions/aix.c b/libblkid/src/partitions/aix.c
new file mode 100644
index 0000000..03a311a
--- /dev/null
+++ b/libblkid/src/partitions/aix.c
@@ -0,0 +1,57 @@
+/*
+ * 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 BLKID_PROBE_OK;
+
+ ls = blkid_probe_get_partlist(pr);
+ if (!ls)
+ return BLKID_PROBE_NONE;
+
+ tab = blkid_partlist_new_parttable(ls, "aix", 0);
+ if (!tab)
+ return -ENOMEM;
+
+ return BLKID_PROBE_OK;
+}
+
+/*
+ * 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 trying to be smart and AIX signature is ignored when
+ * there is a valid DOS partitions table. We don't support such behavior. 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/libblkid/src/partitions/aix.h b/libblkid/src/partitions/aix.h
new file mode 100644
index 0000000..f767c5a
--- /dev/null
+++ b/libblkid/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/libblkid/src/partitions/atari.c b/libblkid/src/partitions/atari.c
new file mode 100644
index 0000000..314f047
--- /dev/null
+++ b/libblkid/src/partitions/atari.c
@@ -0,0 +1,312 @@
+/*
+ * atari partitions parsing code
+ *
+ * Copyright (C) 2018 Vaclav Dolezal <vdolezal@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Based on Linux kernel implementation and atari-fdisk
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "partitions.h"
+
+struct atari_part_def {
+ /*
+ * flags:
+ * 0 (LSB): active
+ * 1-6: (reserved)
+ * 7 (MSB): bootable
+ */
+ unsigned char flags;
+ char id[3];
+ uint32_t start;
+ uint32_t size;
+} __attribute__((packed));
+
+struct atari_rootsector {
+ char unused0[0x156]; /* boot code */
+ struct atari_part_def icd_part[8]; /* ICD partition entries */
+ char unused1[0xc];
+ uint32_t hd_size;
+ struct atari_part_def part[4]; /* primary partition entries */
+ uint32_t bsl_start; /* bad sector list start */
+ uint32_t bsl_len; /* bad sector list length */
+ uint16_t checksum;
+} __attribute__((packed));
+
+
+/*
+ * Generated using linux kernel ctype.{c,h}
+ *
+ * Since kernel uses isalnum() to detect whether it is Atari PT, we need same
+ * definition of alnum character to be consistent with kernel.
+ */
+static const unsigned char _linux_isalnum[] = {
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,
+0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
+0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1
+};
+
+static int linux_isalnum(unsigned char c) {
+ return _linux_isalnum[c];
+}
+
+#define isalnum linux_isalnum
+
+#define IS_ACTIVE(partdef) ((partdef).flags & 1)
+
+static int is_valid_dimension(uint32_t start, uint32_t size, uint32_t maxoff)
+{
+ uint64_t end = start + size;
+
+ return end >= start
+ && 0 < start && start <= maxoff
+ && 0 < size && size <= maxoff
+ && 0 < end && end <= maxoff;
+}
+
+static int is_valid_partition(struct atari_part_def *part, uint32_t maxoff)
+{
+ uint32_t start = be32_to_cpu(part->start),
+ size = be32_to_cpu(part->size);
+
+ return (part->flags & 1)
+ && isalnum(part->id[0])
+ && isalnum(part->id[1])
+ && isalnum(part->id[2])
+ && is_valid_dimension(start, size, maxoff);
+}
+
+static int is_id_common(char *id)
+{
+ const char *ids[] = {"GEM", "BGM", "LNX", "SWP", "RAW", };
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(ids); i++) {
+ if (!memcmp(ids[i], id, 3))
+ return 1;
+ }
+ return 0;
+}
+
+static int parse_partition(blkid_partlist ls, blkid_parttable tab,
+ struct atari_part_def *part, uint32_t offset)
+{
+ blkid_partition par;
+ uint32_t start;
+ uint32_t size;
+
+ start = be32_to_cpu(part->start) + offset;
+ size = be32_to_cpu(part->size);
+
+ if (blkid_partlist_get_partition_by_start(ls, start)) {
+ /* Don't increment partno for extended parts */
+ if (!offset)
+ blkid_partlist_increment_partno(ls);
+ return 0;
+ }
+
+ par = blkid_partlist_add_partition(ls, tab, start, size);
+ if (!par)
+ return -ENOMEM;
+
+ blkid_partition_set_type_string(par, (unsigned char *) part->id,
+ sizeof(part->id));
+ return 1;
+}
+
+/*
+ * \return 1: OK, 0: bad format or -errno
+ */
+static int parse_extended(blkid_probe pr, blkid_partlist ls,
+ blkid_parttable tab, struct atari_part_def *part)
+{
+ uint32_t x0start, xstart;
+ unsigned ct = 0, i = 0;
+ int rc;
+
+ x0start = xstart = be32_to_cpu(part->start);
+ while (1) {
+ struct atari_rootsector *xrs;
+
+ if (++ct > 100)
+ break;
+
+ xrs = (struct atari_rootsector *) blkid_probe_get_sector(pr, xstart);
+ if (!xrs) {
+ if (errno)
+ return -errno;
+ return 0;
+ }
+
+ /*
+ * There must be data partition followed by reference to next
+ * XGM or inactive entry.
+ */
+ for (i=0; ; i++) {
+ if (i >= ARRAY_SIZE(xrs->part) - 1)
+ return 0;
+ if (IS_ACTIVE(xrs->part[i]))
+ break;
+ }
+
+ if (!memcmp(xrs->part[i].id, "XGM", 3))
+ return 0;
+
+ rc = parse_partition(ls, tab, &xrs->part[i], xstart);
+ if (rc <= 0)
+ return rc;
+
+ if (!IS_ACTIVE(xrs->part[i+1]))
+ break;
+
+ if (memcmp(xrs->part[i+1].id, "XGM", 3) != 0)
+ return 0;
+
+ xstart = x0start + be32_to_cpu(xrs->part[i+1].start);
+ }
+
+ return 1;
+}
+
+static int probe_atari_pt(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ struct atari_rootsector *rs;
+
+ blkid_parttable tab = NULL;
+ blkid_partlist ls;
+
+ unsigned i;
+ int has_xgm = 0;
+ int rc = 0;
+ uint32_t rssize; /* size in sectors from root sector */
+ uint64_t size; /* size in sectors from system */
+
+ /* Atari partition is not defined for other sector sizes */
+ if (blkid_probe_get_sectorsize(pr) != 512)
+ goto nothing;
+
+ size = blkid_probe_get_size(pr) / 512;
+
+ /* Atari is not well defined to support large disks */
+ if (size > INT32_MAX)
+ goto nothing;
+
+ /* read root sector */
+ rs = (struct atari_rootsector *) blkid_probe_get_sector(pr, 0);
+ if (!rs) {
+ if (errno)
+ return -errno;
+ goto nothing;
+ }
+
+ rssize = be32_to_cpu(rs->hd_size);
+
+ /* check number of sectors stored in the root sector */
+ if (rssize < 2 || rssize > size)
+ goto nothing;
+
+ /* check list of bad blocks */
+ if ((rs->bsl_start || rs->bsl_len)
+ && !is_valid_dimension(be32_to_cpu(rs->bsl_start),
+ be32_to_cpu(rs->bsl_len),
+ rssize))
+ goto nothing;
+
+ /*
+ * At least one valid partition required
+ */
+ for (i = 0; i < 4; i++) {
+ if (is_valid_partition(&rs->part[i], rssize)) {
+ if (blkid_probe_set_magic(pr,
+ offsetof(struct atari_rootsector, part[i]),
+ sizeof(rs->part[i].flags) + sizeof(rs->part[i].id),
+ (unsigned char *) &rs->part[i]))
+ goto err;
+ break;
+ }
+ }
+
+ if (i == 4)
+ goto nothing;
+
+ if (blkid_partitions_need_typeonly(pr))
+ /* caller does not ask for details about partitions */
+ return BLKID_PROBE_OK;
+
+ ls = blkid_probe_get_partlist(pr);
+ if (!ls)
+ goto nothing;
+
+ tab = blkid_partlist_new_parttable(ls, "atari", 0);
+ if (!tab)
+ goto err;
+
+ for (i = 0; i < ARRAY_SIZE(rs->part); i++) {
+ struct atari_part_def *p = &rs->part[i];
+
+ if (!IS_ACTIVE(*p)) {
+ blkid_partlist_increment_partno(ls);
+ continue;
+ }
+ if (!memcmp(p->id, "XGM", 3)) {
+ has_xgm = 1;
+ rc = parse_extended(pr, ls, tab, p);
+ } else {
+ rc = parse_partition(ls, tab, p, 0);
+ }
+ if (rc < 0)
+ return rc;
+ }
+
+ /* if there are no XGM partitions, we can try ICD format */
+ /* if first ICD partition ID is not valid, assume no ICD format */
+ if (!has_xgm && is_id_common(rs->icd_part[0].id)) {
+ for (i = 0; i < ARRAY_SIZE(rs->icd_part); i++) {
+ struct atari_part_def *p = &rs->icd_part[i];
+
+ if (!IS_ACTIVE(*p) || !is_id_common(p->id)) {
+ blkid_partlist_increment_partno(ls);
+ continue;
+ }
+
+ rc = parse_partition(ls, tab, p, 0);
+ if (rc < 0)
+ return rc;
+ }
+ }
+
+ return BLKID_PROBE_OK;
+
+nothing:
+ return BLKID_PROBE_NONE;
+err:
+ return -ENOMEM;
+}
+
+const struct blkid_idinfo atari_pt_idinfo =
+{
+ .name = "atari",
+ .probefunc = probe_atari_pt,
+ .magics = BLKID_NONE_MAGIC
+};
diff --git a/libblkid/src/partitions/bsd.c b/libblkid/src/partitions/bsd.c
new file mode 100644
index 0000000..ba12019
--- /dev/null
+++ b/libblkid/src/partitions/bsd.c
@@ -0,0 +1,205 @@
+/*
+ * 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"
+#include "pt-bsd.h"
+
+/* 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 uint16_t bsd_checksum(const struct bsd_disklabel *l)
+{
+ uint16_t v, csum = 0;
+ const char *end = (const char *) (l + 1);
+
+ for (const char *c = (const char *) l; c < end; c += sizeof(uint16_t)) {
+ memcpy(&v, c, sizeof(v));
+ csum ^= v;
+ }
+ return csum ^ le16_to_cpu(l->d_checksum);
+}
+
+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;
+ int rc = BLKID_PROBE_NONE;
+ uint32_t abs_offset = 0;
+
+ if (blkid_partitions_need_typeonly(pr))
+ /* caller does not ask for details about partitions */
+ return rc;
+
+ data = blkid_probe_get_sector(pr, BLKID_MAG_SECTOR(mag));
+ if (!data) {
+ if (errno)
+ rc = -errno;
+ goto nothing;
+ }
+
+ l = (struct bsd_disklabel *) (data + BLKID_MAG_LASTOFFSET(mag));
+
+ if (!blkid_probe_verify_csum(pr, bsd_checksum(l), le16_to_cpu(l->d_checksum))) {
+ rc = BLKID_PROBE_NONE;
+ goto nothing;
+ }
+
+ ls = blkid_probe_get_partlist(pr);
+ if (!ls)
+ goto nothing;
+
+ /* 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 MBR_FREEBSD_PARTITION:
+ name = "freebsd";
+ abs_offset = blkid_partition_get_start(parent);
+ break;
+ case MBR_NETBSD_PARTITION:
+ name = "netbsd";
+ break;
+ case MBR_OPENBSD_PARTITION:
+ name = "openbsd";
+ break;
+ default:
+ DBG(LOWPROBE, ul_debug(
+ "WARNING: BSD label detected on unknown (0x%x) "
+ "primary partition",
+ blkid_partition_get_type(parent)));
+ break;
+ }
+ }
+
+ tab = blkid_partlist_new_parttable(ls, name, BLKID_MAG_OFFSET(mag));
+ if (!tab) {
+ rc = -ENOMEM;
+ goto nothing;
+ }
+
+ 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(LOWPROBE, ul_debug(
+ "WARNING: ignore %d more BSD partitions",
+ 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;
+
+ if (p->p_fstype == BSD_FS_UNUSED)
+ continue;
+
+ start = le32_to_cpu(p->p_offset);
+ size = le32_to_cpu(p->p_size);
+
+ /* FreeBSD since version 10 uses relative offsets. We can use
+ * 3rd partition (special wholedisk partition) to detect this
+ * situation.
+ */
+ if (abs_offset && nparts >= 3
+ && le32_to_cpu(l->d_partitions[2].p_offset) == 0)
+ start += abs_offset;
+
+ if (parent && blkid_partition_get_start(parent) == start
+ && blkid_partition_get_size(parent) == size) {
+ DBG(LOWPROBE, ul_debug(
+ "WARNING: BSD partition (%d) same like parent, "
+ "ignore", i));
+ continue;
+ }
+ if (parent && !blkid_is_nested_dimension(parent, start, size)) {
+ DBG(LOWPROBE, ul_debug(
+ "WARNING: BSD partition (%d) overflow "
+ "detected, ignore", i));
+ continue;
+ }
+
+ par = blkid_partlist_add_partition(ls, tab, start, size);
+ if (!par) {
+ rc = -ENOMEM;
+ goto nothing;
+ }
+
+ blkid_partition_set_type(par, p->p_fstype);
+ }
+
+ return BLKID_PROBE_OK;
+
+nothing:
+ return rc;
+}
+
+
+/*
+ * 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 | |
+ * ------------------------+-------------+------------
+ * sparc64 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/libblkid/src/partitions/dos.c b/libblkid/src/partitions/dos.c
new file mode 100644
index 0000000..5c7718c
--- /dev/null
+++ b/libblkid/src/partitions/dos.c
@@ -0,0 +1,375 @@
+/*
+ * 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 "superblocks/superblocks.h"
+#include "aix.h"
+
+/* see superblocks/vfat.c */
+extern int blkid_probe_is_vfat(blkid_probe pr);
+/* see superblocks/exfat.c */
+extern int blkid_probe_is_exfat(blkid_probe pr);
+
+static const struct dos_subtypes {
+ unsigned char type;
+ const struct blkid_idinfo *id;
+} dos_nested[] = {
+ { MBR_FREEBSD_PARTITION, &bsd_pt_idinfo },
+ { MBR_NETBSD_PARTITION, &bsd_pt_idinfo },
+ { MBR_OPENBSD_PARTITION, &bsd_pt_idinfo },
+ { MBR_UNIXWARE_PARTITION, &unixware_pt_idinfo },
+ { MBR_SOLARIS_X86_PARTITION, &solaris_x86_pt_idinfo },
+ { MBR_MINIX_PARTITION, &minix_pt_idinfo }
+};
+
+static inline int is_extended(struct dos_partition *p)
+{
+ return (p->sys_ind == MBR_DOS_EXTENDED_PARTITION ||
+ p->sys_ind == MBR_W95_EXTENDED_PARTITION ||
+ p->sys_ind == MBR_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;
+
+ DBG(LOWPROBE, ul_debug("parse EBR [start=%d, size=%d]", ex_start/ssf, ex_size/ssf));
+ if (ex_start == 0) {
+ DBG(LOWPROBE, ul_debug("Bad offset in primary extended partition -- ignore"));
+ return 0;
+ }
+
+ while (1) {
+ struct dos_partition *p, *p0;
+ uint32_t start = 0, size;
+
+ if (++ct_nodata > 100)
+ return BLKID_PROBE_OK;
+ data = blkid_probe_get_sector(pr, cur_start);
+ if (!data) {
+ if (errno)
+ return -errno;
+ goto leave; /* malformed partition? */
+ }
+
+ if (!mbr_is_valid_magic(data))
+ goto leave;
+
+ p0 = mbr_get_partition(data, 0);
+
+ /* 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_get_start(p) * ssf;
+ size = dos_partition_get_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;
+ }
+
+ /* Avoid recursive non-empty links, see ct_nodata counter */
+ if (blkid_partlist_get_partition_by_start(ls, abs_start)) {
+ DBG(LOWPROBE, ul_debug("#%d: EBR duplicate data partition [abs start=%u] -- ignore",
+ i + 1, abs_start));
+ continue;
+ }
+
+ par = blkid_partlist_add_partition(ls, tab, abs_start, size);
+ if (!par)
+ return -ENOMEM;
+
+ blkid_partition_set_type(par, p->sys_ind);
+ blkid_partition_set_flags(par, p->boot_ind);
+ blkid_partition_gen_uuid(par);
+ 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_get_start(p) * ssf;
+ size = dos_partition_get_size(p) * ssf;
+
+ if (size && is_extended(p)) {
+ if (start == 0)
+ DBG(LOWPROBE, ul_debug("#%d: EBR link offset is zero -- ignore", i + 1));
+ else
+ break;
+ }
+ }
+ if (i == 4)
+ goto leave;
+
+ cur_start = ex_start + start;
+ cur_size = size;
+ }
+leave:
+ return BLKID_PROBE_OK;
+}
+
+static inline int is_lvm(blkid_probe pr)
+{
+ struct blkid_prval *v = __blkid_probe_lookup_value(pr, "TYPE");
+
+ return (v && v->data && strcmp((char *) v->data, "LVM2_member") == 0);
+}
+
+static inline int is_empty_mbr(unsigned char *mbr)
+{
+ struct dos_partition *p = mbr_get_partition(mbr, 0);
+ int i, nparts = 0;
+
+ for (i = 0; i < 4; i++) {
+ if (dos_partition_get_size(p) > 0)
+ nparts++;
+ p++;
+ }
+
+ return nparts == 0;
+}
+
+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, id;
+ char idstr[UUID_STR_LEN];
+
+
+ data = blkid_probe_get_sector(pr, 0);
+ if (!data) {
+ if (errno)
+ return -errno;
+ 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;
+
+ p0 = mbr_get_partition(data, 0);
+
+ /*
+ * 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(LOWPROBE, ul_debug("missing boot indicator -- ignore"));
+ goto nothing;
+ }
+
+ /*
+ * GPT uses valid MBR
+ */
+ for (p = p0, i = 0; i < 4; i++, p++) {
+ if (p->sys_ind == MBR_GPT_PARTITION) {
+ DBG(LOWPROBE, ul_debug("probably GPT -- ignore"));
+ 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) == 1 || blkid_probe_is_exfat(pr) == 1) {
+ DBG(LOWPROBE, ul_debug("probably FAT -- ignore"));
+ goto nothing;
+ }
+
+ /* Another false positive is NTFS */
+ if (blkid_probe_is_ntfs(pr) == 1) {
+ DBG(LOWPROBE, ul_debug("probably NTFS -- ignore"));
+ goto nothing;
+ }
+
+ /*
+ * Ugly exception, if the device contains a valid LVM physical volume
+ * and empty MBR (=no partition defined) then it's LVM and MBR should
+ * be ignored. Crazy people use it to boot from LVM devices.
+ */
+ if (is_lvm(pr) && is_empty_mbr(data)) {
+ DBG(LOWPROBE, ul_debug("empty MBR on LVM device -- ignore"));
+ goto nothing;
+ }
+
+ blkid_probe_use_wiper(pr, MBR_PT_OFFSET, 512 - MBR_PT_OFFSET);
+
+ id = mbr_get_id(data);
+ if (id)
+ snprintf(idstr, sizeof(idstr), "%08x", id);
+
+ /*
+ * Well, all checks pass, it's MS-DOS partition table
+ */
+ if (blkid_partitions_need_typeonly(pr)) {
+ /* Non-binary interface -- caller does not ask for details
+ * about partitions, just set generic variables only. */
+ if (id)
+ blkid_partitions_strcpy_ptuuid(pr, idstr);
+ return 0;
+ }
+
+ ls = blkid_probe_get_partlist(pr);
+ if (!ls)
+ goto nothing;
+
+ /* 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", MBR_PT_OFFSET);
+ if (!tab)
+ return -ENOMEM;
+
+ if (id)
+ blkid_parttable_set_id(tab, (unsigned char *) idstr);
+
+ /* Parse primary partitions */
+ for (p = p0, i = 0; i < 4; i++, p++) {
+ blkid_partition par;
+
+ start = dos_partition_get_start(p) * ssf;
+ size = dos_partition_get_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)
+ return -ENOMEM;
+
+ blkid_partition_set_type(par, p->sys_ind);
+ blkid_partition_set_flags(par, p->boot_ind);
+ blkid_partition_gen_uuid(par);
+ }
+
+ /* 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_get_start(p) * ssf;
+ size = dos_partition_get_size(p) * ssf;
+
+ if (!size)
+ continue;
+ if (is_extended(p) &&
+ parse_dos_extended(pr, tab, start, size, ssf) == -1)
+ goto nothing;
+ }
+
+ /* Parse subtypes (nested partitions) on large disks */
+ if (!blkid_probe_is_tiny(pr)) {
+ int nparts = blkid_partlist_numof_partitions(ls);
+
+ DBG(LOWPROBE, ul_debug("checking for subtypes"));
+
+ for (i = 0; i < nparts; i++) {
+ size_t n;
+ int type;
+ blkid_partition pa = blkid_partlist_get_partition(ls, i);
+
+ if (pa == NULL
+ || blkid_partition_get_size(pa) == 0
+ || blkid_partition_is_extended(pa)
+ || blkid_partition_is_logical(pa))
+ continue;
+
+ type = blkid_partition_get_type(pa);
+
+ for (n = 0; n < ARRAY_SIZE(dos_nested); n++) {
+ int rc;
+
+ if (dos_nested[n].type != type)
+ continue;
+
+ rc = blkid_partitions_do_subprobe(pr, pa,
+ dos_nested[n].id);
+ if (rc < 0)
+ return rc;
+ break;
+ }
+ }
+ }
+ return BLKID_PROBE_OK;
+
+nothing:
+ return BLKID_PROBE_NONE;
+}
+
+
+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/libblkid/src/partitions/gpt.c b/libblkid/src/partitions/gpt.c
new file mode 100644
index 0000000..89e7bb6
--- /dev/null
+++ b/libblkid/src/partitions/gpt.c
@@ -0,0 +1,473 @@
+/*
+ * 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 <limits.h>
+#include <inttypes.h>
+
+#include "partitions.h"
+#include "crc32.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; /* first 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,
+ size_t exclude_off, size_t exclude_len)
+{
+ return (ul_crc32_exclude_offset(~0L, buf, len, exclude_off, exclude_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)
+{
+ uint64_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_partitions_set_flags()).
+ */
+static int is_pmbr_valid(blkid_probe pr, int *has)
+{
+ int flags = blkid_partitions_get_flags(pr);
+ unsigned char *data;
+ struct dos_partition *p;
+ int i;
+
+ if (has)
+ *has = 0;
+ else if (flags & BLKID_PARTS_FORCE_GPT)
+ return 1; /* skip PMBR check */
+
+ data = blkid_probe_get_sector(pr, 0);
+ if (!data) {
+ if (errno)
+ return -errno;
+ goto failed;
+ }
+
+ if (!mbr_is_valid_magic(data))
+ goto failed;
+
+ for (i = 0, p = mbr_get_partition(data, 0); i < 4; i++, p++) {
+ if (p->sys_ind == MBR_GPT_PARTITION) {
+ DBG(LOWPROBE, ul_debug(" #%d valid PMBR partition", i + 1));
+ goto ok;
+ }
+ }
+failed:
+ return 0;
+ok:
+ if (has)
+ *has = 1;
+ 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;
+ uint64_t lu, fu;
+ uint64_t esz;
+ uint32_t hsz, ssz;
+
+ ssz = blkid_probe_get_sectorsize(pr);
+
+ DBG(LOWPROBE, ul_debug(" checking for GPT header at %"PRIu64, lba));
+
+ /* 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 */
+ crc = count_crc32((unsigned char *) h, hsz,
+ offsetof(struct gpt_header, header_crc32),
+ sizeof(h->header_crc32));
+
+ if (!blkid_probe_verify_csum(pr, crc, le32_to_cpu(h->header_crc32))) {
+ DBG(LOWPROBE, ul_debug("GPT header corrupted"));
+ return NULL;
+ }
+
+ /* Valid header has to be at MyLBA */
+ if (le64_to_cpu(h->my_lba) != lba) {
+ DBG(LOWPROBE, ul_debug(
+ "GPT->MyLBA mismatch with real position"));
+ 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(LOWPROBE, ul_debug(
+ "GPT->{First,Last}UsableLBA out of range"));
+ return NULL;
+ }
+
+ /* The header has to be outside usable range */
+ if (fu < lba && lba < lu) {
+ DBG(LOWPROBE, ul_debug("GPT header is inside usable area"));
+ return NULL;
+ }
+
+ /* Size of blocks with GPT entries */
+ esz = (uint64_t)le32_to_cpu(h->num_partition_entries) *
+ le32_to_cpu(h->sizeof_partition_entry);
+
+ if (esz == 0 || esz >= UINT32_MAX ||
+ le32_to_cpu(h->sizeof_partition_entry) != sizeof(struct gpt_entry)) {
+ DBG(LOWPROBE, ul_debug("GPT entries undefined"));
+ 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(LOWPROBE, ul_debug("GPT entries unreadable"));
+ return NULL;
+ }
+
+ /* Validate entries */
+ crc = count_crc32((unsigned char *) *ents, esz, 0, 0);
+ if (crc != le32_to_cpu(h->partition_entry_array_crc32)) {
+ DBG(LOWPROBE, ul_debug("GPT entries corrupted"));
+ 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;
+ int ret;
+
+ if (last_lba(pr, &lastlba))
+ goto nothing;
+
+ ret = is_pmbr_valid(pr, NULL);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ goto nothing;
+
+ errno = 0;
+ h = get_gpt_header(pr, &hdr, &e, (lba = GPT_PRIMARY_LBA), lastlba);
+ if (!h && !errno)
+ h = get_gpt_header(pr, &hdr, &e, (lba = lastlba), lastlba);
+
+ if (!h) {
+ if (errno)
+ return -errno;
+ goto nothing;
+ }
+
+ blkid_probe_use_wiper(pr, lba * blkid_probe_get_size(pr), 8);
+
+ if (blkid_probe_set_magic(pr, blkid_probe_get_sectorsize(pr) * lba,
+ sizeof(GPT_HEADER_SIGNATURE_STR) - 1,
+ (unsigned char *) GPT_HEADER_SIGNATURE_STR))
+ goto err;
+
+ guid = h->disk_guid;
+ swap_efi_guid(&guid);
+
+ if (blkid_partitions_need_typeonly(pr)) {
+ /* Non-binary interface -- caller does not ask for details
+ * about partitions, just set generic variables only. */
+ blkid_partitions_set_ptuuid(pr, (unsigned char *) &guid);
+ return BLKID_PROBE_OK;
+ }
+
+ ls = blkid_probe_get_partlist(pr);
+ if (!ls)
+ goto nothing;
+
+ tab = blkid_partlist_new_parttable(ls, "gpt",
+ blkid_probe_get_sectorsize(pr) * lba);
+ if (!tab)
+ goto err;
+
+ blkid_parttable_set_uuid(tab, (const unsigned char *) &guid);
+
+ 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(LOWPROBE, ul_debug(
+ "GPT entry[%d] overflows usable area - ignore",
+ 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), UL_ENCODE_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, le64_to_cpu(e->attributes));
+ }
+
+ return BLKID_PROBE_OK;
+
+nothing:
+ return BLKID_PROBE_NONE;
+
+err:
+ return -ENOMEM;
+}
+
+
+const struct blkid_idinfo gpt_pt_idinfo =
+{
+ .name = "gpt",
+ .probefunc = probe_gpt_pt,
+
+ /*
+ * It would be possible to check for DOS signature (0xAA55), but
+ * unfortunately almost all EFI GPT implementations allow to optionally
+ * 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
+};
+
+
+
+/* probe for *alone* protective MBR */
+static int probe_pmbr_pt(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ int has = 0;
+ struct gpt_entry *e;
+ uint64_t lastlba = 0;
+ struct gpt_header hdr;
+
+ if (last_lba(pr, &lastlba))
+ goto nothing;
+
+ is_pmbr_valid(pr, &has);
+ if (!has)
+ goto nothing;
+
+ if (!get_gpt_header(pr, &hdr, &e, GPT_PRIMARY_LBA, lastlba) &&
+ !get_gpt_header(pr, &hdr, &e, lastlba, lastlba))
+ return 0;
+nothing:
+ return 1;
+}
+
+const struct blkid_idinfo pmbr_pt_idinfo =
+{
+ .name = "PMBR",
+ .probefunc = probe_pmbr_pt,
+ .magics =
+ {
+ { .magic = "\x55\xAA", .len = 2, .sboff = 510 },
+ { NULL }
+ }
+};
+
diff --git a/libblkid/src/partitions/mac.c b/libblkid/src/partitions/mac.c
new file mode 100644
index 0000000..75a558b
--- /dev/null
+++ b/libblkid/src/partitions/mac.c
@@ -0,0 +1,200 @@
+/*
+ * 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, (uint64_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, nprts, 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) {
+ if (errno)
+ return -errno;
+ goto nothing;
+ }
+
+ block_size = be16_to_cpu(md->block_size);
+ if (block_size < sizeof(struct mac_partition))
+ goto nothing;
+
+ /* 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) {
+ if (errno)
+ return -errno;
+ 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 nothing;
+
+ tab = blkid_partlist_new_parttable(ls, "mac", 0);
+ if (!tab)
+ goto err;
+
+ ssf = block_size / 512;
+ nblks = be32_to_cpu(p->map_count);
+ if (nblks > 256) {
+ nprts = 256;
+ DBG(LOWPROBE, ul_debug(
+ "mac: map_count too large, entry[0]: %u, "
+ "enforcing limit of %u", nblks, nprts));
+ } else
+ nprts = nblks;
+
+ for (i = 0; i < nprts; ++i) {
+ blkid_partition par;
+ uint32_t start;
+ uint32_t size;
+
+ p = (struct mac_partition *) get_mac_block(pr, block_size, i + 1);
+ if (!p) {
+ if (errno)
+ return -errno;
+ goto nothing;
+ }
+ if (!has_part_signature(p))
+ goto nothing;
+
+ if (be32_to_cpu(p->map_count) != nblks) {
+ DBG(LOWPROBE, ul_debug(
+ "mac: inconsistent map_count in partition map, "
+ "entry[0]: %u, entry[%u]: %u",
+ nblks, i,
+ 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 BLKID_PROBE_OK;
+
+nothing:
+ return BLKID_PROBE_NONE;
+err:
+ return -ENOMEM;
+}
+
+/*
+ * 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/libblkid/src/partitions/minix.c b/libblkid/src/partitions/minix.c
new file mode 100644
index 0000000..43c9d9a
--- /dev/null
+++ b/libblkid/src/partitions/minix.c
@@ -0,0 +1,102 @@
+/*
+ * 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 "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) {
+ if (errno)
+ return -errno;
+ goto nothing;
+ }
+
+ ls = blkid_probe_get_partlist(pr);
+ if (!ls)
+ goto nothing;
+
+ /* 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) != MBR_MINIX_PARTITION)
+ goto nothing;
+
+ if (blkid_partitions_need_typeonly(pr))
+ /* caller does not ask for details about partitions */
+ return BLKID_PROBE_OK;
+
+ tab = blkid_partlist_new_parttable(ls, "minix", MBR_PT_OFFSET);
+ if (!tab)
+ goto err;
+
+ for (i = 0, p = mbr_get_partition(data, 0);
+ i < MINIX_MAXPARTITIONS; i++, p++) {
+
+ uint32_t start, size;
+ blkid_partition par;
+
+ if (p->sys_ind != MBR_MINIX_PARTITION)
+ continue;
+
+ start = dos_partition_get_start(p);
+ size = dos_partition_get_size(p);
+
+ if (parent && !blkid_is_nested_dimension(parent, start, size)) {
+ DBG(LOWPROBE, ul_debug(
+ "WARNING: minix partition (%d) overflow "
+ "detected, ignore", i));
+ continue;
+ }
+
+ par = blkid_partlist_add_partition(ls, tab, start, size);
+ if (!par)
+ goto err;
+
+ blkid_partition_set_type(par, p->sys_ind);
+ blkid_partition_set_flags(par, p->boot_ind);
+ }
+
+ return BLKID_PROBE_OK;
+
+nothing:
+ return BLKID_PROBE_NONE;
+err:
+ return -ENOMEM;
+}
+
+/* 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/libblkid/src/partitions/partitions.c b/libblkid/src/partitions/partitions.c
new file mode 100644
index 0000000..8ebf480
--- /dev/null
+++ b/libblkid/src/partitions/partitions.c
@@ -0,0 +1,1532 @@
+/*
+ * 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"
+#include "strutils.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.).
+ *
+ * @PTUUID: partition table id (uuid for gpt, hex for dos).
+
+ * @PART_ENTRY_SCHEME: partition table type
+ *
+ * @PART_ENTRY_NAME: partition name (gpt and mac only)
+ *
+ * @PART_ENTRY_UUID: partition UUID (gpt, or pseudo IDs for MBR)
+ *
+ * @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: failed 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: failed 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,
+ &pmbr_pt_idinfo, /* always after GPT */
+ &mac_pt_idinfo,
+ &ultrix_pt_idinfo,
+ &bsd_pt_idinfo,
+ &unixware_pt_idinfo,
+ &solaris_x86_pt_idinfo,
+ &minix_pt_idinfo,
+ &atari_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 */
+ uint64_t offset; /* begin of the partition table (in bytes) */
+ int nparts; /* number of partitions */
+ blkid_partition parent; /* parent of nested partition table */
+ char id[UUID_STR_LEN]; /* PT identifier (e.g. UUID for GPT) */
+
+ struct list_head t_tabs; /* all tables */
+};
+
+/* exported as opaque type "blkid_partition" */
+struct blkid_struct_partition {
+ uint64_t start; /* begin of the partition (512-bytes sectors) */
+ uint64_t size; /* size of the partitions (512-bytes sectors) */
+
+ int type; /* partition type */
+ char typestr[UUID_STR_LEN]; /* partition type string (GPT and Mac) */
+
+ unsigned long long flags; /* partition flags / attributes */
+
+ int partno; /* partition number */
+ char uuid[UUID_STR_LEN]; /* UUID (when supported by PT), e.g. GPT */
+ unsigned char name[128]; /* Partition in UTF8 name (when supported 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)
+{
+ 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)
+{
+ pr->chains[BLKID_CHAIN_PARTS].flags = flags;
+ return 0;
+}
+
+int blkid_probe_get_partitions_flags(blkid_probe pr)
+{
+ return pr->chains[BLKID_CHAIN_PARTS].flags;
+}
+
+/**
+ * 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)
+{
+ if (tab)
+ tab->nparts++;
+}
+
+static void unref_parttable(blkid_parttable tab)
+{
+ if (!tab)
+ return;
+
+ 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(LOWPROBE, ul_debug("partlist reset"));
+}
+
+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(LOWPROBE, ul_debug("parts: initialized partitions list (size=%d)", 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, uint64_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(LOWPROBE, ul_debug("parts: create a new partition table "
+ "(type=%s, offset=%"PRId64")", 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 partitions.
+ */
+ void *tmp = realloc(ls->parts, (ls->nparts_max + 32) *
+ sizeof(struct blkid_struct_partition));
+ if (!tmp)
+ return NULL;
+ ls->parts = tmp;
+ 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, uint64_t start, uint64_t size)
+{
+ blkid_partition par = new_partition(ls, tab);
+
+ if (!par)
+ return NULL;
+
+ par->start = start;
+ par->size = size;
+
+ DBG(LOWPROBE, ul_debug("parts: add partition (start=%"
+ PRIu64 ", size=%" PRIu64 ")",
+ par->start, par->size));
+ return par;
+}
+
+/* can be used 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;
+}
+
+/* can be used to set "parent" for the next nested partition */
+static 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,
+ uint64_t start, uint64_t size)
+{
+ uint64_t pstart;
+ uint64_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;
+ uint64_t off;
+ int rc = BLKID_PROBE_NONE; /* default is nothing */
+
+ if (pr->size <= 0 || (id->minsz && (unsigned)id->minsz > pr->size))
+ goto nothing; /* the device is too small */
+ if (pr->flags & BLKID_FL_NOSCAN_DEV)
+ goto nothing;
+
+ rc = blkid_probe_get_idmag(pr, id, &off, &mag);
+ if (rc != BLKID_PROBE_OK)
+ goto nothing;
+
+ /* final check by probing function */
+ if (id->probefunc) {
+ DBG(LOWPROBE, ul_debug(
+ "%s: ---> call probefunc()", id->name));
+ errno = 0;
+ rc = id->probefunc(pr, mag);
+ if (rc < 0) {
+ /* reset after error */
+ reset_partlist(blkid_probe_get_partlist(pr));
+ if (chn && !chn->binary)
+ blkid_probe_chain_reset_values(pr, chn);
+ DBG(LOWPROBE, ul_debug("%s probefunc failed, rc %d",
+ id->name, rc));
+ }
+ if (rc == BLKID_PROBE_OK && mag && chn && !chn->binary)
+ rc = blkid_probe_set_magic(pr, off, mag->len,
+ (const unsigned char *) mag->magic);
+
+ DBG(LOWPROBE, ul_debug("%s: <--- (rc = %d)", id->name, rc));
+ }
+
+ return rc;
+
+nothing:
+ return BLKID_PROBE_NONE;
+}
+
+/*
+ * The blkid_do_probe() backend.
+ */
+static int partitions_probe(blkid_probe pr, struct blkid_chain *chn)
+{
+ int rc = BLKID_PROBE_NONE;
+ size_t i;
+
+ if (!pr || chn->idx < -1)
+ return -EINVAL;
+
+ blkid_probe_chain_reset_values(pr, chn);
+
+ if (pr->flags & BLKID_FL_NOSCAN_DEV)
+ return BLKID_PROBE_NONE;
+
+ if (chn->binary)
+ partitions_init_data(chn);
+
+ if (!pr->wipe_size && (pr->prob_flags & BLKID_PROBE_FL_IGNORE_PT))
+ goto details_only;
+
+ DBG(LOWPROBE, ul_debug("--> starting probing loop [PARTS idx=%d]",
+ 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 */
+ rc = idinfo_probe(pr, idinfos[i], chn);
+ if (rc < 0)
+ break;
+ if (rc != BLKID_PROBE_OK)
+ continue;
+
+ name = idinfos[i]->name;
+
+ if (!chn->binary)
+ /*
+ * Non-binary interface, set generic variables. Note
+ * that the another variables could be set in prober
+ * functions.
+ */
+ blkid_probe_set_value(pr, "PTTYPE",
+ (const unsigned char *) name,
+ strlen(name) + 1);
+
+ DBG(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [PARTS idx=%d]",
+ name, chn->idx));
+ rc = BLKID_PROBE_OK;
+ break;
+ }
+
+ if (rc != BLKID_PROBE_OK) {
+ DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed=%d) [PARTS idx=%d]",
+ rc, chn->idx));
+ }
+
+details_only:
+ /*
+ * Gather PART_ENTRY_* values if the current device is a partition.
+ */
+ if ((rc == BLKID_PROBE_OK || rc == BLKID_PROBE_NONE) && !chn->binary &&
+ (blkid_partitions_get_flags(pr) & BLKID_PARTS_ENTRY_DETAILS)) {
+
+ int xrc = blkid_partitions_probe_partition(pr);
+
+ /* partition entry probing is optional, and "not-found" from
+ * this sub-probing must not to overwrite previous success. */
+ if (xrc < 0)
+ rc = xrc; /* always propagate errors */
+ else if (rc == BLKID_PROBE_NONE)
+ rc = xrc;
+ }
+
+ DBG(LOWPROBE, ul_debug("partitions probe done [rc=%d]", rc));
+ 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;
+ blkid_partlist ls;
+ uint64_t sz, off;
+
+ DBG(LOWPROBE, ul_debug(
+ "parts: ----> %s subprobe requested)",
+ id->name));
+
+ if (!pr || !parent || !parent->size)
+ return -EINVAL;
+ if (pr->flags & BLKID_FL_NOSCAN_DEV)
+ return BLKID_PROBE_NONE;
+
+ /* range defined by parent */
+ sz = parent->size << 9;
+ off = parent->start << 9;
+
+ if (off < pr->off || pr->off + pr->size < off + sz) {
+ DBG(LOWPROBE, ul_debug(
+ "ERROR: parts: <---- '%s' subprobe: overflow detected.",
+ id->name));
+ return -ENOSPC;
+ }
+
+ /* create private prober */
+ prc = blkid_clone_probe(pr);
+ if (!prc)
+ return -ENOMEM;
+
+ 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(LOWPROBE, ul_debug(
+ "parts: <---- %s subprobe done (rc=%d)",
+ id->name, rc));
+
+ return rc;
+}
+
+static int blkid_partitions_probe_partition(blkid_probe pr)
+{
+ blkid_probe disk_pr = NULL;
+ blkid_partlist ls;
+ blkid_partition par;
+ dev_t devno;
+
+ DBG(LOWPROBE, ul_debug("parts: start probing for partition entry"));
+
+ if (pr->flags & BLKID_FL_NOSCAN_DEV)
+ goto nothing;
+
+ 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)
+ goto nothing;
+ else {
+ 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",
+ (const unsigned char *) v, strlen(v) + 1);
+ }
+
+ v = blkid_partition_get_name(par);
+ if (v)
+ blkid_probe_set_value(pr, "PART_ENTRY_NAME",
+ (const unsigned char *) v, strlen(v) + 1);
+
+ v = blkid_partition_get_uuid(par);
+ if (v)
+ blkid_probe_set_value(pr, "PART_ENTRY_UUID",
+ (const unsigned char *) v, strlen(v) + 1);
+
+ /* type */
+ v = blkid_partition_get_type_string(par);
+ if (v)
+ blkid_probe_set_value(pr, "PART_ENTRY_TYPE",
+ (const 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",
+ (intmax_t)blkid_partition_get_start(par));
+ blkid_probe_sprintf_value(pr, "PART_ENTRY_SIZE", "%jd",
+ (intmax_t)blkid_partition_get_size(par));
+
+ blkid_probe_sprintf_value(pr, "PART_ENTRY_DISK", "%u:%u",
+ major(disk), minor(disk));
+ }
+
+ DBG(LOWPROBE, ul_debug("parts: end probing for partition entry [success]"));
+ return BLKID_PROBE_OK;
+
+nothing:
+ DBG(LOWPROBE, ul_debug("parts: end probing for partition entry [nothing]"));
+ return BLKID_PROBE_NONE;
+
+
+}
+
+/*
+ * 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,
+ uint64_t offset, uint64_t size)
+{
+ blkid_probe prc = NULL;
+ blkid_partlist ls = NULL;
+ uint64_t start, end;
+ int nparts, i, rc = 0;
+
+ DBG(LOWPROBE, ul_debug(
+ "=> checking if off=%"PRIu64" size=%"PRIu64" covered by PT",
+ offset, size));
+
+ if (pr->flags & BLKID_FL_NOSCAN_DEV)
+ goto done;
+
+ 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(LOWPROBE, ul_debug("partition #%d overflows "
+ "device (off=%" PRId64 " size=%" PRId64 ")",
+ 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(LOWPROBE, ul_debug("<= %s covered by PT", rc ? "IS" : "NOT"));
+ return rc;
+}
+
+/**
+ * blkid_known_pttype:
+ * @pttype: partition 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_partitions_get_name:
+ * @idx: number >= 0
+ * @name: returns name of a supported partition
+ *
+ * Since: 2.30
+ *
+ * Returns: -1 if @idx is out of range, or 0 on success.
+ */
+int blkid_partitions_get_name(const size_t idx, const char **name)
+{
+ if (idx < ARRAY_SIZE(idinfos)) {
+ *name = idinfos[idx]->name;
+ return 0;
+ }
+ return -1;
+}
+
+/**
+ * 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->nparts;
+}
+
+/**
+ * blkid_partlist_get_table:
+ * @ls: partitions list
+ *
+ * Returns: top-level partition table or NULL if there is not a partition table
+ * on the device.
+ */
+blkid_parttable blkid_partlist_get_table(blkid_partlist ls)
+{
+ if (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 (n < 0 || n >= ls->nparts)
+ return NULL;
+
+ return &ls->parts[n];
+}
+
+blkid_partition blkid_partlist_get_partition_by_start(blkid_partlist ls, uint64_t start)
+{
+ int i, nparts;
+ blkid_partition par;
+
+ nparts = blkid_partlist_numof_partitions(ls);
+ for (i = 0; i < nparts; i++) {
+ par = blkid_partlist_get_partition(ls, i);
+ if ((uint64_t) blkid_partition_get_start(par) == start)
+ return par;
+ }
+ return NULL;
+}
+
+/**
+ * blkid_partlist_get_partition_by_partno
+ * @ls: partitions list
+ * @n: the partition number (e.g. 'N' from sda'N')
+ *
+ * This does not assume any order of the input blkid_partlist. And correctly
+ * handles "out of order" partition tables. partition N is located after
+ * partition N+1 on the disk.
+ *
+ * Returns: partition object or NULL in case or error.
+ */
+blkid_partition blkid_partlist_get_partition_by_partno(blkid_partlist ls, int n)
+{
+ int i, nparts;
+ blkid_partition par;
+
+ nparts = blkid_partlist_numof_partitions(ls);
+ for (i = 0; i < nparts; i++) {
+ par = blkid_partlist_get_partition(ls, i);
+ if (n == blkid_partition_get_partno(par))
+ return par;
+ }
+ return NULL;
+}
+
+
+/**
+ * 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 path_cxt *pc;
+ uint64_t start = 0, size;
+ int i, rc, partno = 0;
+
+ DBG(LOWPROBE, ul_debug("trying to convert devno 0x%llx to partition",
+ (long long) devno));
+
+
+ pc = ul_new_sysfs_path(devno, NULL, NULL);
+ if (!pc) {
+ DBG(LOWPROBE, ul_debug("failed t init sysfs context"));
+ return NULL;
+ }
+ rc = ul_path_read_u64(pc, &size, "size");
+ if (!rc) {
+ rc = ul_path_read_u64(pc, &start, "start");
+ if (rc) {
+ /* try to get partition number from DM uuid.
+ */
+ char *uuid = NULL, *tmp, *prefix;
+
+ ul_path_read_string(pc, &uuid, "dm/uuid");
+ tmp = uuid;
+ prefix = uuid ? strsep(&tmp, "-") : NULL;
+
+ if (prefix && strncasecmp(prefix, "part", 4) == 0) {
+ char *end = NULL;
+
+ errno = 0;
+ partno = strtol(prefix + 4, &end, 10);
+ if (errno || prefix == end || (end && *end))
+ partno = 0;
+ else
+ rc = 0; /* success */
+ }
+ free(uuid);
+ }
+ }
+
+ ul_unref_path(pc);
+
+ if (rc)
+ return NULL;
+
+ if (partno) {
+ DBG(LOWPROBE, ul_debug("mapped by DM, using partno %d", 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 relation between 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 (size == (uint64_t)blkid_partition_get_size(par) ||
+ (blkid_partition_is_extended(par) && size <= 1024ULL))
+ return par;
+
+ }
+ return NULL;
+ }
+
+ DBG(LOWPROBE, ul_debug("searching by offset/size"));
+
+ for (i = 0; i < ls->nparts; i++) {
+ blkid_partition par = &ls->parts[i];
+
+ if ((uint64_t)blkid_partition_get_start(par) == start &&
+ (uint64_t)blkid_partition_get_size(par) == size)
+ return par;
+
+ /* exception for extended dos partitions */
+ if ((uint64_t)blkid_partition_get_start(par) == start &&
+ blkid_partition_is_extended(par) && size <= 1024ULL)
+ return par;
+
+ }
+
+ DBG(LOWPROBE, ul_debug("not found partition for device"));
+ return NULL;
+}
+
+
+int blkid_parttable_set_uuid(blkid_parttable tab, const unsigned char *id)
+{
+ if (!tab)
+ return -1;
+
+ blkid_unparse_uuid(id, tab->id, sizeof(tab->id));
+ return 0;
+}
+
+int blkid_parttable_set_id(blkid_parttable tab, const unsigned char *id)
+{
+ if (!tab)
+ return -1;
+
+ xstrncpy(tab->id, (const char *) id, sizeof(tab->id));
+ return 0;
+}
+
+/* set PTUUID variable for non-binary API */
+int blkid_partitions_set_ptuuid(blkid_probe pr, unsigned char *uuid)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ struct blkid_prval *v;
+
+ if (chn->binary || blkid_uuid_is_empty(uuid, 16))
+ return 0;
+
+ v = blkid_probe_assign_value(pr, "PTUUID");
+ if (!v)
+ return -ENOMEM;
+
+ v->len = UUID_STR_LEN;
+ v->data = calloc(1, v->len);
+ if (v->data) {
+ blkid_unparse_uuid(uuid, (char *) v->data, v->len);
+ return 0;
+ }
+
+ blkid_probe_free_value(v);
+ return -ENOMEM;
+}
+
+/* set PTUUID variable for non-binary API for tables where
+ * the ID is just a string */
+int blkid_partitions_strcpy_ptuuid(blkid_probe pr, const char *str)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+ if (chn->binary || !str || !*str)
+ return 0;
+
+ if (!blkid_probe_set_value(pr, "PTUUID", (unsigned char *) str, strlen(str) + 1))
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * blkid_parttable_get_id:
+ * @tab: partition table
+ *
+ * The ID is GPT disk UUID or DOS disk ID (in hex format).
+ *
+ * Returns: partition table ID (for example GPT disk UUID) or NULL
+ */
+const char *blkid_parttable_get_id(blkid_parttable tab)
+{
+ return *tab->id ? tab->id : NULL;
+}
+
+
+int blkid_partition_set_type(blkid_partition par, int type)
+{
+ 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->type;
+}
+
+/**
+ * blkid_parttable_get_parent:
+ * @tab: partition table
+ *
+ * Returns: parent for nested partition tables or NULL.
+ */
+blkid_partition blkid_parttable_get_parent(blkid_parttable tab)
+{
+ return tab->parent;
+}
+
+/**
+ * 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 partition 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 (blkid_loff_t)tab->offset;
+}
+
+/**
+ * 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->tab;
+}
+
+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 == MBR_DOS_EXTENDED_PARTITION ||
+ par->type == MBR_W95_EXTENDED_PARTITION ||
+ par->type == MBR_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;
+
+ ul_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;
+}
+
+int blkid_partition_gen_uuid(blkid_partition par)
+{
+ if (!par || !par->tab || !*par->tab->id)
+ return -1;
+
+ snprintf(par->uuid, sizeof(par->uuid), "%.33s-%02x",
+ par->tab->id, par->partno);
+ 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->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->uuid ? par->uuid : NULL;
+}
+
+/**
+ * blkid_partition_get_partno:
+ * @par: partition
+ *
+ * Returns: proposed partition number (e.g. 'N' from sda'N') or -1 in case of
+ * error. Note that the number is generated by library independently of your OS.
+ */
+int blkid_partition_get_partno(blkid_partition par)
+{
+ return par->partno;
+}
+
+/**
+ * blkid_partition_get_start:
+ * @par: partition
+ *
+ * Be careful if you _not_ probe whole disk:
+ *
+ * 1) the offset is usually 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 relative 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 probe 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 (blkid_loff_t)par->start;
+}
+
+/**
+ * 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 to
+ * 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 inaccessible 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 (blkid_loff_t)par->size;
+}
+
+/**
+ * blkid_partition_get_type:
+ * @par: partition
+ *
+ * Returns: partition type.
+ */
+int blkid_partition_get_type(blkid_partition par)
+{
+ return par->type;
+}
+
+/* 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)
+{
+ set_string((unsigned char *) par->typestr,
+ sizeof(par->typestr), type, len);
+ return 0;
+}
+
+/* Sets partition 'type' for PT where the type is defined by UUID rather
+ * than by number
+ */
+int blkid_partition_set_type_uuid(blkid_partition par, const unsigned char *uuid)
+{
+ 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->typestr ? par->typestr : NULL;
+}
+
+
+int blkid_partition_set_flags(blkid_partition par, unsigned long long flags)
+{
+ 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->flags;
+}
+
diff --git a/libblkid/src/partitions/partitions.h b/libblkid/src/partitions/partitions.h
new file mode 100644
index 0000000..784e0c0
--- /dev/null
+++ b/libblkid/src/partitions/partitions.h
@@ -0,0 +1,74 @@
+#ifndef BLKID_PARTITIONS_H
+#define BLKID_PARTITIONS_H
+
+#include "blkidP.h"
+#include "pt-mbr.h"
+
+extern int blkid_partitions_get_flags(blkid_probe pr);
+
+extern blkid_parttable blkid_partlist_new_parttable(blkid_partlist ls,
+ const char *type, uint64_t offset);
+
+extern int blkid_parttable_set_uuid(blkid_parttable tab, const unsigned char *id);
+extern int blkid_parttable_set_id(blkid_parttable tab, const unsigned char *id);
+
+extern blkid_partition blkid_partlist_add_partition(blkid_partlist ls,
+ blkid_parttable tab,
+ uint64_t start, uint64_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 blkid_partition blkid_partlist_get_partition_by_start(blkid_partlist ls, uint64_t start);
+
+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_partitions_set_ptuuid(blkid_probe pr, unsigned char *uuid);
+extern int blkid_partitions_strcpy_ptuuid(blkid_probe pr, const char *str);
+
+
+extern int blkid_is_nested_dimension(blkid_partition par,
+ uint64_t start, uint64_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_gen_uuid(blkid_partition par);
+
+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 pmbr_pt_idinfo;
+extern const struct blkid_idinfo ultrix_pt_idinfo;
+extern const struct blkid_idinfo atari_pt_idinfo;
+
+#endif /* BLKID_PARTITIONS_H */
diff --git a/libblkid/src/partitions/sgi.c b/libblkid/src/partitions/sgi.c
new file mode 100644
index 0000000..d4f81bb
--- /dev/null
+++ b/libblkid/src/partitions/sgi.c
@@ -0,0 +1,87 @@
+/*
+ * 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"
+#include "pt-sgi.h"
+
+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) {
+ if (errno)
+ return -errno;
+ goto nothing;
+ }
+
+ if (!blkid_probe_verify_csum(pr, sgi_pt_checksum(l), 0)) {
+ DBG(LOWPROBE, ul_debug(
+ "detected corrupted sgi disk label -- ignore"));
+ goto nothing;
+ }
+
+ if (blkid_partitions_need_typeonly(pr))
+ /* caller does not ask for details about partitions */
+ return BLKID_PROBE_OK;
+
+ ls = blkid_probe_get_partlist(pr);
+ if (!ls)
+ goto nothing;
+
+ 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) {
+ 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 BLKID_PROBE_OK;
+
+nothing:
+ return BLKID_PROBE_NONE;
+err:
+ return -ENOMEM;
+}
+
+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/libblkid/src/partitions/solaris_x86.c b/libblkid/src/partitions/solaris_x86.c
new file mode 100644
index 0000000..df1def5
--- /dev/null
+++ b/libblkid/src/partitions/solaris_x86.c
@@ -0,0 +1,154 @@
+/*
+ * 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 can be used 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 partitioning
+ * 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; /* partition */
+ 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) {
+ if (errno)
+ return -errno;
+ goto nothing;
+ }
+
+ if (le32_to_cpu(l->v_version) != 1) {
+ DBG(LOWPROBE, ul_debug(
+ "WARNING: unsupported solaris x86 version %d, ignore",
+ le32_to_cpu(l->v_version)));
+ goto nothing;
+ }
+
+ if (blkid_partitions_need_typeonly(pr))
+ /* caller does not ask for details about partitions */
+ return BLKID_PROBE_OK;
+
+ ls = blkid_probe_get_partlist(pr);
+ if (!ls)
+ goto nothing;
+
+ 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(LOWPROBE, ul_debug(
+ "WARNING: solaris partition (%d) overflow "
+ "detected, ignore", 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 BLKID_PROBE_OK;
+
+nothing:
+ return BLKID_PROBE_NONE;
+err:
+ return -ENOMEM;
+}
+
+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/libblkid/src/partitions/sun.c b/libblkid/src/partitions/sun.c
new file mode 100644
index 0000000..ce24364
--- /dev/null
+++ b/libblkid/src/partitions/sun.c
@@ -0,0 +1,125 @@
+/*
+ * 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 "pt-sun.h"
+#include "partitions.h"
+
+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;
+ uint64_t spc;
+ int i, use_vtoc;
+
+ l = (struct sun_disklabel *) blkid_probe_get_sector(pr, 0);
+ if (!l) {
+ if (errno)
+ return -errno;
+ goto nothing;
+ }
+
+ if (!blkid_probe_verify_csum(pr, sun_pt_checksum(l), 0)) {
+ DBG(LOWPROBE, ul_debug(
+ "detected corrupted sun disk label -- ignore"));
+ goto nothing;
+ }
+
+ if (blkid_partitions_need_typeonly(pr))
+ /* caller does not ask for details about partitions */
+ return BLKID_PROBE_OK;
+
+ ls = blkid_probe_get_partlist(pr);
+ if (!ls)
+ goto nothing;
+
+ tab = blkid_partlist_new_parttable(ls, "sun", 0);
+ if (!tab)
+ goto err;
+
+ /* sectors per cylinder (partition offset is in cylinders...) */
+ spc = (uint64_t) be16_to_cpu(l->nhead) * be16_to_cpu(l->nsect);
+
+ DBG(LOWPROBE, ul_debug("Sun VTOC sanity=%u version=%u nparts=%u",
+ 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,
+ * allow 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++) {
+
+ uint64_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 BLKID_PROBE_OK;
+
+nothing:
+ return BLKID_PROBE_NONE;
+err:
+ return -ENOMEM;
+}
+
+
+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/libblkid/src/partitions/ultrix.c b/libblkid/src/partitions/ultrix.c
new file mode 100644
index 0000000..9c060be
--- /dev/null
+++ b/libblkid/src/partitions/ultrix.c
@@ -0,0 +1,99 @@
+/*
+ * 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) {
+ if (errno)
+ return -errno;
+ 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 BLKID_PROBE_OK;
+
+ ls = blkid_probe_get_partlist(pr);
+ if (!ls)
+ goto nothing;
+
+ 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 BLKID_PROBE_OK;
+nothing:
+ return BLKID_PROBE_NONE;
+err:
+ return -ENOMEM;
+}
+
+const struct blkid_idinfo ultrix_pt_idinfo =
+{
+ .name = "ultrix",
+ .probefunc = probe_ultrix_pt,
+ .magics = BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/src/partitions/unixware.c b/libblkid/src/partitions/unixware.c
new file mode 100644
index 0000000..845924e
--- /dev/null
+++ b/libblkid/src/partitions/unixware.c
@@ -0,0 +1,197 @@
+/*
+ * 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 interesting 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) {
+ if (errno)
+ return -errno;
+ 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 BLKID_PROBE_OK;
+
+ ls = blkid_probe_get_partlist(pr);
+ if (!ls)
+ goto nothing;
+
+ 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(LOWPROBE, ul_debug(
+ "WARNING: unixware partition (%d) overflow "
+ "detected, ignore", 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 BLKID_PROBE_OK;
+
+nothing:
+ return BLKID_PROBE_NONE;
+err:
+ return -ENOMEM;
+}
+
+
+/*
+ * 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/libblkid/src/probe.c b/libblkid/src/probe.c
new file mode 100644
index 0000000..fbf504a
--- /dev/null
+++ b/libblkid/src/probe.c
@@ -0,0 +1,2413 @@
+/*
+ * 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) == BLKID_PROBE_OK)
+ * ... 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_LINUX_BLKZONED_H
+#include <linux/blkzoned.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <limits.h>
+#ifdef HAVE_OPAL_GET_STATUS
+#include <linux/sed-opal.h>
+#endif
+
+#include "blkidP.h"
+#include "all-io.h"
+#include "sysfs.h"
+#include "strutils.h"
+#include "list.h"
+#include "fileutils.h"
+
+/*
+ * 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_values(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;
+
+ pr = calloc(1, sizeof(struct blkid_struct_probe));
+ if (!pr)
+ return NULL;
+
+ DBG(LOWPROBE, ul_debug("allocate a new probe"));
+
+ /* 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);
+ INIT_LIST_HEAD(&pr->values);
+ INIT_LIST_HEAD(&pr->hints);
+ return pr;
+}
+
+/*
+ * Clone @parent, the new clone shares all, but except:
+ *
+ * - probing result
+ * - buffers 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(LOWPROBE, ul_debug("allocate a probe clone"));
+
+ 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->zone_size = parent->zone_size;
+ pr->parent = parent;
+
+ pr->flags &= ~BLKID_FL_PRIVATE_FD;
+
+ 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;
+ blkid_probe pr = NULL;
+
+ fd = open(filename, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ 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:
+ 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);
+ ch->fltr = NULL;
+ }
+
+ if ((pr->flags & BLKID_FL_PRIVATE_FD) && pr->fd >= 0)
+ close(pr->fd);
+ blkid_probe_reset_buffers(pr);
+ blkid_probe_reset_values(pr);
+ blkid_probe_reset_hints(pr);
+ blkid_free_probe(pr->disk_probe);
+
+ DBG(LOWPROBE, ul_debug("free probe"));
+ free(pr);
+}
+
+void blkid_probe_free_value(struct blkid_prval *v)
+{
+ if (!v)
+ return;
+
+ list_del(&v->prvals);
+ free(v->data);
+
+ DBG(LOWPROBE, ul_debug(" free value %s", v->name));
+ free(v);
+}
+
+/*
+ * Removes chain values from probing result.
+ */
+void blkid_probe_chain_reset_values(blkid_probe pr, struct blkid_chain *chn)
+{
+
+ struct list_head *p, *pnext;
+
+ if (list_empty(&pr->values))
+ return;
+
+ DBG(LOWPROBE, ul_debug("Resetting %s values", chn->driver->name));
+
+ list_for_each_safe(p, pnext, &pr->values) {
+ struct blkid_prval *v = list_entry(p,
+ struct blkid_prval, prvals);
+
+ if (v->chain == chn)
+ blkid_probe_free_value(v);
+ }
+}
+
+static void blkid_probe_chain_reset_position(struct blkid_chain *chn)
+{
+ chn->idx = -1;
+}
+
+/*
+ * Move chain values from probing result to @vals
+ */
+int blkid_probe_chain_save_values(blkid_probe pr, struct blkid_chain *chn,
+ struct list_head *vals)
+{
+ struct list_head *p, *pnext;
+ struct blkid_prval *v;
+
+ DBG(LOWPROBE, ul_debug("saving %s values", chn->driver->name));
+
+ list_for_each_safe(p, pnext, &pr->values) {
+
+ v = list_entry(p, struct blkid_prval, prvals);
+ if (v->chain != chn)
+ continue;
+
+ list_del_init(&v->prvals);
+ list_add_tail(&v->prvals, vals);
+ }
+ return 0;
+}
+
+/*
+ * Appends values from @vals to the probing result
+ */
+void blkid_probe_append_values_list(blkid_probe pr, struct list_head *vals)
+{
+ DBG(LOWPROBE, ul_debug("appending values"));
+
+ list_splice(vals, &pr->values);
+ INIT_LIST_HEAD(vals);
+}
+
+
+void blkid_probe_free_values_list(struct list_head *vals)
+{
+ if (!vals)
+ return;
+
+ DBG(LOWPROBE, ul_debug("freeing values list"));
+
+ while (!list_empty(vals)) {
+ struct blkid_prval *v = list_entry(vals->next, struct blkid_prval, prvals);
+ blkid_probe_free_value(v);
+ }
+}
+
+struct blkid_chain *blkid_probe_get_chain(blkid_probe pr)
+{
+ return pr->cur_chain;
+}
+
+static const char *blkid_probe_get_probername(blkid_probe pr)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+ if (chn && chn->idx >= 0 && (unsigned)chn->idx < chn->driver->nidinfos)
+ return chn->driver->idinfos[chn->idx]->name;
+
+ return NULL;
+}
+
+void *blkid_probe_get_binary_data(blkid_probe pr, struct blkid_chain *chn)
+{
+ int rc, org_prob_flags;
+ struct blkid_chain *org_chn;
+
+ /* 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(LOWPROBE, ul_debug("returning %s binary data", 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;
+
+ blkid_probe_reset_values(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(LOWPROBE, ul_debug("%d: %s: %s",
+ 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 (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(LOWPROBE, ul_debug("probing filter inverted"));
+ /* 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 (has) {
+ if (flag & BLKID_FLTR_NOTIN)
+ blkid_bmp_set_item(fltr, i);
+ } else if (flag & BLKID_FLTR_ONLYIN)
+ blkid_bmp_set_item(fltr, i);
+ }
+
+ DBG(LOWPROBE, ul_debug("%s: a new probing type-filter initialized",
+ chn->driver->name));
+ /* blkid_probe_dump_filter(pr, chain); */
+ return 0;
+}
+
+static struct blkid_bufinfo *read_buffer(blkid_probe pr, uint64_t real_off, uint64_t len)
+{
+ ssize_t ret;
+ struct blkid_bufinfo *bf = NULL;
+
+ if (lseek(pr->fd, real_off, SEEK_SET) == (off_t) -1) {
+ errno = 0;
+ return NULL;
+ }
+
+ /* someone trying to overflow some buffers? */
+ if (len > ULONG_MAX - sizeof(struct blkid_bufinfo)) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* allocate info and space for data by one malloc call */
+ bf = calloc(1, sizeof(struct blkid_bufinfo) + len);
+ if (!bf) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ bf->data = ((unsigned char *) bf) + sizeof(struct blkid_bufinfo);
+ bf->len = len;
+ bf->off = real_off;
+ INIT_LIST_HEAD(&bf->bufs);
+
+ DBG(LOWPROBE, ul_debug("\tread: off=%"PRIu64" len=%"PRIu64"",
+ real_off, len));
+
+ ret = read(pr->fd, bf->data, len);
+ if (ret != (ssize_t) len) {
+ DBG(LOWPROBE, ul_debug("\tread failed: %m"));
+ free(bf);
+
+ /* I/O errors on CDROMs are non-fatal to work with hybrid
+ * audio+data disks */
+ if (ret >= 0 || blkid_probe_is_cdrom(pr) || blkdid_probe_is_opal_locked(pr))
+ errno = 0;
+#ifdef HAVE_OPAL_GET_STATUS
+ else {
+ struct opal_status st = { };
+
+ /* If the device is locked with OPAL, we'll fail to read with I/O
+ * errors when probing deep into the block device. Do not return
+ * an error, so that we can move on to different types of checks.*/
+ ret = ioctl(pr->fd, IOC_OPAL_GET_STATUS, &st);
+ if (ret == 0 && (st.flags & OPAL_FL_LOCKED)) {
+ pr->flags |= BLKID_FL_OPAL_LOCKED;
+ errno = 0;
+ }
+ }
+#endif
+
+ return NULL;
+ }
+
+ return bf;
+}
+
+/*
+ * Search in buffers we already have in memory
+ */
+static struct blkid_bufinfo *get_cached_buffer(blkid_probe pr, uint64_t off, uint64_t len)
+{
+ uint64_t real_off = pr->off + off;
+ struct list_head *p;
+
+ list_for_each(p, &pr->buffers) {
+ struct blkid_bufinfo *x =
+ list_entry(p, struct blkid_bufinfo, bufs);
+
+ if (real_off >= x->off && real_off + len <= x->off + x->len) {
+ DBG(BUFFER, ul_debug("\treuse: off=%"PRIu64" len=%"PRIu64" (for off=%"PRIu64" len=%"PRIu64")",
+ x->off, x->len, real_off, len));
+ return x;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Zeroize in-memory data in already read buffer. The next blkid_probe_get_buffer()
+ * will return modified buffer. This is usable when you want to call the same probing
+ * function more than once and hide previously detected magic strings.
+ *
+ * See blkid_probe_hide_range().
+ */
+static int hide_buffer(blkid_probe pr, uint64_t off, uint64_t len)
+{
+ uint64_t real_off = pr->off + off;
+ struct list_head *p;
+ int ct = 0;
+
+ if (UINT64_MAX - len < off) {
+ DBG(BUFFER, ul_debug("\t hide-buffer overflow (ignore)"));
+ return -EINVAL;
+ }
+
+ list_for_each(p, &pr->buffers) {
+ struct blkid_bufinfo *x =
+ list_entry(p, struct blkid_bufinfo, bufs);
+ unsigned char *data;
+
+ if (real_off >= x->off && real_off + len <= x->off + x->len) {
+
+ assert(x->off <= real_off);
+ assert(x->off + x->len >= real_off + len);
+
+ data = real_off ? x->data + (real_off - x->off) : x->data;
+
+ DBG(BUFFER, ul_debug("\thiding: off=%"PRIu64" len=%"PRIu64,
+ off, len));
+ memset(data, 0, len);
+ ct++;
+ }
+ }
+ return ct == 0 ? -EINVAL : 0;
+}
+
+
+/*
+ * Note that @off is offset within probing area, the probing area is defined by
+ * pr->off and pr->size.
+ */
+unsigned char *blkid_probe_get_buffer(blkid_probe pr, uint64_t off, uint64_t len)
+{
+ struct blkid_bufinfo *bf = NULL;
+ uint64_t real_off = pr->off + off;
+
+ /*
+ DBG(BUFFER, ul_debug("\t>>>> off=%ju, real-off=%ju (probe <%ju..%ju>, len=%ju",
+ off, real_off, pr->off, pr->off + pr->size, len));
+ */
+ if (pr->size == 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (UINT64_MAX - len < off || UINT64_MAX - len < real_off) {
+ DBG(BUFFER, ul_debug("\t read-buffer overflow (ignore)"));
+ return NULL;
+ }
+
+ if (len == 0
+ || (!S_ISCHR(pr->mode) && (pr->size < off || pr->size < len))
+ || (!S_ISCHR(pr->mode) && (pr->off + pr->size < real_off + len))) {
+ DBG(BUFFER, ul_debug("\t read-buffer out of probing area (ignore)"));
+ errno = 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
+ * begin of the device.
+ */
+ return blkid_probe_get_buffer(pr->parent,
+ pr->off + off - pr->parent->off, len);
+ }
+
+ /* try buffers we already have in memory or read from device */
+ bf = get_cached_buffer(pr, off, len);
+ if (!bf) {
+ bf = read_buffer(pr, real_off, len);
+ if (!bf)
+ return NULL;
+
+ list_add_tail(&bf->bufs, &pr->buffers);
+ }
+
+ assert(bf->off <= real_off);
+ assert(bf->off + bf->len >= real_off + len);
+
+ errno = 0;
+ return real_off ? bf->data + (real_off - bf->off) : bf->data;
+}
+
+/**
+ * blkid_probe_reset_buffers:
+ * @pr: prober
+ *
+ * libblkid reuse all already read buffers from the device. The buffers may be
+ * modified by blkid_probe_hide_range(). This function reset and free all
+ * cached buffers. The next blkid_do_probe() will read all data from the
+ * device.
+ *
+ * Since: 2.31
+ *
+ * Returns: <0 in case of failure, or 0 on success.
+ */
+int blkid_probe_reset_buffers(blkid_probe pr)
+{
+ uint64_t ct = 0, len = 0;
+
+ pr->flags &= ~BLKID_FL_MODIF_BUFF;
+
+ if (list_empty(&pr->buffers))
+ return 0;
+
+ DBG(BUFFER, ul_debug("Resetting probing buffers"));
+
+ while (!list_empty(&pr->buffers)) {
+ struct blkid_bufinfo *bf = list_entry(pr->buffers.next,
+ struct blkid_bufinfo, bufs);
+ ct++;
+ len += bf->len;
+ list_del(&bf->bufs);
+
+ DBG(BUFFER, ul_debug(" remove buffer: [off=%"PRIu64", len=%"PRIu64"]",
+ bf->off, bf->len));
+ free(bf);
+ }
+
+ DBG(LOWPROBE, ul_debug(" buffers summary: %"PRIu64" bytes by %"PRIu64" read() calls",
+ len, ct));
+
+ INIT_LIST_HEAD(&pr->buffers);
+
+ return 0;
+}
+
+/**
+ * blkid_probe_hide_range:
+ * @pr: prober
+ * @off: start of the range
+ * @len: size of the range
+ *
+ * This function modifies in-memory cached data from the device. The specified
+ * range is zeroized. This is usable together with blkid_probe_step_back().
+ * The next blkid_do_probe() will not see specified area.
+ *
+ * Note that this is usable for already (by library) read data, and this
+ * function is not a way how to hide any large areas on your device.
+ *
+ * The function blkid_probe_reset_buffers() reverts all.
+ *
+ * Since: 2.31
+ *
+ * Returns: <0 in case of failure, or 0 on success.
+ */
+int blkid_probe_hide_range(blkid_probe pr, uint64_t off, uint64_t len)
+{
+ int rc = hide_buffer(pr, off, len);
+
+ if (rc == 0)
+ pr->flags |= BLKID_FL_MODIF_BUFF;
+ return rc;
+}
+
+
+static void blkid_probe_reset_values(blkid_probe pr)
+{
+ if (list_empty(&pr->values))
+ return;
+
+ DBG(LOWPROBE, ul_debug("resetting results"));
+
+ while (!list_empty(&pr->values)) {
+ struct blkid_prval *v = list_entry(pr->values.next,
+ struct blkid_prval, prvals);
+ blkid_probe_free_value(v);
+ }
+
+ INIT_LIST_HEAD(&pr->values);
+}
+
+/*
+ * Small devices need a special care.
+ */
+int blkid_probe_is_tiny(blkid_probe pr)
+{
+ return (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->flags & BLKID_FL_CDROM_DEV);
+}
+
+int blkdid_probe_is_opal_locked(blkid_probe pr)
+{
+ return (pr->flags & BLKID_FL_OPAL_LOCKED);
+}
+
+#ifdef CDROM_GET_CAPABILITY
+
+static int is_sector_readable(int fd, uint64_t sector)
+{
+ char buf[512];
+ ssize_t sz;
+
+ if (lseek(fd, sector * 512, SEEK_SET) == (off_t) -1)
+ goto failed;
+
+ sz = read(fd, buf, sizeof(buf));
+ if (sz != (ssize_t) sizeof(buf))
+ goto failed;
+
+ return 1;
+failed:
+ DBG(LOWPROBE, ul_debug("CDROM: read sector %"PRIu64" failed %m", sector));
+ errno = 0;
+ return 0;
+}
+
+/*
+ * Linux kernel reports (BLKGETSIZE) cdrom device size greater than area
+ * readable by read(2). We have to reduce the probing area to avoid unwanted
+ * I/O errors in probing functions. It seems that unreadable are always last 2
+ * or 3 CD blocks (CD block size is 2048 bytes, it means 12 in 512-byte
+ * sectors). Linux kernel reports (CDROM_LAST_WRITTEN) also location of last
+ * written block, so we will reduce size based on it too.
+ */
+static void cdrom_size_correction(blkid_probe pr, uint64_t last_written)
+{
+ uint64_t n, nsectors = pr->size >> 9;
+
+ if (last_written && nsectors > ((last_written+1) << 2))
+ nsectors = (last_written+1) << 2;
+
+ for (n = nsectors - 12; n < nsectors; n++) {
+ if (!is_sector_readable(pr->fd, n))
+ goto failed;
+ }
+
+ DBG(LOWPROBE, ul_debug("CDROM: full size available"));
+ return;
+failed:
+ /* 'n' is the failed sector, reduce device size to n-1; */
+ DBG(LOWPROBE, ul_debug("CDROM: reduce size from %ju to %ju.",
+ (uintmax_t) pr->size,
+ (uintmax_t) n << 9));
+ pr->size = n << 9;
+}
+
+#endif
+
+/**
+ * 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, resets
+ * the current probing, and close previously associated device (if open by
+ * libblkid).
+ *
+ * If @fd is < 0 than only resets the prober and returns 1. Note that
+ * blkid_reset_probe() keeps the device associated with the prober, but
+ * blkid_probe_set_device() does complete reset.
+ *
+ * Returns: -1 in case of failure, 0 on success and 1 on reset.
+ */
+int blkid_probe_set_device(blkid_probe pr, int fd,
+ blkid_loff_t off, blkid_loff_t size)
+{
+ struct stat sb;
+ uint64_t devsiz = 0;
+ char *dm_uuid = NULL;
+ int is_floppy = 0;
+
+ blkid_reset_probe(pr);
+ blkid_probe_reset_buffers(pr);
+
+ if ((pr->flags & BLKID_FL_PRIVATE_FD) && pr->fd >= 0)
+ close(pr->fd);
+
+ if (pr->disk_probe) {
+ blkid_free_probe(pr->disk_probe);
+ pr->disk_probe = NULL;
+ }
+
+ 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 = (uint64_t) 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;
+ pr->zone_size = 0;
+
+ if (fd < 0)
+ return 1;
+
+
+#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)) {
+ errno = EINVAL;
+ goto err;
+ }
+
+ pr->mode = sb.st_mode;
+ if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode))
+ pr->devno = sb.st_rdev;
+
+ if (S_ISBLK(sb.st_mode)) {
+ if (blkdev_get_size(fd, (unsigned long long *) &devsiz)) {
+ DBG(LOWPROBE, ul_debug("failed to get device size"));
+ goto err;
+ }
+ } else if (S_ISCHR(sb.st_mode)) {
+ char buf[PATH_MAX];
+
+ if (!sysfs_chrdev_devno_to_devname(sb.st_rdev, buf, sizeof(buf))
+ || strncmp(buf, "ubi", 3) != 0) {
+ DBG(LOWPROBE, ul_debug("no UBI char device"));
+ errno = EINVAL;
+ goto err;
+ }
+ devsiz = 1; /* UBI devices are char... */
+ } else if (S_ISREG(sb.st_mode))
+ devsiz = sb.st_size; /* regular file */
+
+ pr->size = size ? (uint64_t)size : devsiz;
+
+ if (off && size == 0)
+ /* only offset without size specified */
+ pr->size -= (uint64_t) off;
+
+ if (pr->off + pr->size > devsiz) {
+ DBG(LOWPROBE, ul_debug("area specified by offset and size is bigger than device"));
+ errno = EINVAL;
+ goto err;
+ }
+
+ if (pr->size <= 1440 * 1024 && !S_ISCHR(sb.st_mode))
+ pr->flags |= BLKID_FL_TINY_DEV;
+
+#ifdef FDGETFDCSTAT
+ if (S_ISBLK(sb.st_mode)) {
+ /*
+ * Re-open without O_NONBLOCK for floppy device.
+ *
+ * Since kernel commit c7e9d0020361f4308a70cdfd6d5335e273eb8717
+ * floppy drive works bad when opened with O_NONBLOCK.
+ */
+ struct floppy_fdc_state flst;
+
+ if (ioctl(fd, FDGETFDCSTAT, &flst) >= 0) {
+ int flags = fcntl(fd, F_GETFL, 0);
+
+ if (flags < 0)
+ goto err;
+ if (flags & O_NONBLOCK) {
+ flags &= ~O_NONBLOCK;
+
+ fd = ul_reopen(fd, flags | O_CLOEXEC);
+ if (fd < 0)
+ goto err;
+
+ pr->flags |= BLKID_FL_PRIVATE_FD;
+ pr->fd = fd;
+ }
+ is_floppy = 1;
+ }
+ errno = 0;
+ }
+#endif
+ if (S_ISBLK(sb.st_mode) &&
+ !is_floppy &&
+ sysfs_devno_is_dm_private(sb.st_rdev, &dm_uuid)) {
+ DBG(LOWPROBE, ul_debug("ignore private device mapper device"));
+ pr->flags |= BLKID_FL_NOSCAN_DEV;
+ }
+
+#ifdef CDROM_GET_CAPABILITY
+ else if (S_ISBLK(sb.st_mode) &&
+ !blkid_probe_is_tiny(pr) &&
+ !dm_uuid &&
+ !is_floppy &&
+ blkid_probe_is_wholedisk(pr)) {
+
+ long last_written = 0;
+
+ /*
+ * pktcdvd.ko accepts only these ioctls:
+ * CDROMEJECT CDROMMULTISESSION CDROMREADTOCENTRY
+ * CDROM_LAST_WRITTEN CDROM_SEND_PACKET SCSI_IOCTL_SEND_COMMAND
+ * So CDROM_GET_CAPABILITY cannot be used for detecting pktcdvd
+ * devices. But CDROM_GET_CAPABILITY and CDROM_DRIVE_STATUS are
+ * fast so use them for detecting if medium is present. In any
+ * case use last written block form CDROM_LAST_WRITTEN.
+ */
+
+ if (ioctl(fd, CDROM_GET_CAPABILITY, NULL) >= 0) {
+# ifdef CDROM_DRIVE_STATUS
+ switch (ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT)) {
+ case CDS_TRAY_OPEN:
+ case CDS_NO_DISC:
+ errno = ENOMEDIUM;
+ goto err;
+ }
+# endif
+ pr->flags |= BLKID_FL_CDROM_DEV;
+ }
+
+# ifdef CDROM_LAST_WRITTEN
+ if (ioctl(fd, CDROM_LAST_WRITTEN, &last_written) == 0) {
+ pr->flags |= BLKID_FL_CDROM_DEV;
+ } else {
+ if (errno == ENOMEDIUM)
+ goto err;
+ }
+# endif
+
+ if (pr->flags & BLKID_FL_CDROM_DEV) {
+ cdrom_size_correction(pr, last_written);
+
+# ifdef CDROMMULTISESSION
+ if (!pr->off && blkid_probe_get_hint(pr, "session_offset", NULL) < 0) {
+ struct cdrom_multisession multisession = { .addr_format = CDROM_LBA };
+ if (ioctl(fd, CDROMMULTISESSION, &multisession) == 0 && multisession.xa_flag)
+ blkid_probe_set_hint(pr, "session_offset", (multisession.addr.lba << 11));
+ }
+# endif
+ }
+ }
+#endif
+ free(dm_uuid);
+
+# ifdef BLKGETZONESZ
+ if (S_ISBLK(sb.st_mode) && !is_floppy) {
+ uint32_t zone_size_sector;
+
+ if (!ioctl(pr->fd, BLKGETZONESZ, &zone_size_sector))
+ pr->zone_size = zone_size_sector << 9;
+ }
+# endif
+
+ DBG(LOWPROBE, ul_debug("ready for low-probing, offset=%"PRIu64", size=%"PRIu64", zonesize=%"PRIu64,
+ pr->off, pr->size, pr->zone_size));
+ DBG(LOWPROBE, ul_debug("whole-disk: %s, regfile: %s",
+ blkid_probe_is_wholedisk(pr) ?"YES" : "NO",
+ S_ISREG(pr->mode) ? "YES" : "NO"));
+
+ return 0;
+err:
+ DBG(LOWPROBE, ul_debug("failed to prepare a device for low-probing"));
+ return -1;
+
+}
+
+int blkid_probe_get_dimension(blkid_probe pr, uint64_t *off, uint64_t *size)
+{
+ *off = pr->off;
+ *size = pr->size;
+ return 0;
+}
+
+int blkid_probe_set_dimension(blkid_probe pr, uint64_t off, uint64_t size)
+{
+ DBG(LOWPROBE, ul_debug(
+ "changing probing area: size=%"PRIu64", off=%"PRIu64" "
+ "-to-> size=%"PRIu64", off=%"PRIu64"",
+ pr->size, pr->off, size, off));
+
+ pr->off = off;
+ pr->size = size;
+ pr->flags &= ~BLKID_FL_TINY_DEV;
+
+ if (pr->size <= 1440ULL * 1024ULL && !S_ISCHR(pr->mode))
+ pr->flags |= BLKID_FL_TINY_DEV;
+
+ blkid_probe_reset_buffers(pr);
+
+ return 0;
+}
+
+unsigned char *blkid_probe_get_sb_buffer(blkid_probe pr, const struct blkid_idmag *mag, size_t size)
+{
+ uint64_t hint_offset;
+
+ if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0)
+ hint_offset = 0;
+
+ return blkid_probe_get_buffer(pr, hint_offset + (mag->kboff << 10), size);
+}
+
+/*
+ * Check for matching magic value.
+ * Returns BLKID_PROBE_OK if found, BLKID_PROBE_NONE if not found
+ * or no magic present, or negative value on error.
+ */
+int blkid_probe_get_idmag(blkid_probe pr, const struct blkid_idinfo *id,
+ uint64_t *offset, const struct blkid_idmag **res)
+{
+ const struct blkid_idmag *mag = NULL;
+ uint64_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;
+ uint64_t kboff;
+ uint64_t hint_offset;
+
+ if (!mag->hoff || blkid_probe_get_hint(pr, mag->hoff, &hint_offset) < 0)
+ hint_offset = 0;
+
+ /* If the magic is for zoned device, skip non-zoned device */
+ if (mag->is_zoned && !pr->zone_size) {
+ mag++;
+ continue;
+ }
+
+ if (!mag->is_zoned)
+ kboff = mag->kboff;
+ else
+ kboff = ((mag->zonenum * pr->zone_size) >> 10) + mag->kboff_inzone;
+
+ off = hint_offset + ((kboff + (mag->sboff >> 10)) << 10);
+ buf = blkid_probe_get_buffer(pr, off, 1024);
+
+ if (!buf && errno)
+ return -errno;
+
+ if (buf && !memcmp(mag->magic,
+ buf + (mag->sboff & 0x3ff), mag->len)) {
+
+ DBG(LOWPROBE, ul_debug("\tmagic sboff=%u, kboff=%" PRIu64,
+ mag->sboff, kboff));
+ if (offset)
+ *offset = off + (mag->sboff & 0x3ff);
+ if (res)
+ *res = mag;
+ return BLKID_PROBE_OK;
+ }
+ mag++;
+ }
+
+ if (id && id->magics[0].magic)
+ /* magic string(s) defined, but not found */
+ return BLKID_PROBE_NONE;
+
+ return BLKID_PROBE_OK;
+}
+
+static inline void blkid_probe_start(blkid_probe pr)
+{
+ DBG(LOWPROBE, ul_debug("start probe"));
+ pr->cur_chain = NULL;
+ pr->prob_flags = 0;
+ blkid_probe_set_wiper(pr, 0, 0);
+}
+
+static inline void blkid_probe_end(blkid_probe pr)
+{
+ DBG(LOWPROBE, ul_debug("end probe"));
+ 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) == BLKID_PROBE_OK) {
+ * 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) == BLKID_PROBE_OK) {
+ * 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->flags & BLKID_FL_NOSCAN_DEV)
+ return BLKID_PROBE_NONE;
+
+ 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 BLKID_PROBE_NONE; /* all chains already probed */
+ }
+ }
+
+ chn->binary = FALSE; /* for sure... */
+
+ DBG(LOWPROBE, ul_debug("chain probe %s %s (idx=%d)",
+ 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 == BLKID_PROBE_NONE);
+
+ if (rc < 0)
+ return BLKID_PROBE_ERROR;
+
+ return rc;
+}
+
+#ifdef HAVE_LINUX_BLKZONED_H
+static int is_conventional(blkid_probe pr, uint64_t offset)
+{
+ struct blk_zone_report *rep = NULL;
+ int ret;
+ uint64_t zone_mask;
+
+ if (!pr->zone_size)
+ return 1;
+
+ zone_mask = ~(pr->zone_size - 1);
+ rep = blkdev_get_zonereport(blkid_probe_get_fd(pr),
+ (offset & zone_mask) >> 9, 1);
+ if (!rep)
+ return -1;
+
+ if (rep->zones[0].type == BLK_ZONE_TYPE_CONVENTIONAL)
+ ret = 1;
+ else
+ ret = 0;
+
+ free(rep);
+
+ return ret;
+}
+#else
+static inline int is_conventional(blkid_probe pr __attribute__((__unused__)),
+ uint64_t offset __attribute__((__unused__)))
+{
+ return 1;
+}
+#endif
+
+/**
+ * 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 (if you want to erase also superblock with broken check
+ * sums then use BLKID_SUBLKS_BADCSUM too).
+ *
+ * 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. All in-memory cached data from the device are always
+ * reset.
+ *
+ * <example>
+ * <title>wipe all filesystems or raids from the device</title>
+ * <programlisting>
+ * fd = open(devname, O_RDWR|O_CLOEXEC);
+ * 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>
+ *
+ * See also blkid_probe_step_back() if you cannot use this built-in wipe
+ * function, but you want to use libblkid probing as a source for wiping.
+ *
+ * Returns: 0 on success, and -1 in case of error.
+ */
+int blkid_do_wipe(blkid_probe pr, int dryrun)
+{
+ const char *off = NULL;
+ size_t len = 0;
+ uint64_t offset, magoff;
+ int conventional;
+ char buf[BUFSIZ];
+ int fd, rc = 0;
+ struct blkid_chain *chn;
+
+ chn = pr->cur_chain;
+ if (!chn)
+ return BLKID_PROBE_ERROR;
+
+ 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 BLKID_PROBE_OK;
+ }
+
+ if (rc || len == 0 || off == NULL)
+ return BLKID_PROBE_OK;
+
+ errno = 0;
+ magoff = strtoumax(off, NULL, 10);
+ if (errno)
+ return BLKID_PROBE_OK;
+
+ offset = magoff + pr->off;
+ fd = blkid_probe_get_fd(pr);
+ if (fd < 0)
+ return BLKID_PROBE_ERROR;
+
+ if (len > sizeof(buf))
+ len = sizeof(buf);
+
+ rc = is_conventional(pr, offset);
+ if (rc < 0)
+ return BLKID_PROBE_ERROR;
+ conventional = rc == 1;
+
+ DBG(LOWPROBE, ul_debug(
+ "do_wipe [offset=0x%"PRIx64" (%"PRIu64"), len=%zu, chain=%s, idx=%d, dryrun=%s]\n",
+ offset, offset, len, chn->driver->name, chn->idx, dryrun ? "yes" : "not"));
+
+ if (lseek(fd, offset, SEEK_SET) == (off_t) -1)
+ return BLKID_PROBE_ERROR;
+
+ if (!dryrun && len) {
+ if (conventional) {
+ memset(buf, 0, len);
+
+ /* wipen on device */
+ if (write_all(fd, buf, len))
+ return BLKID_PROBE_ERROR;
+ if (fsync(fd) != 0)
+ return BLKID_PROBE_ERROR;
+ } else {
+#ifdef HAVE_LINUX_BLKZONED_H
+ uint64_t zone_mask = ~(pr->zone_size - 1);
+ struct blk_zone_range range = {
+ .sector = (offset & zone_mask) >> 9,
+ .nr_sectors = pr->zone_size >> 9,
+ };
+
+ rc = ioctl(fd, BLKRESETZONE, &range);
+ if (rc < 0)
+ return BLKID_PROBE_ERROR;
+#else
+ /* Should not reach here */
+ assert(0);
+#endif
+ }
+
+ pr->flags &= ~BLKID_FL_MODIF_BUFF; /* be paranoid */
+
+ return blkid_probe_step_back(pr);
+
+ }
+
+ if (dryrun) {
+ /* wipe in memory only */
+ blkid_probe_hide_range(pr, magoff, len);
+ return blkid_probe_step_back(pr);
+ }
+
+ return BLKID_PROBE_OK;
+}
+
+/**
+ * blkid_probe_step_back:
+ * @pr: prober
+ *
+ * This function move pointer to the probing chain one step back -- it means
+ * that the previously used probing function will be called again in the next
+ * blkid_do_probe() call.
+ *
+ * This is necessary for example if you erase or modify on-disk superblock
+ * according to the current libblkid probing result.
+ *
+ * Note that blkid_probe_hide_range() changes semantic of this function and
+ * cached buffers are not reset, but library uses in-memory modified
+ * buffers to call the next probing function.
+ *
+ * <example>
+ * <title>wipe all superblock, but use libblkid only for probing</title>
+ * <programlisting>
+ * pr = blkid_new_probe_from_filename(devname);
+ *
+ * blkid_probe_enable_superblocks(pr, 1);
+ * blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC);
+ *
+ * blkid_probe_enable_partitions(pr, 1);
+ * blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC);
+ *
+ * while (blkid_do_probe(pr) == BLKID_PROBE_OK) {
+ * const char *ostr = NULL;
+ * size_t len = 0;
+ *
+ * // superblocks
+ * if (blkid_probe_lookup_value(pr, "SBMAGIC_OFFSET", &ostr, NULL) == 0)
+ * blkid_probe_lookup_value(pr, "SBMAGIC", NULL, &len);
+ *
+ * // partition tables
+ * if (len == 0 && blkid_probe_lookup_value(pr, "PTMAGIC_OFFSET", &ostr, NULL) == 0)
+ * blkid_probe_lookup_value(pr, "PTMAGIC", NULL, &len);
+ *
+ * if (!len || !str)
+ * continue;
+ *
+ * // convert ostr to the real offset by off = strtoll(ostr, NULL, 10);
+ * // use your stuff to erase @len bytes at the @off
+ * ....
+ *
+ * // retry the last probing to check for backup superblocks ..etc.
+ * blkid_probe_step_back(pr);
+ * }
+ * </programlisting>
+ * </example>
+ *
+ * Returns: 0 on success, and -1 in case of error.
+ */
+int blkid_probe_step_back(blkid_probe pr)
+{
+ struct blkid_chain *chn;
+
+ chn = pr->cur_chain;
+ if (!chn)
+ return -1;
+
+ if (!(pr->flags & BLKID_FL_MODIF_BUFF))
+ blkid_probe_reset_buffers(pr);
+
+ if (chn->idx >= 0) {
+ chn->idx--;
+ DBG(LOWPROBE, ul_debug("step back: moving %s chain index to %d",
+ 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 previous chain.
+ */
+ size_t idx = chn->driver->id > 0 ? chn->driver->id - 1 : 0;
+
+ DBG(LOWPROBE, ul_debug("step back: moving to previous chain"));
+
+ 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 superblocks 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 ambivalent result is
+ * detected and -1 on case of error.
+ */
+int blkid_do_safeprobe(blkid_probe pr)
+{
+ int i, count = 0, rc = 0;
+
+ if (pr->flags & BLKID_FL_NOSCAN_DEV)
+ return BLKID_PROBE_NONE;
+
+ 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(LOWPROBE, ul_debug("chain safeprobe %s %s",
+ 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 BLKID_PROBE_ERROR;
+
+ return count == 0 ? BLKID_PROBE_NONE : BLKID_PROBE_OK;
+}
+
+/**
+ * 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->flags & BLKID_FL_NOSCAN_DEV)
+ return BLKID_PROBE_NONE;
+
+ 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(LOWPROBE, ul_debug("chain fullprobe %s: %s",
+ 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 BLKID_PROBE_ERROR;
+
+ return count == 0 ? BLKID_PROBE_NONE : BLKID_PROBE_OK;
+}
+
+/* same sa blkid_probe_get_buffer() but works with 512-sectors */
+unsigned char *blkid_probe_get_sector(blkid_probe pr, unsigned int sector)
+{
+ return blkid_probe_get_buffer(pr, ((uint64_t) sector) << 9, 0x200);
+}
+
+struct blkid_prval *blkid_probe_assign_value(blkid_probe pr, const char *name)
+{
+ struct blkid_prval *v;
+
+ v = calloc(1, sizeof(struct blkid_prval));
+ if (!v)
+ return NULL;
+
+ INIT_LIST_HEAD(&v->prvals);
+ v->name = name;
+ v->chain = pr->cur_chain;
+ list_add_tail(&v->prvals, &pr->values);
+
+ DBG(LOWPROBE, ul_debug("assigning %s [%s]", name, v->chain->driver->name));
+ return v;
+}
+
+/* Note that value data is always terminated by zero to keep things robust,
+ * this extra zero is not count to the value length. It's caller responsibility
+ * to set proper value length (for strings we count terminator to the length,
+ * for binary data it's without terminator).
+ */
+int blkid_probe_value_set_data(struct blkid_prval *v,
+ const unsigned char *data, size_t len)
+{
+ v->data = calloc(1, len + 1); /* always terminate by \0 */
+
+ if (!v->data)
+ return -ENOMEM;
+ memcpy(v->data, data, len);
+ v->len = len;
+ return 0;
+}
+
+int blkid_probe_set_value(blkid_probe pr, const char *name,
+ const unsigned char *data, size_t len)
+{
+ struct blkid_prval *v;
+
+ v = blkid_probe_assign_value(pr, name);
+ if (!v)
+ return -1;
+
+ return blkid_probe_value_set_data(v, data, len);
+}
+
+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 -ENOMEM;
+
+ len = vasprintf((char **) &v->data, fmt, ap);
+
+ if (len <= 0) {
+ blkid_probe_free_value(v);
+ return len == 0 ? -EINVAL : -ENOMEM;
+ }
+ 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, uint64_t offset,
+ size_t len, const unsigned char *magic)
+{
+ int rc = 0;
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+ if (!chn || !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;
+}
+
+static void blkid_probe_log_csum_mismatch(blkid_probe pr, size_t n, const void *csum,
+ const void *expected)
+{
+ char csum_hex[256];
+ char expected_hex[sizeof(csum_hex)];
+ int hex_size = min(sizeof(csum_hex), n * 2);
+
+ for (int i = 0; i < hex_size; i+=2) {
+ sprintf(&csum_hex[i], "%02X", ((const unsigned char *) csum)[i / 2]);
+ sprintf(&expected_hex[i], "%02X", ((const unsigned char *) expected)[i / 2]);
+ }
+
+ ul_debug(
+ "incorrect checksum for type %s,"
+ " got %*s, expected %*s",
+ blkid_probe_get_probername(pr),
+ hex_size, csum_hex, hex_size, expected_hex);
+}
+
+
+int blkid_probe_verify_csum_buf(blkid_probe pr, size_t n, const void *csum,
+ const void *expected)
+{
+ if (memcmp(csum, expected, n) != 0) {
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+ ON_DBG(LOWPROBE, blkid_probe_log_csum_mismatch(pr, n, csum, expected));
+
+ /*
+ * Accept bad checksum if BLKID_SUBLKS_BADCSUM flags is set
+ */
+ if (chn && chn->driver->id == BLKID_CHAIN_SUBLKS
+ && (chn->flags & BLKID_SUBLKS_BADCSUM)) {
+ blkid_probe_set_value(pr, "SBBADCSUM", (unsigned char *) "1", 2);
+ goto accept;
+ }
+ return 0; /* bad checksum */
+ }
+
+accept:
+ return 1;
+}
+
+int blkid_probe_verify_csum(blkid_probe pr, uint64_t csum, uint64_t expected)
+{
+ return blkid_probe_verify_csum_buf(pr, sizeof(csum), &csum, &expected);
+}
+
+/**
+ * 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);
+ int flags;
+
+ if (!disk_path)
+ return NULL;
+
+ DBG(LOWPROBE, ul_debug("allocate a wholedisk probe"));
+
+ pr->disk_probe = blkid_new_probe_from_filename(disk_path);
+
+ free(disk_path);
+
+ if (!pr->disk_probe)
+ return NULL; /* ENOMEM? */
+
+ flags = blkid_probe_get_partitions_flags(pr);
+ if (flags & BLKID_PARTS_FORCE_GPT)
+ blkid_probe_set_partitions_flags(pr->disk_probe,
+ BLKID_PARTS_FORCE_GPT);
+ }
+
+ 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 (blkid_loff_t) pr->size;
+}
+
+/**
+ * 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 (blkid_loff_t) pr->off;
+}
+
+/**
+ * blkid_probe_get_fd:
+ * @pr: probe
+ *
+ * Returns: file descriptor for assigned device/file or -1 in case of error.
+ */
+int blkid_probe_get_fd(blkid_probe pr)
+{
+ return pr->fd;
+}
+
+/**
+ * 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->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_set_sectorsize:
+ * @pr: probe
+ * @sz: new size (to overwrite system default)
+ *
+ * Note that blkid_probe_set_device() resets this setting. Use it after
+ * blkid_probe_set_device() and before any probing call.
+ *
+ * Since: 2.30
+ *
+ * Returns: 0 or <0 in case of error
+ */
+int blkid_probe_set_sectorsize(blkid_probe pr, unsigned int sz)
+{
+ pr->blkssz = sz;
+ return 0;
+}
+
+/**
+ * blkid_probe_get_sectors:
+ * @pr: probe
+ *
+ * Returns: 512-byte sector count or -1 in case of error.
+ */
+blkid_loff_t blkid_probe_get_sectors(blkid_probe pr)
+{
+ return (blkid_loff_t) (pr->size >> 9);
+}
+
+/**
+ * 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)
+{
+ int i = 0;
+ struct list_head *p;
+
+ list_for_each(p, &pr->values)
+ ++i;
+ return i;
+}
+
+/**
+ * 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(LOWPROBE, ul_debug("returning %s value", 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)
+{
+ int i = 0;
+ struct list_head *p;
+
+ if (num < 0)
+ return NULL;
+
+ list_for_each(p, &pr->values) {
+ if (i++ != num)
+ continue;
+ return list_entry(p, struct blkid_prval, prvals);
+ }
+ return NULL;
+}
+
+struct blkid_prval *__blkid_probe_lookup_value(blkid_probe pr, const char *name)
+{
+ struct list_head *p;
+
+ if (list_empty(&pr->values))
+ return NULL;
+
+ list_for_each(p, &pr->values) {
+ struct blkid_prval *v = list_entry(p, struct blkid_prval,
+ prvals);
+
+ if (v->name && strcmp(name, v->name) == 0) {
+ DBG(LOWPROBE, ul_debug("returning %s value", v->name));
+ return v;
+ }
+ }
+ return NULL;
+}
+
+
+/* converts DCE UUID (uuid[16]) to human readable string
+ * - the @len should be always 37 */
+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]);
+}
+
+/* like uuid_is_null() from libuuid, but works with arbitrary size of UUID */
+int blkid_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;
+}
+
+/* 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)
+{
+ return rtrim_whitespace(str);
+}
+
+/* Removes whitespace from the left-hand side of a string.
+ *
+ * Returns size of the new string (without \0).
+ */
+size_t blkid_ltrim_whitespace(unsigned char *str)
+{
+ return ltrim_whitespace(str);
+}
+
+/*
+ * 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"
+ * can be used 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, uint64_t off, uint64_t size)
+{
+ struct blkid_chain *chn;
+
+ if (!size) {
+ DBG(LOWPROBE, ul_debug("zeroize wiper"));
+ 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(LOWPROBE,
+ ul_debug("wiper set to %s::%s off=%"PRIu64" size=%"PRIu64"",
+ chn->driver->name,
+ chn->driver->idinfos[chn->idx]->name,
+ pr->wipe_off, pr->wipe_size));
+}
+
+/*
+ * Returns 1 if the <@off,@size> area was wiped
+ */
+int blkid_probe_is_wiped(blkid_probe pr, struct blkid_chain **chn, uint64_t off, uint64_t size)
+{
+ if (!size)
+ return 0;
+
+ if (pr->wipe_off <= off && off + size <= pr->wipe_off + pr->wipe_size) {
+ *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 (reset).
+ */
+void blkid_probe_use_wiper(blkid_probe pr, uint64_t off, uint64_t size)
+{
+ struct blkid_chain *chn = NULL;
+
+ if (blkid_probe_is_wiped(pr, &chn, off, size) && chn) {
+ DBG(LOWPROBE, ul_debug("previously wiped area modified "
+ " -- ignore previous results"));
+ blkid_probe_set_wiper(pr, 0, 0);
+ blkid_probe_chain_reset_values(pr, chn);
+ }
+}
+
+static struct blkid_hint *get_hint(blkid_probe pr, const char *name)
+{
+ struct list_head *p;
+
+ if (list_empty(&pr->hints))
+ return NULL;
+
+ list_for_each(p, &pr->hints) {
+ struct blkid_hint *h = list_entry(p, struct blkid_hint, hints);
+
+ if (h->name && strcmp(name, h->name) == 0)
+ return h;
+ }
+ return NULL;
+}
+
+/**
+ * blkid_probe_set_hint:
+ * @pr: probe
+ * @name: hint name or NAME=value
+ * @value: offset or another number
+ *
+ * Sets extra hint for low-level prober. If the hint is set by NAME=value
+ * notation than @value is ignored. The functions blkid_probe_set_device()
+ * and blkid_reset_probe() resets all hints.
+ *
+ * The hints are optional way how to force libblkid probing functions to check
+ * for example another location.
+ *
+ * Returns: 0 on success, or -1 in case of error.
+ */
+int blkid_probe_set_hint(blkid_probe pr, const char *name, uint64_t value)
+{
+ struct blkid_hint *hint = NULL;
+ char *n = NULL, *v = NULL;
+
+ if (strchr(name, '=')) {
+ char *end = NULL;
+
+ if (blkid_parse_tag_string(name, &n, &v) != 0)
+ goto done;
+
+ errno = 0;
+ value = strtoumax(v, &end, 10);
+
+ if (errno || v == end || (end && *end))
+ goto done;
+ }
+
+ hint = get_hint(pr, n ? n : name);
+ if (hint) {
+ /* alter old hint */
+ hint->value = value;
+ DBG(LOWPROBE,
+ ul_debug("updated hint '%s' to %"PRIu64"", hint->name, hint->value));
+ } else {
+ /* add a new hint */
+ if (!n) {
+ n = strdup(name);
+ if (!n)
+ goto done;
+ }
+ hint = malloc(sizeof(*hint));
+ if (!hint)
+ goto done;
+
+ hint->name = n;
+ hint->value = value;
+
+ INIT_LIST_HEAD(&hint->hints);
+ list_add_tail(&hint->hints, &pr->hints);
+
+ DBG(LOWPROBE,
+ ul_debug("new hint '%s' is %"PRIu64"", hint->name, hint->value));
+ n = NULL;
+ }
+done:
+ free(n);
+ free(v);
+
+ if (!hint)
+ return errno ? -errno : -EINVAL;
+ return 0;
+}
+
+int blkid_probe_get_hint(blkid_probe pr, const char *name, uint64_t *value)
+{
+ struct blkid_hint *h = get_hint(pr, name);
+
+ if (!h)
+ return -EINVAL;
+ if (value)
+ *value = h->value;
+ return 0;
+}
+
+/**
+ * blkid_probe_reset_hints:
+ * @pr: probe
+ *
+ * Removes all previously defined probinig hints. See also blkid_probe_set_hint().
+ */
+void blkid_probe_reset_hints(blkid_probe pr)
+{
+ if (list_empty(&pr->hints))
+ return;
+
+ DBG(LOWPROBE, ul_debug("resetting hints"));
+
+ while (!list_empty(&pr->hints)) {
+ struct blkid_hint *h = list_entry(pr->hints.next,
+ struct blkid_hint, hints);
+ list_del(&h->hints);
+ free(h->name);
+ free(h);
+ }
+
+ INIT_LIST_HEAD(&pr->hints);
+}
diff --git a/libblkid/src/read.c b/libblkid/src/read.c
new file mode 100644
index 0000000..d62edfa
--- /dev/null
+++ b/libblkid/src/read.c
@@ -0,0 +1,455 @@
+/*
+ * 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
+
+/*
+ * 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(READ, ul_debug("found device header: %8s", 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(READ, ul_debug("found device trailer %9s", *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(READ, ul_debug("blkid: short line parsing dev: %s", *cp));
+ return -BLKID_ERR_CACHE;
+ }
+ start = skip_over_blank(start + 1);
+ end = skip_over_word(start);
+
+ DBG(READ, ul_debug("device should be %*s",
+ (int)(end - start), start));
+
+ if (**cp == '>')
+ *cp = end;
+ else
+ (*cp)++;
+
+ *tmp = '\0';
+
+ if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) {
+ DBG(READ, ul_debug("blkid: missing </device> ending: %s", end));
+ } else if (tmp)
+ *tmp = '\0';
+
+ if (end - start <= 1) {
+ DBG(READ, ul_debug("blkid: empty device name: %s", *cp));
+ return -BLKID_ERR_CACHE;
+ }
+
+ name = strndup(start, end - start);
+ if (name == NULL)
+ return -BLKID_ERR_MEM;
+
+ DBG(READ, ul_debug("found dev %s", 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 == '"') {
+ char *p = end = *value + 1;
+
+ /* convert 'foo\"bar' to 'foo"bar' */
+ while (*p) {
+ if (*p == '\\') {
+ p++;
+ *end = *p;
+ } else {
+ *end = *p;
+ if (*p == '"')
+ break;
+ }
+ p++;
+ end++;
+ }
+
+ if (*end != '"') {
+ DBG(READ, ul_debug("unbalanced quotes at: %s", *value));
+ *cp = *value;
+ return -BLKID_ERR_CACHE;
+ }
+ (*value)++;
+ *end = '\0';
+ end = ++p;
+ } else {
+ end = skip_over_word(*value);
+ if (*end) {
+ *end = '\0';
+ end++;
+ }
+ }
+ *cp = end;
+
+ return 1;
+}
+
+/*
+ * 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)
+ return ret;
+
+ DBG(READ, ul_debug("tag: %s=\"%s\"", name, value));
+
+ errno = 0;
+
+ /* Some tags are stored directly in the device struct */
+ if (!strcmp(name, "DEVNO")) {
+ dev->bid_devno = strtoull(value, NULL, 0);
+ if (errno)
+ return -errno;
+ } else if (!strcmp(name, "PRI")) {
+ dev->bid_pri = strtol(value, NULL, 0);
+ if (errno)
+ return -errno;
+ } else if (!strcmp(name, "TIME")) {
+ char *end = NULL;
+
+ dev->bid_time = strtoull(value, &end, 0);
+ if (errno == 0 && end && *end == '.')
+ dev->bid_utime = strtoull(end + 1, NULL, 0);
+ if (errno)
+ return -errno;
+ } else
+ ret = blkid_set_tag(dev, name, value, strlen(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(READ, ul_debug("line: %s", 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(READ, ul_debug("blkid: device %s has no TYPE",dev->bid_name));
+ blkid_free_dev(dev);
+ goto done;
+ }
+
+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 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|O_CLOEXEC)) < 0)
+ return;
+ if (fstat(fd, &st) < 0)
+ goto errout;
+ if ((st.st_mtime == cache->bic_ftime) ||
+ (cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+ DBG(CACHE, ul_debug("skipping re-read of %s",
+ cache->bic_filename));
+ goto errout;
+ }
+
+ DBG(CACHE, ul_debug("reading cache file %s",
+ cache->bic_filename));
+
+ file = fdopen(fd, "r" UL_CLOEXECSTR);
+ 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(READ, ul_debug("blkid: bad format on line %d", 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);
+}
+
+#ifdef TEST_PROGRAM
+
+int main(int argc, char**argv)
+{
+ blkid_cache cache = NULL;
+ int ret;
+
+ blkid_init_debug(BLKID_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/libblkid/src/resolve.c b/libblkid/src/resolve.c
new file mode 100644
index 0000000..16653fa
--- /dev/null
+++ b/libblkid/src/resolve.c
@@ -0,0 +1,129 @@
+/*
+ * 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(TAG, ul_debug("looking for tag %s on %s device", tagname, devname));
+
+ if (!devname)
+ return NULL;
+ if (!cache && 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 = found->bit_val ? strdup(found->bit_val) : NULL;
+
+ 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 = NULL, *v = NULL;
+ char *ret = NULL;
+
+ if (!token)
+ return NULL;
+ if (!cache && blkid_get_cache(&c, NULL) < 0)
+ return NULL;
+
+ DBG(TAG, ul_debug("looking for %s%s%s %s", token, value ? "=" : "",
+ value ? value : "", cache ? "in cache" : "from disk"));
+
+ if (!value) {
+ if (!strchr(token, '=')) {
+ ret = strdup(token);
+ goto out;
+ }
+ if (blkid_parse_tag_string(token, &t, &v) != 0 || !t || !v)
+ goto out;
+ token = t;
+ value = v;
+ }
+
+ dev = blkid_find_dev_with_tag(c, token, value);
+ if (!dev)
+ goto out;
+
+ ret = dev->bid_name ? strdup(dev->bid_name) : NULL;
+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(BLKID_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/libblkid/src/save.c b/libblkid/src/save.c
new file mode 100644
index 0000000..1a617c0
--- /dev/null
+++ b/libblkid/src/save.c
@@ -0,0 +1,244 @@
+/*
+ * 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 "closestream.h"
+#include "fileutils.h"
+
+#include "blkidP.h"
+
+
+static void save_quoted(const char *data, FILE *file)
+{
+ const char *p;
+
+ fputc('"', file);
+ for (p = data; p && *p; p++) {
+ if ((unsigned char) *p == 0x22 || /* " */
+ (unsigned char) *p == 0x5c) /* \ */
+ fputc('\\', file);
+
+ fputc(*p, file);
+ }
+ fputc('"', file);
+}
+static int save_dev(blkid_dev dev, FILE *file)
+{
+ struct list_head *p;
+
+ if (!dev || dev->bid_name[0] != '/')
+ return 0;
+
+ DBG(SAVE, ul_debug("device %s, type %s", dev->bid_name, dev->bid_type ?
+ dev->bid_type : "(null)"));
+
+ fprintf(file, "<device DEVNO=\"0x%04lx\" TIME=\"%lld.%lld\"",
+ (unsigned long) dev->bid_devno,
+ (long long) dev->bid_time,
+ (long 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);
+
+ fputc(' ', file); /* space between tags */
+ fputs(tag->bit_name, file); /* tag NAME */
+ fputc('=', file); /* separator between NAME and VALUE */
+ save_quoted(tag->bit_val, file); /* tag "VALUE" */
+ }
+ 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 (list_empty(&cache->bic_devs) ||
+ !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+ DBG(SAVE, ul_debug("skipping cache file write"));
+ 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(SAVE, ul_debug("can't create %s directory for cache file",
+ 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(SAVE, ul_debug("can't write to cache file %s", 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)) {
+ size_t len = strlen(filename) + 8;
+ tmp = malloc(len);
+ if (tmp) {
+ snprintf(tmp, len, "%s-XXXXXX", filename);
+ fd = mkstemp_cloexec(tmp);
+ if (fd >= 0) {
+ if (fchmod(fd, 0644) != 0)
+ DBG(SAVE, ul_debug("%s: fchmod failed", filename));
+ else if ((file = fdopen(fd, "w" UL_CLOEXECSTR)))
+ opened = tmp;
+ if (!file)
+ close(fd);
+ }
+ }
+ }
+
+ if (!file) {
+ file = fopen(filename, "w" UL_CLOEXECSTR);
+ opened = filename;
+ }
+
+ DBG(SAVE, ul_debug("writing cache file %s (really %s)",
+ 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;
+ }
+
+ if (close_stream(file) != 0)
+ DBG(SAVE, ul_debug("write failed: %s", filename));
+
+ if (opened != filename) {
+ if (ret < 0) {
+ unlink(opened);
+ DBG(SAVE, ul_debug("unlinked temp cache %s", opened));
+ } else {
+ char *backup;
+ size_t len = strlen(filename) + 5;
+
+ backup = malloc(len);
+ if (backup) {
+ snprintf(backup, len, "%s.old", filename);
+ unlink(backup);
+ if (link(filename, backup)) {
+ DBG(SAVE, ul_debug("can't link %s to %s",
+ filename, backup));
+ }
+ free(backup);
+ }
+ if (rename(opened, filename)) {
+ ret = errno;
+ DBG(SAVE, ul_debug("can't rename %s to %s",
+ opened, filename));
+ } else {
+ DBG(SAVE, ul_debug("moved temp cache %s", 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(BLKID_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 = 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/libblkid/src/superblocks/adaptec_raid.c b/libblkid/src/superblocks/adaptec_raid.c
new file mode 100644
index 0000000..5fc5fc4
--- /dev/null
+++ b/libblkid/src/superblocks/adaptec_raid.c
@@ -0,0 +1,116 @@
+/*
+ * 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 BLKID_PROBE_NONE;
+
+ if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+ return BLKID_PROBE_NONE;
+
+ off = ((pr->size / 0x200)-1) * 0x200;
+ ad = (struct adaptec_metadata *)
+ blkid_probe_get_buffer(pr,
+ off,
+ sizeof(struct adaptec_metadata));
+ if (!ad)
+ return errno ? -errno : BLKID_PROBE_NONE;
+
+ if (ad->smagic != be32_to_cpu(AD_SIGNATURE))
+ return BLKID_PROBE_NONE;
+ if (ad->b0idcode != be32_to_cpu(AD_MAGIC))
+ return BLKID_PROBE_NONE;
+ if (blkid_probe_sprintf_version(pr, "%u", ad->resver) != 0)
+ return BLKID_PROBE_NONE;
+ if (blkid_probe_set_magic(pr, off, sizeof(ad->b0idcode),
+ (unsigned char *) &ad->b0idcode))
+ return BLKID_PROBE_NONE;
+
+ return BLKID_PROBE_OK;
+}
+
+const struct blkid_idinfo adraid_idinfo = {
+ .name = "adaptec_raid_member",
+ .usage = BLKID_USAGE_RAID,
+ .probefunc = probe_adraid,
+ .magics = BLKID_NONE_MAGIC
+};
+
+
diff --git a/libblkid/src/superblocks/apfs.c b/libblkid/src/superblocks/apfs.c
new file mode 100644
index 0000000..048423a
--- /dev/null
+++ b/libblkid/src/superblocks/apfs.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 Harry Mallon <hjmallon@gmail.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include "superblocks.h"
+
+#define APFS_CONTAINER_SUPERBLOCK_TYPE 1
+#define APFS_CONTAINER_SUPERBLOCK_SUBTYPE 0
+#define APFS_STANDARD_BLOCK_SIZE 4096
+
+/*
+ * This struct is much longer than this, but this seems
+ * to contain the useful bits (for now).
+ *
+ * All values are little-endian.
+ */
+struct apfs_super_block {
+ // Generic part to all APFS objects
+ uint64_t checksum;
+ uint64_t oid;
+ uint64_t xid;
+ uint16_t type;
+ uint16_t flags;
+ uint16_t subtype;
+ uint16_t pad;
+
+ // Specific to container header
+ uint32_t magic; // 'NXSB'
+ uint32_t block_size;
+ uint64_t block_count;
+ uint64_t features;
+ uint64_t read_only_features;
+ uint64_t incompatible_features;
+ uint8_t uuid[16];
+};
+
+static int probe_apfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct apfs_super_block *sb;
+
+ sb = blkid_probe_get_sb(pr, mag, struct apfs_super_block);
+ if (!sb)
+ return errno ? -errno : BLKID_PROBE_NONE;
+
+ if (le16_to_cpu(sb->type) != APFS_CONTAINER_SUPERBLOCK_TYPE)
+ return BLKID_PROBE_NONE;
+
+ if (le16_to_cpu(sb->subtype) != APFS_CONTAINER_SUPERBLOCK_SUBTYPE)
+ return BLKID_PROBE_NONE;
+
+ if (le16_to_cpu(sb->pad) != 0)
+ return BLKID_PROBE_NONE;
+
+ /*
+ * This check is pretty draconian, but should avoid false
+ * positives. Can be improved as more APFS documentation
+ * is published.
+ */
+ if (le32_to_cpu(sb->block_size) != APFS_STANDARD_BLOCK_SIZE)
+ return BLKID_PROBE_NONE;
+
+ if (blkid_probe_set_uuid(pr, sb->uuid) < 0)
+ return BLKID_PROBE_NONE;
+
+ blkid_probe_set_fsblocksize(pr, le32_to_cpu(sb->block_size));
+ blkid_probe_set_block_size(pr, le32_to_cpu(sb->block_size));
+
+ return BLKID_PROBE_OK;
+}
+
+const struct blkid_idinfo apfs_idinfo =
+{
+ .name = "apfs",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_apfs,
+ .magics =
+ {
+ { .magic = "NXSB", .len = 4, .sboff = 32 },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/bcache.c b/libblkid/src/superblocks/bcache.c
new file mode 100644
index 0000000..d3afc41
--- /dev/null
+++ b/libblkid/src/superblocks/bcache.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2013 Rolf Fokkens <rolf@fokkens.nl>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Based on code fragments from bcache-tools by Kent Overstreet:
+ * http://evilpiepirate.org/git/bcache-tools.git
+ */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "superblocks.h"
+#include "crc32c.h"
+#include "crc64.h"
+#include "xxhash.h"
+
+#define SB_LABEL_SIZE 32
+#define SB_JOURNAL_BUCKETS 256U
+
+/*
+ * The bcache_super_block is adapted from struct cache_sb in kernel.
+ * https://github.com/torvalds/linux/blob/master/drivers/md/bcache/bcache_ondisk.h
+ */
+struct bcache_super_block {
+ uint64_t csum;
+ uint64_t offset; /* where this super block was written */
+ uint64_t version;
+ uint8_t magic[16]; /* bcache file system identifier */
+ uint8_t uuid[16]; /* device identifier */
+ uint8_t set_info[16]; /* magic or uuid */
+ uint8_t label[SB_LABEL_SIZE];
+ uint64_t flags;
+ uint64_t seq;
+
+ uint64_t feature_compat;
+ uint64_t feature_incompat;
+ uint64_t feature_ro_compat;
+
+ uint64_t pad[5];
+
+ union {
+ struct {
+ /* Cache devices */
+ uint64_t nbuckets; /* device size */
+
+ uint16_t block_size; /* sectors */
+ uint16_t bucket_size; /* sectors */
+
+ uint16_t nr_in_set;
+ uint16_t nr_this_dev;
+ };
+ struct {
+ /* Backing devices */
+ uint64_t data_offset;
+ };
+ };
+
+ uint32_t last_mount;
+
+ uint16_t first_bucket;
+ union {
+ uint16_t njournal_buckets;
+ uint16_t keys;
+ };
+ uint64_t d[SB_JOURNAL_BUCKETS]; /* journal buckets */
+ uint16_t obso_bucket_size_hi; /* obsoleted */
+} __attribute__((packed));
+
+struct bcachefs_sb_field {
+ uint32_t u64s;
+ uint32_t type;
+} __attribute__((packed));
+
+struct bcachefs_sb_member {
+ uint8_t uuid[16];
+ uint64_t nbuckets;
+ uint16_t first_bucket;
+ uint16_t bucket_size;
+ uint32_t pad;
+ uint64_t last_mount;
+ uint64_t flags[2];
+} __attribute__((packed));
+
+struct bcachefs_sb_field_members {
+ struct bcachefs_sb_field field;
+ struct bcachefs_sb_member members[];
+} __attribute__((packed));
+
+struct bcachefs_sb_disk_group {
+ uint8_t label[SB_LABEL_SIZE];
+ uint64_t flags[2];
+} __attribute__((packed));
+
+struct bcachefs_sb_field_disk_groups {
+ struct bcachefs_sb_field field;
+ struct bcachefs_sb_disk_group disk_groups[];
+} __attribute__((packed));
+
+enum bcachefs_sb_csum_type {
+ BCACHEFS_SB_CSUM_TYPE_NONE = 0,
+ BCACHEFS_SB_CSUM_TYPE_CRC32C = 1,
+ BCACHEFS_SB_CSUM_TYPE_CRC64 = 2,
+ BCACHEFS_SB_CSUM_TYPE_XXHASH = 7,
+};
+
+union bcachefs_sb_csum {
+ uint32_t crc32c;
+ uint64_t crc64;
+ XXH64_hash_t xxh64;
+ uint8_t raw[16];
+} __attribute__((packed));
+
+struct bcachefs_sb_layout {
+ uint8_t magic[16];
+ uint8_t layout_type;
+ uint8_t sb_max_size_bits;
+ uint8_t nr_superblocks;
+ uint8_t pad[5];
+ uint64_t sb_offset[61];
+} __attribute__((packed));
+
+struct bcachefs_super_block {
+ union bcachefs_sb_csum csum;
+ uint16_t version;
+ uint16_t version_min;
+ uint16_t pad[2];
+ uint8_t magic[16];
+ uint8_t uuid[16];
+ uint8_t user_uuid[16];
+ uint8_t label[SB_LABEL_SIZE];
+ uint64_t offset;
+ uint64_t seq;
+ uint16_t block_size;
+ uint8_t dev_idx;
+ uint8_t nr_devices;
+ uint32_t u64s;
+ uint64_t time_base_lo;
+ uint32_t time_base_hi;
+ uint32_t time_precision;
+ uint64_t flags[8];
+ uint64_t features[2];
+ uint64_t compat[2];
+ struct bcachefs_sb_layout layout;
+ struct bcachefs_sb_field _start[];
+} __attribute__((packed));
+
+/* magic string */
+#define BCACHE_SB_MAGIC "\xc6\x85\x73\xf6\x4e\x1a\x45\xca\x82\x65\xf5\x7f\x48\xba\x6d\x81"
+#define BCACHEFS_SB_MAGIC "\xc6\x85\x73\xf6\x66\xce\x90\xa9\xd9\x6a\x60\xcf\x80\x3d\xf7\xef"
+/* magic string len */
+#define BCACHE_SB_MAGIC_LEN (sizeof(BCACHE_SB_MAGIC) - 1)
+/* super block offset */
+#define BCACHE_SB_OFF 0x1000U
+/* supper block offset in kB */
+#define BCACHE_SB_KBOFF (BCACHE_SB_OFF >> 10)
+/* magic string offset within super block */
+#define BCACHE_SB_MAGIC_OFF offsetof(struct bcache_super_block, magic)
+/* start of checksummed data within superblock */
+#define BCACHE_SB_CSUMMED_START 8U
+/* granularity of offset and length fields within superblock */
+#define BCACHEFS_SECTOR_SIZE 512U
+/* maximum superblock size shift */
+#define BCACHEFS_SB_MAX_SIZE_SHIFT 0x10U
+/* maximum superblock size */
+#define BCACHEFS_SB_MAX_SIZE (1U << BCACHEFS_SB_MAX_SIZE_SHIFT)
+/* fields offset within super block */
+#define BCACHEFS_SB_FIELDS_OFF offsetof(struct bcachefs_super_block, _start)
+/* tag value for members field */
+#define BCACHEFS_SB_FIELD_TYPE_MEMBERS 1
+/* tag value for disk_groups field */
+#define BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS 5
+/* version splitting helpers */
+#define BCH_VERSION_MAJOR(_v) ((uint16_t) ((_v) >> 10))
+#define BCH_VERSION_MINOR(_v) ((uint16_t) ((_v) & ~(~0U << 10)))
+
+#define BYTES(f) ((((uint64_t) le32_to_cpu((f)->u64s)) * 8))
+
+static int bcache_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag,
+ const struct bcache_super_block *bcs)
+{
+ const unsigned char *csummed;
+ size_t csummed_size;
+ uint64_t csum;
+
+ if (le16_to_cpu(bcs->keys) > ARRAY_SIZE(bcs->d))
+ return 0;
+
+ /* up to the end of bcs->d[] */
+ csummed_size = offsetof(typeof(*bcs), d) +
+ sizeof(bcs->d[0]) * le16_to_cpu(bcs->keys);
+ csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size);
+ csum = ul_crc64_we(csummed + BCACHE_SB_CSUMMED_START,
+ csummed_size - BCACHE_SB_CSUMMED_START);
+ return blkid_probe_verify_csum(pr, csum, le64_to_cpu(bcs->csum));
+}
+
+static int probe_bcache (blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct bcache_super_block *bcs;
+
+ bcs = blkid_probe_get_sb(pr, mag, struct bcache_super_block);
+ if (!bcs)
+ return errno ? -errno : BLKID_PROBE_NONE;
+
+ if (!bcache_verify_checksum(pr, mag, bcs))
+ return BLKID_PROBE_NONE;
+
+ if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / 512)
+ return BLKID_PROBE_NONE;
+
+ if (blkid_probe_set_uuid(pr, bcs->uuid) < 0)
+ return BLKID_PROBE_NONE;
+
+ blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF);
+
+ return BLKID_PROBE_OK;
+}
+
+static void probe_bcachefs_sb_members(blkid_probe pr,
+ const struct bcachefs_super_block *bcs,
+ const struct bcachefs_sb_field *field,
+ uint8_t dev_idx)
+{
+ struct bcachefs_sb_field_members *members =
+ (struct bcachefs_sb_field_members *) field;
+ uint64_t sectors = 0;
+ uint8_t i;
+
+ if (BYTES(field) != offsetof(typeof(*members), members[bcs->nr_devices]))
+ return;
+
+ blkid_probe_set_uuid_as(pr, members->members[dev_idx].uuid, "UUID_SUB");
+
+ for (i = 0; i < bcs->nr_devices; i++) {
+ struct bcachefs_sb_member *member = &members->members[i];
+ sectors += le64_to_cpu(member->nbuckets) * le16_to_cpu(member->bucket_size);
+ }
+ blkid_probe_set_fssize(pr, sectors * BCACHEFS_SECTOR_SIZE);
+}
+
+static void probe_bcachefs_sb_disk_groups(blkid_probe pr,
+ const struct bcachefs_super_block *bcs,
+ const struct bcachefs_sb_field *field,
+ uint8_t dev_idx)
+{
+ struct bcachefs_sb_field_disk_groups *disk_groups =
+ (struct bcachefs_sb_field_disk_groups *) field;
+
+ if (BYTES(field) != offsetof(typeof(*disk_groups), disk_groups[bcs->nr_devices]))
+ return;
+
+ blkid_probe_set_id_label(pr, "LABEL_SUB",
+ disk_groups->disk_groups[dev_idx].label,
+ sizeof(disk_groups->disk_groups[dev_idx].label));
+}
+
+static int is_within_range(void *start, uint64_t size, void *end)
+{
+ ptrdiff_t diff;
+
+ if (start >= end)
+ return 0; // should not happen
+
+ diff = (unsigned char *) end - (unsigned char *) start;
+ return size <= (uint64_t) diff;
+}
+
+static void probe_bcachefs_sb_fields(blkid_probe pr, const struct bcachefs_super_block *bcs,
+ unsigned char *sb_start, unsigned char *sb_end)
+{
+ unsigned char *field_addr = sb_start + BCACHEFS_SB_FIELDS_OFF;
+
+ while (1) {
+ struct bcachefs_sb_field *field = (struct bcachefs_sb_field *) field_addr;
+ uint64_t field_size;
+ uint32_t type;
+
+ if (!is_within_range(field, sizeof(*field), sb_end))
+ break;
+
+ field_size = BYTES(field);
+
+ if (field_size < sizeof(*field))
+ break;
+
+ if (!is_within_range(field, field_size, sb_end))
+ break;
+
+ type = le32_to_cpu(field->type);
+ if (!type)
+ break;
+
+ if (type == BCACHEFS_SB_FIELD_TYPE_MEMBERS)
+ probe_bcachefs_sb_members(pr, bcs, field, bcs->dev_idx);
+
+ if (type == BCACHEFS_SB_FIELD_TYPE_DISK_GROUPS)
+ probe_bcachefs_sb_disk_groups(pr, bcs, field, bcs->dev_idx);
+
+ field_addr += BYTES(field);
+ }
+}
+
+static int bcachefs_validate_checksum(blkid_probe pr, const struct bcachefs_super_block *bcs,
+ unsigned char *sb, unsigned char *sb_end)
+{
+ uint8_t checksum_type = be64_to_cpu(bcs->flags[0]) >> 58;
+ unsigned char *checksummed_data_start = sb + sizeof(bcs->csum);
+ size_t checksummed_data_size = sb_end - checksummed_data_start;
+
+ switch (checksum_type) {
+ case BCACHEFS_SB_CSUM_TYPE_NONE:
+ return 1;
+ case BCACHEFS_SB_CSUM_TYPE_CRC32C: {
+ uint32_t crc = crc32c(~0LL, checksummed_data_start, checksummed_data_size) ^ ~0LL;
+ return blkid_probe_verify_csum(pr, crc, le32_to_cpu(bcs->csum.crc32c));
+ }
+ case BCACHEFS_SB_CSUM_TYPE_CRC64: {
+ uint64_t crc = ul_crc64_we(checksummed_data_start, checksummed_data_size);
+ return blkid_probe_verify_csum(pr, crc, le64_to_cpu(bcs->csum.crc64));
+ }
+ case BCACHEFS_SB_CSUM_TYPE_XXHASH: {
+ XXH64_hash_t xxh64 = XXH64(checksummed_data_start, checksummed_data_size, 0);
+ return blkid_probe_verify_csum(pr, xxh64, le64_to_cpu(bcs->csum.xxh64));
+ }
+ default:
+ DBG(LOWPROBE, ul_debug("bcachefs: unknown checksum type %d, ignoring.", checksum_type));
+ return 1;
+ }
+}
+
+static int probe_bcachefs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct bcachefs_super_block *bcs;
+ unsigned char *sb, *sb_end;
+ uint64_t sb_size, blocksize;
+ uint16_t version;
+
+ bcs = blkid_probe_get_sb(pr, mag, struct bcachefs_super_block);
+ if (!bcs)
+ return errno ? -errno : BLKID_PROBE_NONE;
+
+ if (le64_to_cpu(bcs->offset) != BCACHE_SB_OFF / BCACHEFS_SECTOR_SIZE)
+ return BLKID_PROBE_NONE;
+
+ if (bcs->nr_devices == 0 || bcs->dev_idx >= bcs->nr_devices)
+ return BLKID_PROBE_NONE;
+
+ sb_size = BCACHEFS_SB_FIELDS_OFF + BYTES(bcs);
+
+ if (sb_size > BCACHEFS_SB_MAX_SIZE)
+ return BLKID_PROBE_NONE;
+
+ if (bcs->layout.sb_max_size_bits > BCACHEFS_SB_MAX_SIZE_SHIFT)
+ return BLKID_PROBE_NONE;
+
+ if (sb_size > (BCACHEFS_SECTOR_SIZE << bcs->layout.sb_max_size_bits))
+ return BLKID_PROBE_NONE;
+
+ sb = blkid_probe_get_sb_buffer(pr, mag, sb_size);
+ if (!sb)
+ return BLKID_PROBE_NONE;
+ sb_end = sb + sb_size;
+
+ if (!bcachefs_validate_checksum(pr, bcs, sb, sb_end))
+ return BLKID_PROBE_NONE;
+
+ blkid_probe_set_uuid(pr, bcs->user_uuid);
+ blkid_probe_set_label(pr, bcs->label, sizeof(bcs->label));
+ version = le16_to_cpu(bcs->version);
+ blkid_probe_sprintf_version(pr, "%"PRIu16".%"PRIu16,
+ BCH_VERSION_MAJOR(version),
+ BCH_VERSION_MINOR(version));
+ blocksize = le16_to_cpu(bcs->block_size);
+ blkid_probe_set_block_size(pr, blocksize * BCACHEFS_SECTOR_SIZE);
+ blkid_probe_set_fsblocksize(pr, blocksize * BCACHEFS_SECTOR_SIZE);
+ blkid_probe_set_wiper(pr, 0, BCACHE_SB_OFF);
+
+ probe_bcachefs_sb_fields(pr, bcs, sb, sb_end);
+
+ return BLKID_PROBE_OK;
+}
+
+const struct blkid_idinfo bcache_idinfo =
+{
+ .name = "bcache",
+ .usage = BLKID_USAGE_OTHER,
+ .probefunc = probe_bcache,
+ .minsz = 8192,
+ .magics =
+ {
+ {
+ .magic = BCACHE_SB_MAGIC,
+ .len = BCACHE_SB_MAGIC_LEN,
+ .kboff = BCACHE_SB_KBOFF,
+ .sboff = BCACHE_SB_MAGIC_OFF
+ },
+ { NULL }
+ }
+};
+
+const struct blkid_idinfo bcachefs_idinfo =
+{
+ .name = "bcachefs",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_bcachefs,
+ .minsz = 256 * BCACHEFS_SECTOR_SIZE,
+ .magics = {
+ {
+ .magic = BCACHE_SB_MAGIC,
+ .len = BCACHE_SB_MAGIC_LEN,
+ .kboff = BCACHE_SB_KBOFF,
+ .sboff = BCACHE_SB_MAGIC_OFF,
+ },
+ {
+ .magic = BCACHEFS_SB_MAGIC,
+ .len = BCACHE_SB_MAGIC_LEN,
+ .kboff = BCACHE_SB_KBOFF,
+ .sboff = BCACHE_SB_MAGIC_OFF,
+ },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/befs.c b/libblkid/src/superblocks/befs.c
new file mode 100644
index 0000000..5112d44
--- /dev/null
+++ b/libblkid/src/superblocks/befs.c
@@ -0,0 +1,550 @@
+/*
+ * 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,
+ ((uint64_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))
+ + ((uint64_t) FS16_TO_CPU(br->start, fs_le)
+ << FS32_TO_CPU(bs->block_shift, fs_le)),
+ (uint64_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,
+ ((uint64_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))
+ + ((uint64_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);
+ 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);
+ start -= br_len;
+ }
+ } else if (start < (int64_t) FS64_TO_CPU(ds->max_double_indirect_range, fs_le)) {
+ struct block_run *br;
+ int64_t max_br, 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;
+
+ if (di_index >= br_per_di_br)
+ return NULL; /* Corrupt? */
+
+ br = (struct block_run *) get_block_run(pr, bs,
+ &ds->double_indirect, fs_le);
+ if (!br)
+ return NULL;
+
+ max_br = ((int64_t)FS16_TO_CPU(br[di_index].len, fs_le)
+ << FS32_TO_CPU(bs->block_shift, fs_le))
+ / sizeof(struct block_run);
+
+ if (i_index >= max_br)
+ return NULL; /* Corrupt? */
+
+ 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;
+}
+
+#define BAD_KEYS -2
+
+static int32_t compare_keys(const char keys1[], uint16_t keylengths1[],
+ int32_t index, const char *key2,
+ uint16_t keylength2, uint16_t all_key_length,
+ int fs_le)
+{
+ const char *key1;
+ uint16_t keylength1, keystart1;
+ int32_t result;
+
+ keystart1 = index == 0 ? 0 : FS16_TO_CPU(keylengths1[index - 1], fs_le);
+ keylength1 = FS16_TO_CPU(keylengths1[index], fs_le) - keystart1;
+
+ if (keystart1 + keylength1 > all_key_length)
+ return BAD_KEYS; /* Corrupt? */
+
+ key1 = &keys1[keystart1];
+
+ result = strncmp(key1, key2, min(keylength1, keylength2));
+
+ if (result == 0)
+ return keylength1 - keylength2;
+
+ if (result < 0) /* Don't clash with BAD_KEYS */
+ result = -1;
+
+ 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;
+ uint32_t bn_size, all_key_count, all_key_length;
+ uint32_t keylengths_offset, values_offset;
+ int32_t first, last, mid, cmp;
+ int loop_detect = 0;
+
+ errno = 0;
+ bh = (struct bplustree_header *) get_tree_node(pr, bs, &bi->data, 0,
+ sizeof(struct bplustree_header), fs_le);
+ if (!bh)
+ return errno ? -errno : -ENOENT;
+
+ if ((int32_t) FS32_TO_CPU(bh->magic, fs_le) != BPLUSTREE_MAGIC)
+ return -ENOENT;
+
+ node_pointer = FS64_TO_CPU(bh->root_node_pointer, fs_le);
+ bn_size = FS32_TO_CPU(bh->node_size, fs_le);
+
+ if (bn_size < sizeof(struct bplustree_node))
+ return -ENOENT; /* Corrupt? */
+
+ do {
+ errno = 0;
+
+ bn = (struct bplustree_node *) get_tree_node(pr, bs, &bi->data,
+ node_pointer, bn_size, fs_le);
+ if (!bn)
+ return errno ? -errno : -ENOENT;
+
+ all_key_count = FS16_TO_CPU(bn->all_key_count, fs_le);
+ all_key_length = FS16_TO_CPU(bn->all_key_length, fs_le);
+ keylengths_offset =
+ (sizeof(struct bplustree_node) + all_key_length
+ + sizeof(int64_t) - 1) & ~(sizeof(int64_t) - 1);
+ values_offset = keylengths_offset +
+ all_key_count * sizeof(uint16_t);
+
+ if (values_offset + all_key_count * sizeof(uint64_t) > bn_size)
+ return -ENOENT; /* Corrupt? */
+
+ keylengths = (uint16_t *) ((uint8_t *) bn + keylengths_offset);
+ values = (int64_t *) ((uint8_t *) bn + values_offset);
+
+ first = 0;
+ mid = 0;
+ last = all_key_count - 1;
+
+ cmp = compare_keys(bn->name, keylengths, last, key,
+ strlen(key), all_key_length, fs_le);
+ if (cmp == BAD_KEYS)
+ return -ENOENT;
+
+ if (cmp == 0) {
+ if ((int64_t) FS64_TO_CPU(bn->overflow_link, fs_le)
+ == BPLUSTREE_NULL)
+ return FS64_TO_CPU(values[last], fs_le);
+
+ 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),
+ all_key_length, fs_le);
+ if (cmp == BAD_KEYS)
+ return -ENOENT;
+
+ if (cmp == 0) {
+ if ((int64_t) FS64_TO_CPU(bn->overflow_link,
+ fs_le) == BPLUSTREE_NULL)
+ return FS64_TO_CPU(values[mid],
+ fs_le);
+ break;
+ }
+
+ 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 (++loop_detect < 100 &&
+ (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;
+ uint64_t bi_size, offset, sd_size, sd_total_size;
+
+ bi = (struct befs_inode *) get_block_run(pr, bs, &bs->root_dir, fs_le);
+ if (!bi)
+ return errno ? -errno : BLKID_PROBE_NONE;
+
+ if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
+ return BLKID_PROBE_NONE;
+
+ bi_size = (uint64_t)FS16_TO_CPU(bs->root_dir.len, fs_le) <<
+ FS32_TO_CPU(bs->block_shift, fs_le);
+ sd_total_size = min(bi_size - sizeof(struct befs_inode),
+ (uint64_t)FS32_TO_CPU(bi->inode_size, fs_le));
+
+ offset = 0;
+
+ while (offset + sizeof(struct small_data) <= sd_total_size) {
+ sd = (struct small_data *) ((uint8_t *)bi->small_data + offset);
+ sd_size = sizeof(struct small_data)
+ + FS16_TO_CPU(sd->name_size, fs_le) + 3
+ + FS16_TO_CPU(sd->data_size, fs_le) + 1;
+
+ if (offset + sd_size > sd_total_size)
+ break;
+
+ 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;
+ }
+
+ 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;
+
+ offset += sd_size;
+ }
+
+ 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 errno ? -errno : BLKID_PROBE_NONE;
+
+ if (FS32_TO_CPU(bi->magic1, fs_le) != INODE_MAGIC1)
+ return BLKID_PROBE_NONE;
+
+ value = get_key_value(pr, bs, bi, KEY_NAME, fs_le);
+ if (value < 0)
+ return value == -ENOENT ? BLKID_PROBE_NONE : value;
+
+ 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 errno ? -errno : BLKID_PROBE_NONE;
+
+ 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 errno ? -errno : BLKID_PROBE_NONE;
+
+ *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;
+ uint32_t block_size, block_shift;
+ 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 errno ? -errno : BLKID_PROBE_NONE;
+
+ 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 BLKID_PROBE_NONE;
+
+ block_size = FS32_TO_CPU(bs->block_size, fs_le);
+ block_shift = FS32_TO_CPU(bs->block_shift, fs_le);
+
+ if (block_shift < 10 || block_shift > 13 ||
+ block_size != 1U << block_shift)
+ return BLKID_PROBE_NONE;
+
+ if (FS32_TO_CPU(bs->ag_shift, fs_le) > 64)
+ return BLKID_PROBE_NONE;
+
+ ret = get_uuid(pr, bs, &volume_id, fs_le);
+
+ if (ret != 0)
+ return ret;
+
+ /*
+ * all checks pass, set LABEL, VERSION and UUID
+ */
+ if (*bs->name != '\0')
+ 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));
+
+ blkid_probe_set_fsblocksize(pr, block_size);
+ blkid_probe_set_block_size(pr, block_size);
+ blkid_probe_set_fsendianness(pr,
+ fs_le ? BLKID_ENDIANNESS_LITTLE : BLKID_ENDIANNESS_BIG);
+
+ return BLKID_PROBE_OK;
+}
+
+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/libblkid/src/superblocks/bfs.c b/libblkid/src/superblocks/bfs.c
new file mode 100644
index 0000000..8a34c58
--- /dev/null
+++ b/libblkid/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/libblkid/src/superblocks/bitlocker.c b/libblkid/src/superblocks/bitlocker.c
new file mode 100644
index 0000000..a9a7bfb
--- /dev/null
+++ b/libblkid/src/superblocks/bitlocker.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2018 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"
+
+#define BDE_HDR_SIZE 512
+#define BDE_HDR_OFFSET 0
+
+struct bde_header_win7 {
+/* 0 */ unsigned char boot_entry_point[3];
+/* 3 */ unsigned char fs_signature[8];
+/* 11 */ unsigned char __dummy1[67 - 11];
+/* 67 */ uint32_t volume_serial; /* NTFS uses 64bit serial number */
+/* 71 */ unsigned char volume_label[11]; /* "NO NAME\x20\x20\x20\x20" only */
+/* 82 */ unsigned char __dummy2[160 - 82];
+/* 160 */ unsigned char guid[16]; /* BitLocker specific GUID */
+/* 176 */ uint64_t fve_metadata_offset;
+} __attribute__((packed));
+
+
+struct bde_header_togo {
+/* 0 */ unsigned char boot_entry_point[3];
+/* 3 */ unsigned char fs_signature[8];
+/* 11 */ unsigned char __dummy[424 - 11];
+/* 424 */ unsigned char guid[16];
+/* 440 */ uint64_t fve_metadata_offset;
+} __attribute__((packed));
+
+
+struct bde_fve_metadata {
+/* 0 */ unsigned char signature[8];
+/* 8 */ uint16_t size;
+/* 10 */ uint16_t version;
+};
+
+enum {
+ BDE_VERSION_VISTA = 0,
+ BDE_VERSION_WIN7,
+ BDE_VERSION_TOGO
+};
+
+#define BDE_MAGIC_VISTA "\xeb\x52\x90-FVE-FS-"
+#define BDE_MAGIC_WIN7 "\xeb\x58\x90-FVE-FS-"
+#define BDE_MAGIC_TOGO "\xeb\x58\x90MSWIN4.1"
+
+#define BDE_MAGIC_FVE "-FVE-FS-"
+
+static int get_bitlocker_type(const unsigned char *buf)
+{
+ size_t i;
+ static const char *map[] = {
+ [BDE_VERSION_VISTA] = BDE_MAGIC_VISTA,
+ [BDE_VERSION_WIN7] = BDE_MAGIC_WIN7,
+ [BDE_VERSION_TOGO] = BDE_MAGIC_TOGO
+ };
+
+ for (i = 0; i < ARRAY_SIZE(map); i++) {
+ if (memcmp(buf, map[i], 11) == 0)
+ return (int) i;
+ }
+
+ return -1;
+}
+
+/* Returns: < 0 error, 1 nothing, 0 success
+ */
+static int get_bitlocker_headers(blkid_probe pr,
+ int *type,
+ const unsigned char **buf_hdr,
+ const unsigned char **buf_fve)
+{
+
+ const unsigned char *buf;
+ const struct bde_fve_metadata *fve;
+ uint64_t off = 0;
+ int kind;
+
+ if (buf_hdr)
+ *buf_hdr = NULL;
+ if (buf_fve)
+ *buf_fve = NULL;
+ if (type)
+ *type = -1;
+
+ buf = blkid_probe_get_buffer(pr, BDE_HDR_OFFSET, BDE_HDR_SIZE);
+ if (!buf)
+ return errno ? -errno : 1;
+
+ kind = get_bitlocker_type(buf);
+
+ /* Check BitLocker header */
+ switch (kind) {
+ case BDE_VERSION_WIN7:
+ off = le64_to_cpu(((const struct bde_header_win7 *) buf)->fve_metadata_offset);
+ break;
+ case BDE_VERSION_TOGO:
+ off = le64_to_cpu(((const struct bde_header_togo *) buf)->fve_metadata_offset);
+ break;
+ case BDE_VERSION_VISTA:
+ goto done;
+ default:
+ goto nothing;
+ }
+
+ if (!off || off % 64)
+ goto nothing;
+ if (buf_hdr)
+ *buf_hdr = buf;
+
+ /* Check Bitlocker FVE metadata header */
+ buf = blkid_probe_get_buffer(pr, off, sizeof(struct bde_fve_metadata));
+ if (!buf)
+ return errno ? -errno : 1;
+
+ fve = (const struct bde_fve_metadata *) buf;
+ if (memcmp(fve->signature, BDE_MAGIC_FVE, sizeof(fve->signature)) != 0)
+ goto nothing;
+ if (buf_fve)
+ *buf_fve = buf;
+done:
+ if (type)
+ *type = kind;
+ return 0;
+nothing:
+ return 1;
+}
+
+/*
+ * This is used by vFAT and NTFS prober to avoid collisions with bitlocker.
+ */
+int blkid_probe_is_bitlocker(blkid_probe pr)
+{
+ return get_bitlocker_headers(pr, NULL, NULL, NULL) == 0;
+}
+
+static int probe_bitlocker(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ const unsigned char *buf_fve = NULL;
+ const unsigned char *buf_hdr = NULL;
+ int rc, kind;
+
+ rc = get_bitlocker_headers(pr, &kind, &buf_hdr, &buf_fve);
+ if (rc)
+ return rc;
+
+ if (kind == BDE_VERSION_WIN7) {
+ const struct bde_header_win7 *hdr = (const struct bde_header_win7 *) buf_hdr;
+
+ /* Unfortunately, it seems volume_serial is always zero */
+ blkid_probe_sprintf_uuid(pr,
+ (const unsigned char *) &hdr->volume_serial,
+ sizeof(hdr->volume_serial),
+ "%016d", le32_to_cpu(hdr->volume_serial));
+ }
+
+ if (buf_fve) {
+ const struct bde_fve_metadata *fve = (const struct bde_fve_metadata *) buf_fve;
+
+ blkid_probe_sprintf_version(pr, "%d", fve->version);
+ }
+ return 0;
+}
+
+/* See header details:
+ * https://github.com/libyal/libbde/blob/master/documentation/BitLocker%20Drive%20Encryption%20(BDE)%20format.asciidoc
+ */
+const struct blkid_idinfo bitlocker_idinfo =
+{
+ .name = "BitLocker",
+ .usage = BLKID_USAGE_CRYPTO,
+ .probefunc = probe_bitlocker,
+ .magics =
+ {
+ { .magic = BDE_MAGIC_VISTA, .len = 11 },
+ { .magic = BDE_MAGIC_WIN7, .len = 11 },
+ { .magic = BDE_MAGIC_TOGO, .len = 11 },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/bluestore.c b/libblkid/src/superblocks/bluestore.c
new file mode 100644
index 0000000..2ff1f35
--- /dev/null
+++ b/libblkid/src/superblocks/bluestore.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 by Kenneth Van Alstyne <kvanals@kvanals.org>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ *
+ * Ceph BlueStore is one of the supported storage
+ * methods for Object Storage Devices (OSDs).
+ * This is used to detect the backing block devices
+ * used for these types of OSDs in a Ceph Cluster.
+ *
+ */
+#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 "bitops.h"
+#include "superblocks.h"
+
+#define BLUESTORE_MAGIC_L 22
+
+struct bluestore_phdr {
+ uint8_t magic[BLUESTORE_MAGIC_L];
+} __attribute__((packed));
+
+static int probe_bluestore(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct bluestore_phdr *header;
+
+ header = blkid_probe_get_sb(pr, mag, struct bluestore_phdr);
+ if (header == NULL)
+ return errno ? -errno : 1;
+
+ return 0;
+}
+
+const struct blkid_idinfo bluestore_idinfo =
+{
+ .name = "ceph_bluestore",
+ .usage = BLKID_USAGE_OTHER,
+ .probefunc = probe_bluestore,
+ .magics =
+ {
+ { .magic = "bluestore block device", .len = 22 },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/btrfs.c b/libblkid/src/superblocks/btrfs.c
new file mode 100644
index 0000000..b9cf4bd
--- /dev/null
+++ b/libblkid/src/superblocks/btrfs.c
@@ -0,0 +1,326 @@
+/*
+ * 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 <stdbool.h>
+#include <assert.h>
+#include <inttypes.h>
+
+#ifdef HAVE_LINUX_BLKZONED_H
+#include <linux/blkzoned.h>
+#endif
+
+#include "superblocks.h"
+#include "crc32c.h"
+#include "sha256.h"
+#include "xxhash.h"
+
+enum btrfs_super_block_csum_type {
+ BTRFS_SUPER_BLOCK_CSUM_TYPE_CRC32C = 0,
+ BTRFS_SUPER_BLOCK_CSUM_TYPE_XXHASH = 1,
+ BTRFS_SUPER_BLOCK_CSUM_TYPE_SHA256 = 2,
+};
+
+union btrfs_super_block_csum {
+ uint8_t bytes[32];
+ uint32_t crc32c;
+ XXH64_hash_t xxh64;
+ uint8_t sha256[UL_SHA256LENGTH];
+};
+
+struct btrfs_super_block {
+ union btrfs_super_block_csum csum;
+ 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];
+ uint8_t padding[3541]; /* pad to BTRFS_SUPER_INFO_SIZE for csum calculation */
+} __attribute__ ((__packed__));
+
+#define BTRFS_SUPER_INFO_SIZE 4096
+
+/* Number of superblock log zones */
+#define BTRFS_NR_SB_LOG_ZONES 2
+
+/* Introduce some macros and types to unify the code with kernel side */
+#define SECTOR_SHIFT 9
+
+typedef uint64_t sector_t;
+
+#ifdef HAVE_LINUX_BLKZONED_H
+static int sb_write_pointer(blkid_probe pr, struct blk_zone *zones, uint64_t *wp_ret)
+{
+ bool empty[BTRFS_NR_SB_LOG_ZONES];
+ bool full[BTRFS_NR_SB_LOG_ZONES];
+ sector_t sector;
+
+ assert(zones[0].type != BLK_ZONE_TYPE_CONVENTIONAL &&
+ zones[1].type != BLK_ZONE_TYPE_CONVENTIONAL);
+
+ empty[0] = zones[0].cond == BLK_ZONE_COND_EMPTY;
+ empty[1] = zones[1].cond == BLK_ZONE_COND_EMPTY;
+ full[0] = zones[0].cond == BLK_ZONE_COND_FULL;
+ full[1] = zones[1].cond == BLK_ZONE_COND_FULL;
+
+ /*
+ * Possible states of log buffer zones
+ *
+ * Empty[0] In use[0] Full[0]
+ * Empty[1] * x 0
+ * In use[1] 0 x 0
+ * Full[1] 1 1 C
+ *
+ * Log position:
+ * *: Special case, no superblock is written
+ * 0: Use write pointer of zones[0]
+ * 1: Use write pointer of zones[1]
+ * C: Compare super blocks from zones[0] and zones[1], use the latest
+ * one determined by generation
+ * x: Invalid state
+ */
+
+ if (empty[0] && empty[1]) {
+ /* Special case to distinguish no superblock to read */
+ *wp_ret = zones[0].start << SECTOR_SHIFT;
+ return -ENOENT;
+ } else if (full[0] && full[1]) {
+ /* Compare two super blocks */
+ struct btrfs_super_block *super[BTRFS_NR_SB_LOG_ZONES];
+ int i;
+
+ for (i = 0; i < BTRFS_NR_SB_LOG_ZONES; i++) {
+ uint64_t bytenr;
+
+ bytenr = ((zones[i].start + zones[i].len)
+ << SECTOR_SHIFT) - BTRFS_SUPER_INFO_SIZE;
+
+ super[i] = (struct btrfs_super_block *)
+ blkid_probe_get_buffer(pr, bytenr, BTRFS_SUPER_INFO_SIZE);
+ if (!super[i])
+ return -EIO;
+ DBG(LOWPROBE, ul_debug("(btrfs) checking #%d zone "
+ "[start=%" PRIu64", len=%" PRIu64", sb-offset=%" PRIu64"]",
+ i, (uint64_t) zones[i].start,
+ (uint64_t) zones[i].len, bytenr));
+ }
+
+ if (super[0]->generation > super[1]->generation)
+ sector = zones[1].start;
+ else
+ sector = zones[0].start;
+ } else if (!full[0] && (empty[1] || full[1])) {
+ sector = zones[0].wp;
+ } else if (full[0]) {
+ sector = zones[1].wp;
+ } else {
+ return -EUCLEAN;
+ }
+ *wp_ret = sector << SECTOR_SHIFT;
+
+ DBG(LOWPROBE, ul_debug("(btrfs) write pointer: %" PRIu64" sector", sector));
+ return 0;
+}
+
+static int sb_log_offset(blkid_probe pr, uint64_t *bytenr_ret)
+{
+ uint32_t zone_num = 0;
+ uint32_t zone_size_sector;
+ struct blk_zone_report *rep;
+ struct blk_zone *zones;
+ int ret;
+ int i;
+ uint64_t wp;
+
+
+ zone_size_sector = pr->zone_size >> SECTOR_SHIFT;
+ rep = blkdev_get_zonereport(pr->fd, zone_num * zone_size_sector, 2);
+ if (!rep) {
+ ret = -errno;
+ goto out;
+ }
+ zones = (struct blk_zone *)(rep + 1);
+
+ /*
+ * Use the head of the first conventional zone, if the zones
+ * contain one.
+ */
+ for (i = 0; i < BTRFS_NR_SB_LOG_ZONES; i++) {
+ if (zones[i].type == BLK_ZONE_TYPE_CONVENTIONAL) {
+ DBG(LOWPROBE, ul_debug("(btrfs) checking conventional zone"));
+ *bytenr_ret = zones[i].start << SECTOR_SHIFT;
+ ret = 0;
+ goto out;
+ }
+ }
+
+ ret = sb_write_pointer(pr, zones, &wp);
+ if (ret != -ENOENT && ret) {
+ ret = 1;
+ goto out;
+ }
+ if (ret != -ENOENT) {
+ if (wp == zones[0].start << SECTOR_SHIFT)
+ wp = (zones[1].start + zones[1].len) << SECTOR_SHIFT;
+ wp -= BTRFS_SUPER_INFO_SIZE;
+ }
+ *bytenr_ret = wp;
+
+ ret = 0;
+out:
+ free(rep);
+
+ return ret;
+}
+#endif
+
+static int btrfs_verify_csum(blkid_probe pr, const struct btrfs_super_block *bfs)
+{
+ uint16_t csum_type = le16_to_cpu(bfs->csum_type);
+ const void *csum_data = (char *) bfs + sizeof(bfs->csum);
+ size_t csum_data_size = sizeof(*bfs) - sizeof(bfs->csum);
+ switch (csum_type) {
+ case BTRFS_SUPER_BLOCK_CSUM_TYPE_CRC32C: {
+ uint32_t crc = ~crc32c(~0L, csum_data, csum_data_size);
+ return blkid_probe_verify_csum(pr, crc,
+ le32_to_cpu(bfs->csum.crc32c));
+ }
+ case BTRFS_SUPER_BLOCK_CSUM_TYPE_XXHASH: {
+ XXH64_hash_t xxh64 = XXH64(csum_data, csum_data_size, 0);
+ return blkid_probe_verify_csum(pr, xxh64,
+ le64_to_cpu(bfs->csum.xxh64));
+ }
+ case BTRFS_SUPER_BLOCK_CSUM_TYPE_SHA256: {
+ uint8_t sha256[UL_SHA256LENGTH];
+ ul_SHA256(sha256, csum_data, csum_data_size);
+ return blkid_probe_verify_csum_buf(pr, UL_SHA256LENGTH,
+ sha256, bfs->csum.sha256);
+ }
+ default:
+ DBG(LOWPROBE, ul_debug("(btrfs) unknown checksum type %d, skipping validation",
+ csum_type));
+ return 1;
+ }
+}
+
+static int probe_btrfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct btrfs_super_block *bfs;
+
+ if (pr->zone_size) {
+#ifdef HAVE_LINUX_BLKZONED_H
+ uint64_t offset = 0;
+ int ret;
+
+ ret = sb_log_offset(pr, &offset);
+ if (ret)
+ return ret;
+ bfs = (struct btrfs_super_block *)
+ blkid_probe_get_buffer(pr, offset,
+ sizeof(struct btrfs_super_block));
+#else
+ /* Nothing can be done */
+ return 1;
+#endif
+ } else {
+ bfs = blkid_probe_get_sb(pr, mag, struct btrfs_super_block);
+ }
+ if (!bfs)
+ return errno ? -errno : 1;
+
+ if (!btrfs_verify_csum(pr, bfs))
+ return 1;
+
+ /* Invalid sector size; total_bytes would be bogus. */
+ if (!le32_to_cpu(bfs->sectorsize))
+ 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");
+ blkid_probe_set_fsblocksize(pr, le32_to_cpu(bfs->sectorsize));
+ blkid_probe_set_block_size(pr, le32_to_cpu(bfs->sectorsize));
+
+ uint32_t sectorsize_log = 31 -
+ __builtin_clz(le32_to_cpu(bfs->sectorsize));
+ blkid_probe_set_fslastblock(pr,
+ le64_to_cpu(bfs->total_bytes) >> sectorsize_log);
+
+ /* The size is calculated without the RAID factor. It could not be
+ * obtained from the superblock as it is property of device tree.
+ * Without the factor we would show fs size with the redundant data. The
+ * acquisition of the factor will require additional parsing of btrfs
+ * tree.
+ */
+ blkid_probe_set_fssize(pr, le64_to_cpu(bfs->total_bytes));
+
+ 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, .sboff = 0x40, .kboff = 64 },
+ /* For zoned btrfs */
+ { .magic = "_BHRfS_M", .len = 8, .sboff = 0x40,
+ .is_zoned = 1, .zonenum = 0, .kboff_inzone = 0 },
+ { .magic = "_BHRfS_M", .len = 8, .sboff = 0x40,
+ .is_zoned = 1, .zonenum = 1, .kboff_inzone = 0 },
+ { NULL }
+ }
+};
+
diff --git a/libblkid/src/superblocks/cramfs.c b/libblkid/src/superblocks/cramfs.c
new file mode 100644
index 0000000..2a87acd
--- /dev/null
+++ b/libblkid/src/superblocks/cramfs.c
@@ -0,0 +1,112 @@
+/*
+ * 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"
+#include "crc32.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));
+
+#define CRAMFS_FLAG_FSID_VERSION_2 0x00000001 /* fsid version #2 */
+
+static int cramfs_is_little_endian(const struct blkid_idmag *mag)
+{
+ assert(mag->len == 4);
+ return memcmp(mag->magic, "\x45\x3d\xcd\x28", 4) == 0;
+}
+
+static uint32_t cfs32_to_cpu(int le, uint32_t value)
+{
+ if (le)
+ return le32_to_cpu(value);
+ else
+ return be32_to_cpu(value);
+}
+
+static int cramfs_verify_csum(blkid_probe pr, const struct blkid_idmag *mag,
+ struct cramfs_super *cs, int le)
+{
+ uint32_t crc, expected, csummed_size;
+ unsigned char *csummed;
+
+ expected = cfs32_to_cpu(le, cs->info.crc);
+ csummed_size = cfs32_to_cpu(le, cs->size);
+
+ if (csummed_size > (1 << 16)
+ || csummed_size < sizeof(struct cramfs_super))
+ return 0;
+
+ csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size);
+ if (!csummed)
+ return 0;
+ memset(csummed + offsetof(struct cramfs_super, info.crc), 0, sizeof(uint32_t));
+
+ crc = ~ul_crc32(~0LL, csummed, csummed_size);
+
+ return blkid_probe_verify_csum(pr, crc, expected);
+}
+
+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 errno ? -errno : 1;
+
+ int le = cramfs_is_little_endian(mag);
+ int v2 = cfs32_to_cpu(le, cs->flags) & CRAMFS_FLAG_FSID_VERSION_2;
+
+ if (v2 && !cramfs_verify_csum(pr, mag, cs, le))
+ return 1;
+
+ blkid_probe_set_label(pr, cs->name, sizeof(cs->name));
+ blkid_probe_set_fssize(pr, cfs32_to_cpu(le, cs->size));
+ blkid_probe_sprintf_version(pr, "%d", v2 ? 2 : 1);
+ blkid_probe_set_fsendianness(pr,
+ le ? BLKID_ENDIANNESS_LITTLE : BLKID_ENDIANNESS_BIG);
+ return 0;
+}
+
+const struct blkid_idinfo cramfs_idinfo =
+{
+ .name = "cramfs",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_cramfs,
+ .magics =
+ {
+ { .magic = "\x45\x3d\xcd\x28", .len = 4 },
+ { .magic = "\x28\xcd\x3d\x45", .len = 4 },
+ { NULL }
+ }
+};
+
+
diff --git a/libblkid/src/superblocks/cs_fvault2.c b/libblkid/src/superblocks/cs_fvault2.c
new file mode 100644
index 0000000..ef2b567
--- /dev/null
+++ b/libblkid/src/superblocks/cs_fvault2.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2022 Milan Broz <gmazyland@gmail.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include "superblocks.h"
+#include "crc32c.h"
+
+/*
+ * For header details, see:
+ * https://github.com/libyal/libfvde/blob/main/documentation/FileVault%20Drive%20Encryption%20(FVDE).asciidoc
+ * https://is.muni.cz/auth/th/p0aok/thesis.pdf
+ */
+
+/* Apple Core Storage magic bytes */
+#define CS_MAGIC "CS"
+
+struct crc32_checksum {
+ uint32_t value;
+ uint32_t seed;
+} __attribute__((packed));
+
+/*
+ * The superblock structure describes "physical volume"; Core Storage
+ * then uses another abstractions above, similar to LVM.
+ * After activation through dm-crypt, filesystem (usually HFS+) is on top.
+ * The filesystem block size and used data size cannot be directly derived from
+ * this superblock structure without parsing other metadata blocks.
+ */
+
+struct cs_fvault2_sb {
+ struct crc32_checksum checksum;
+ uint16_t version;
+ uint16_t block_type;
+ uint8_t unknown1[52];
+ uint64_t ph_vol_size;
+ uint8_t unknown2[16];
+ uint16_t magic;
+ uint32_t checksum_algo;
+ uint8_t unknown3[2];
+ uint32_t block_size;
+ uint32_t metadata_size;
+ uint64_t disklbl_blkoff;
+ uint64_t other_md_blkoffs[3];
+ uint8_t unknown4[32];
+ uint32_t key_data_size;
+ uint32_t cipher;
+ uint8_t key_data[16];
+ uint8_t unknown5[112];
+ uint8_t ph_vol_uuid[16];
+ uint8_t unknown6[192];
+} __attribute__((packed));
+
+static int cs_fvault2_verify_csum(blkid_probe pr, const struct cs_fvault2_sb *sb)
+{
+ uint32_t seed = le32_to_cpu(sb->checksum.seed);
+ uint32_t crc = le32_to_cpu(sb->checksum.value);
+ unsigned char *buf = (unsigned char *)sb + sizeof(sb->checksum);
+ size_t buf_size = sizeof(*sb) - sizeof(sb->checksum);
+
+ return blkid_probe_verify_csum(pr, crc32c(seed, buf, buf_size), crc);
+}
+
+static int probe_cs_fvault2(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct cs_fvault2_sb *sb;
+
+ sb = blkid_probe_get_sb(pr, mag, struct cs_fvault2_sb);
+ if (!sb)
+ return errno ? -errno : BLKID_PROBE_NONE;
+
+ /* Apple Core storage Physical Volume Header; only type 1 checksum is supported */
+ if (le16_to_cpu(sb->version) != 1 ||
+ le32_to_cpu(sb->checksum_algo) != 1)
+ return BLKID_PROBE_NONE;
+
+ if (!cs_fvault2_verify_csum(pr, sb))
+ return BLKID_PROBE_NONE;
+
+ /* We support only block type 0x10 as it should be used for FileVault2 */
+ if (le16_to_cpu(sb->block_type) != 0x10 ||
+ le32_to_cpu(sb->key_data_size) != 16 ||
+ le32_to_cpu(sb->cipher) != 2 /* AES-XTS */)
+ return BLKID_PROBE_NONE;
+
+ blkid_probe_sprintf_version(pr, "%u", le16_to_cpu(sb->version));
+ blkid_probe_set_uuid(pr, sb->ph_vol_uuid);
+
+ return BLKID_PROBE_OK;
+}
+
+const struct blkid_idinfo cs_fvault2_idinfo =
+{
+ .name = "cs_fvault2",
+ .usage = BLKID_USAGE_CRYPTO,
+ .probefunc = probe_cs_fvault2,
+ .magics =
+ {
+ { .magic = CS_MAGIC, .len = 2, .sboff = 88 },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/ddf_raid.c b/libblkid/src/superblocks/ddf_raid.c
new file mode 100644
index 0000000..0b82e73
--- /dev/null
+++ b/libblkid/src/superblocks/ddf_raid.c
@@ -0,0 +1,141 @@
+/*
+ * 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 = 0, 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 errno ? -errno : 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)
+ return errno ? -errno : 1;
+
+ if (memcmp(buf, &ddf->signature, 4) != 0)
+ 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/libblkid/src/superblocks/drbd.c b/libblkid/src/superblocks/drbd.c
new file mode 100644
index 0000000..f360186
--- /dev/null
+++ b/libblkid/src/superblocks/drbd.c
@@ -0,0 +1,231 @@
+/*
+ * 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 support v08 and v09
+ */
+#define DRBD_MD_MAGIC_08 (DRBD_MAGIC+4)
+#define DRBD_MD_MAGIC_84_UNCLEAN (DRBD_MAGIC+5)
+#define DRBD_MD_MAGIC_09 (DRBD_MAGIC+6)
+/* there is no DRBD_MD_MAGIC_09_UNCLEAN */
+
+/*
+ * 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. */
+};
+
+
+/*
+ * Used by libblkid to avoid unnecessary padding at the end of the structs and
+ * too large unused structs in memory.
+ */
+#define DRBD_MD_OFFSET 4096
+
+/*
+ * user/shared/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];
+
+ /* Unnecessary for libblkid **
+ * char reserved[8 * 512 - (8*(UI_SIZE+3)+4*11)];
+ */
+};
+
+/*
+ * linux/drbd.h, v9 only
+ */
+#define DRBD_PEERS_MAX 32
+#define HISTORY_UUIDS DRBD_PEERS_MAX
+
+/*
+ * drbd-headers/drbd_meta_data.h
+ * Minor modifications wrt. types
+ */
+struct peer_dev_md_on_disk_9 {
+ uint64_t bitmap_uuid;
+ uint64_t bitmap_dagtag;
+ uint32_t flags;
+ int32_t bitmap_index;
+ uint32_t reserved_u32[2];
+} __attribute__((packed));
+
+struct meta_data_on_disk_9 {
+ uint64_t effective_size; /* last agreed size */
+ uint64_t current_uuid;
+ uint64_t reserved_u64[4]; /* to have the magic at the same position as in v07, and v08 */
+ uint64_t device_uuid;
+ uint32_t flags; /* MDF */
+ uint32_t magic;
+ uint32_t md_size_sect;
+ uint32_t al_offset; /* offset to this block */
+ uint32_t al_nr_extents; /* important for restoring the AL */
+ uint32_t bm_offset; /* offset to the bitmap, from here */
+ uint32_t bm_bytes_per_bit; /* BM_BLOCK_SIZE */
+ uint32_t la_peer_max_bio_size; /* last peer max_bio_size */
+ uint32_t bm_max_peers;
+ int32_t node_id;
+
+ /* see al_tr_number_to_on_disk_sector() */
+ uint32_t al_stripes;
+ uint32_t al_stripe_size_4k;
+
+ uint32_t reserved_u32[2];
+
+ struct peer_dev_md_on_disk_9 peers[DRBD_PEERS_MAX];
+ uint64_t history_uuids[HISTORY_UUIDS];
+
+ /* Unnecessary for libblkid **
+ * char padding[0] __attribute__((aligned(4096)));
+ */
+} __attribute__((packed));
+
+
+static int probe_drbd_84(blkid_probe pr)
+{
+ struct md_on_disk_08 *md;
+ off_t off;
+
+ off = pr->size - DRBD_MD_OFFSET;
+
+ /* 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 errno ? -errno : 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;
+}
+
+static int probe_drbd_90(blkid_probe pr)
+{
+ struct meta_data_on_disk_9 *md;
+ off_t off;
+
+ off = pr->size - DRBD_MD_OFFSET;
+
+ /*
+ * Smaller ones are certainly not DRBD9 devices.
+ * Recent utils even refuse to generate larger ones,
+ * keep this as a sufficient lower bound.
+ */
+ if (pr->size < 0x10000)
+ return 1;
+
+ md = (struct meta_data_on_disk_9 *)
+ blkid_probe_get_buffer(pr,
+ off,
+ sizeof(struct meta_data_on_disk_9));
+ if (!md)
+ return errno ? -errno : 1;
+
+ if (be32_to_cpu(md->magic) != DRBD_MD_MAGIC_09)
+ 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, "v09");
+
+ if (blkid_probe_set_magic(pr,
+ off + offsetof(struct meta_data_on_disk_9, magic),
+ sizeof(md->magic),
+ (unsigned char *) &md->magic))
+ return 1;
+
+ return 0;
+}
+
+static int probe_drbd(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ int ret;
+
+ ret = probe_drbd_84(pr);
+ if (ret <= 0) /* success or fatal (-errno) */
+ return ret;
+
+ return probe_drbd_90(pr);
+}
+
+const struct blkid_idinfo drbd_idinfo =
+{
+ .name = "drbd",
+ .usage = BLKID_USAGE_RAID,
+ .probefunc = probe_drbd,
+ .magics = BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/src/superblocks/drbdmanage.c b/libblkid/src/superblocks/drbdmanage.c
new file mode 100644
index 0000000..d56c414
--- /dev/null
+++ b/libblkid/src/superblocks/drbdmanage.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015 by Philipp Marek <philipp.marek@linbit.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * DRBD is a blocklevel replication solution in the Linux kernel,
+ * upstream since 2.6.33. (See http://drbd.linbit.com/)
+ * DRBDmanage is a configuration frontend that assists in
+ * creating/deleting/modifying DRBD resources across multiple machines
+ * (a DRBDmanage "cluster"); this file detects its control volume,
+ * which is replicated (via DRBD 9) on some of the nodes.
+ */
+#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 "bitops.h"
+#include "superblocks.h"
+
+struct drbdmanage_hdr {
+ unsigned char magic[11];
+ unsigned char uuid[32];
+ unsigned char lf;
+} __attribute__ ((packed));
+
+struct drbdmanage_pers {
+ char magic[4];
+ uint32_t version_le;
+} __attribute__ ((packed));
+
+
+static const char persistence_magic[4] = { '\x1a', '\xdb', '\x98', '\xa2' };
+
+
+static int probe_drbdmanage(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ struct drbdmanage_hdr *hdr;
+ unsigned char *cp;
+ struct drbdmanage_pers *prs;
+
+ hdr = (struct drbdmanage_hdr*)
+ blkid_probe_get_buffer(pr, 0, sizeof(*hdr));
+ if (!hdr)
+ return errno ? -errno : 1;
+
+ for(cp=hdr->uuid; cp<&hdr->lf; cp++)
+ if (!isxdigit(*cp))
+ return 1;
+ if (hdr->lf != '\n')
+ return 1;
+
+ if (blkid_probe_strncpy_uuid(pr,
+ hdr->uuid, sizeof(hdr->uuid)))
+ return errno ? -errno : 1;
+
+ prs = (struct drbdmanage_pers*)
+ blkid_probe_get_buffer(pr, 0x1000, sizeof(*prs));
+ if (!prs)
+ return errno ? -errno : 1;
+
+ if (memcmp(prs->magic, persistence_magic, sizeof(prs->magic)) == 0 &&
+ blkid_probe_sprintf_version(pr, "%d", be32_to_cpu(prs->version_le)) != 0)
+ return errno ? -errno : 1;
+
+ return 0;
+}
+
+
+const struct blkid_idinfo drbdmanage_idinfo =
+{
+ .name = "drbdmanage_control_volume",
+ .usage = BLKID_USAGE_OTHER,
+ .probefunc = probe_drbdmanage,
+ .minsz = 64 * 1024,
+ .magics = {
+ { .magic = "$DRBDmgr=q", .len = 10, .sboff = 0 },
+ { NULL }
+ },
+};
+
diff --git a/libblkid/src/superblocks/drbdproxy_datalog.c b/libblkid/src/superblocks/drbdproxy_datalog.c
new file mode 100644
index 0000000..9a4c7db
--- /dev/null
+++ b/libblkid/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 errno ? -errno : 1;
+
+ blkid_probe_set_uuid(pr, lh->uuid);
+ blkid_probe_sprintf_version(pr, "v%"PRIu64, 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/libblkid/src/superblocks/erofs.c b/libblkid/src/superblocks/erofs.c
new file mode 100644
index 0000000..14d272e
--- /dev/null
+++ b/libblkid/src/superblocks/erofs.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 Gao Xiang
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License
+ *
+ * https://docs.kernel.org/filesystems/erofs.html
+ */
+#include <stddef.h>
+#include <string.h>
+
+#include "superblocks.h"
+#include "crc32c.h"
+
+#define EROFS_SUPER_OFFSET 1024
+#define EROFS_SB_KBOFF (EROFS_SUPER_OFFSET >> 10)
+#define EROFS_FEATURE_SB_CSUM (1 << 0)
+
+#define EROFS_SUPER_MAGIC_V1 "\xe2\xe1\xf5\xe0"
+#define EROFS_MAGIC_OFF 0
+
+/* All in little-endian */
+struct erofs_super_block {
+ uint32_t magic;
+ uint32_t checksum;
+ uint32_t feature_compat;
+ uint8_t blkszbits;
+ uint8_t reserved;
+
+ uint16_t root_nid;
+ uint64_t inos;
+
+ uint64_t build_time;
+ uint32_t build_time_nsec;
+ uint32_t blocks;
+ uint32_t meta_blkaddr;
+ uint32_t xattr_blkaddr;
+ uint8_t uuid[16];
+ uint8_t volume_name[16];
+ uint32_t feature_incompat;
+ uint8_t reserved2[44];
+} __attribute__((packed));
+
+static int erofs_verify_checksum(blkid_probe pr, const struct blkid_idmag *mag,
+ const struct erofs_super_block *sb)
+{
+ uint32_t expected, csum;
+ size_t csummed_size;
+ unsigned char *csummed;
+
+ if (!(le32_to_cpu(sb->feature_compat) & EROFS_FEATURE_SB_CSUM))
+ return 1;
+
+ expected = le32_to_cpu(sb->checksum);
+ csummed_size = (1U << sb->blkszbits) - EROFS_SUPER_OFFSET;
+
+ csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size);
+ if (!csummed)
+ return 0;
+
+ csum = ul_crc32c_exclude_offset(~0L, csummed, csummed_size,
+ offsetof(struct erofs_super_block, checksum),
+ sizeof_member(struct erofs_super_block, checksum));
+
+ return blkid_probe_verify_csum(pr, csum, expected);
+}
+
+static int probe_erofs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct erofs_super_block *sb;
+
+ sb = blkid_probe_get_sb(pr, mag, struct erofs_super_block);
+ if (!sb)
+ return errno ? -errno : BLKID_PROBE_NONE;
+
+ /* EROFS is restricted to 4KiB block size */
+ if (sb->blkszbits > 31 || (1U << sb->blkszbits) > 4096)
+ return BLKID_PROBE_NONE;
+
+ if (!erofs_verify_checksum(pr, mag, sb))
+ return BLKID_PROBE_NONE;
+
+ if (sb->volume_name[0])
+ blkid_probe_set_label(pr, (unsigned char *)sb->volume_name,
+ sizeof(sb->volume_name));
+
+ blkid_probe_set_uuid(pr, sb->uuid);
+ blkid_probe_set_fsblocksize(pr, 1U << sb->blkszbits);
+ blkid_probe_set_block_size(pr, 1U << sb->blkszbits);
+ blkid_probe_set_fssize(pr, (uint64_t) (1U << sb->blkszbits) * le32_to_cpu(sb->blocks));
+
+ return BLKID_PROBE_OK;
+}
+
+const struct blkid_idinfo erofs_idinfo =
+{
+ .name = "erofs",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_erofs,
+ .magics =
+ {
+ {
+ .magic = EROFS_SUPER_MAGIC_V1,
+ .len = 4,
+ .kboff = EROFS_SB_KBOFF,
+ .sboff = EROFS_MAGIC_OFF,
+ }, { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/exfat.c b/libblkid/src/superblocks/exfat.c
new file mode 100644
index 0000000..fda1ecd
--- /dev/null
+++ b/libblkid/src/superblocks/exfat.c
@@ -0,0 +1,259 @@
+/*
+ * 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 JumpBoot[3];
+ uint8_t FileSystemName[8];
+ uint8_t MustBeZero[53];
+ uint64_t PartitionOffset;
+ uint64_t VolumeLength;
+ uint32_t FatOffset;
+ uint32_t FatLength;
+ uint32_t ClusterHeapOffset;
+ uint32_t ClusterCount;
+ uint32_t FirstClusterOfRootDirectory;
+ uint8_t VolumeSerialNumber[4];
+ struct {
+ uint8_t vermin;
+ uint8_t vermaj;
+ } FileSystemRevision;
+ uint16_t VolumeFlags;
+ uint8_t BytesPerSectorShift;
+ uint8_t SectorsPerClusterShift;
+ uint8_t NumberOfFats;
+ uint8_t DriveSelect;
+ uint8_t PercentInUse;
+ uint8_t Reserved[7];
+ uint8_t BootCode[390];
+ uint16_t BootSignature;
+} __attribute__((__packed__));
+
+struct exfat_entry_label {
+ uint8_t type;
+ uint8_t length;
+ uint8_t name[22];
+ uint8_t reserved[8];
+} __attribute__((__packed__));
+
+#define BLOCK_SIZE(sb) ((sb)->BytesPerSectorShift < 32 ? (1u << (sb)->BytesPerSectorShift) : 0)
+#define CLUSTER_SIZE(sb) ((sb)->SectorsPerClusterShift < 32 ? (BLOCK_SIZE(sb) << (sb)->SectorsPerClusterShift) : 0)
+#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
+
+#define EXFAT_MAX_DIR_SIZE (256 * 1024 * 1024)
+
+static uint64_t block_to_offset(const struct exfat_super_block *sb,
+ uint64_t block)
+{
+ return block << sb->BytesPerSectorShift;
+}
+
+static uint64_t cluster_to_block(const struct exfat_super_block *sb,
+ uint32_t cluster)
+{
+ return le32_to_cpu(sb->ClusterHeapOffset) +
+ ((uint64_t) (cluster - EXFAT_FIRST_DATA_CLUSTER)
+ << sb->SectorsPerClusterShift);
+}
+
+static uint64_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 *nextp, next;
+ uint64_t fat_offset;
+
+ fat_offset = block_to_offset(sb, le32_to_cpu(sb->FatOffset))
+ + (uint64_t) cluster * sizeof(cluster);
+ nextp = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset,
+ sizeof(uint32_t));
+ if (!nextp)
+ return 0;
+ memcpy(&next, nextp, sizeof(next));
+ 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->FirstClusterOfRootDirectory);
+ uint64_t offset = cluster_to_offset(sb, cluster);
+ uint8_t *entry;
+ const size_t max_iter = EXFAT_MAX_DIR_SIZE / EXFAT_ENTRY_SIZE;
+ size_t i = 0;
+
+ for (; i < max_iter; i++) {
+ 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 (CLUSTER_SIZE(sb) && (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);
+ }
+ }
+
+ return NULL;
+}
+
+/* From https://docs.microsoft.com/en-us/windows/win32/fileio/exfat-specification#34-main-and-backup-boot-checksum-sub-regions */
+static uint32_t exfat_boot_checksum(unsigned char *sectors,
+ size_t sector_size)
+{
+ uint32_t n_bytes = sector_size * 11;
+ uint32_t checksum = 0;
+
+ for (size_t i = 0; i < n_bytes; i++) {
+ if ((i == 106) || (i == 107) || (i == 112))
+ continue;
+
+ checksum = ((checksum & 1) ? 0x80000000 : 0) + (checksum >> 1)
+ + (uint32_t) sectors[i];
+ }
+
+ return checksum;
+}
+
+static int exfat_validate_checksum(blkid_probe pr,
+ const struct exfat_super_block *sb)
+{
+ size_t sector_size = BLOCK_SIZE(sb);
+ /* 11 sectors will be checksummed, the 12th contains the expected */
+ unsigned char *data = blkid_probe_get_buffer(pr, 0, sector_size * 12);
+ if (!data)
+ return 0;
+
+ uint32_t checksum = exfat_boot_checksum(data, sector_size);
+
+ /* The expected checksum is repeated, check all of them */
+ for (size_t i = 0; i < sector_size / sizeof(uint32_t); i++) {
+ size_t offset = sector_size * 11 + i * 4;
+ uint32_t *expected_addr = (uint32_t *) &data[offset];
+ uint32_t expected = le32_to_cpu(*expected_addr);
+ if (!blkid_probe_verify_csum(pr, checksum, expected))
+ return 0;
+ };
+
+ return 1;
+}
+
+static int exfat_valid_superblock(blkid_probe pr, const struct exfat_super_block *sb)
+{
+ if (le16_to_cpu(sb->BootSignature) != 0xAA55)
+ return 0;
+
+ if (!CLUSTER_SIZE(sb))
+ return 0;
+
+ if (memcmp(sb->JumpBoot, "\xEB\x76\x90", 3) != 0)
+ return 0;
+
+ for (size_t i = 0; i < sizeof(sb->MustBeZero); i++)
+ if (sb->MustBeZero[i] != 0x00)
+ return 0;
+
+ if (!exfat_validate_checksum(pr, sb))
+ return 0;
+
+ return 1;
+}
+
+/* function prototype to avoid warnings (duplicate in partitions/dos.c) */
+extern int blkid_probe_is_exfat(blkid_probe pr);
+
+/*
+ * This function is used by MBR partition table parser to avoid
+ * misinterpretation of exFAT filesystem.
+ */
+int blkid_probe_is_exfat(blkid_probe pr)
+{
+ struct exfat_super_block *sb;
+ const struct blkid_idmag *mag = NULL;
+ int rc;
+
+ rc = blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag);
+ if (rc < 0)
+ return rc; /* error */
+ if (rc != BLKID_PROBE_OK || !mag)
+ return 0;
+
+ sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block);
+ if (!sb)
+ return 0;
+
+ if (memcmp(sb->FileSystemName, "EXFAT ", 8) != 0)
+ return 0;
+
+ return exfat_valid_superblock(pr, sb);
+}
+
+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 errno ? -errno : BLKID_PROBE_NONE;
+
+ if (!exfat_valid_superblock(pr, sb))
+ return BLKID_PROBE_NONE;
+
+ label = find_label(pr, sb);
+ if (label)
+ blkid_probe_set_utf8label(pr, label->name,
+ min((size_t) label->length * 2, sizeof(label->name)),
+ UL_ENCODE_UTF16LE);
+ else if (errno)
+ return -errno;
+
+ blkid_probe_sprintf_uuid(pr, sb->VolumeSerialNumber, 4,
+ "%02hhX%02hhX-%02hhX%02hhX",
+ sb->VolumeSerialNumber[3], sb->VolumeSerialNumber[2],
+ sb->VolumeSerialNumber[1], sb->VolumeSerialNumber[0]);
+
+ blkid_probe_sprintf_version(pr, "%u.%u",
+ sb->FileSystemRevision.vermaj, sb->FileSystemRevision.vermin);
+
+ blkid_probe_set_fsblocksize(pr, BLOCK_SIZE(sb));
+ blkid_probe_set_block_size(pr, BLOCK_SIZE(sb));
+ blkid_probe_set_fssize(pr, BLOCK_SIZE(sb) * le64_to_cpu(sb->VolumeLength));
+
+ return BLKID_PROBE_OK;
+}
+
+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/libblkid/src/superblocks/exfs.c b/libblkid/src/superblocks/exfs.c
new file mode 100644
index 0000000..87dc0a0
--- /dev/null
+++ b/libblkid/src/superblocks/exfs.c
@@ -0,0 +1,192 @@
+/*
+ * 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) 2013 Eric Sandeen <sandeen@redhat.com>
+ * Copyright (C) 2017 Hewlett Packard Enterprise Development LP
+ *
+ * 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 exfs_super_block {
+ uint32_t sb_magicnum; /* magic number == EXFS_SB_MAGIC */
+ uint32_t sb_blocksize; /* logical block size, bytes */
+ uint64_t sb_dblocks; /* number of data blocks */
+ uint64_t sb_rblocks; /* number of realtime blocks */
+ uint64_t sb_rextents; /* number of realtime extents */
+ unsigned char sb_uuid[16]; /* file system unique id */
+ uint64_t sb_logstart; /* starting block of log if internal */
+ uint64_t sb_rootino; /* root inode number */
+ uint64_t sb_rbmino; /* bitmap inode for realtime extents */
+ uint64_t sb_rsumino; /* summary inode for rt bitmap */
+ uint32_t sb_rextsize; /* realtime extent size, blocks */
+ uint32_t sb_agblocks; /* size of an allocation group */
+ uint32_t sb_agcount; /* number of allocation groups */
+ uint32_t sb_rbmblocks; /* number of rt bitmap blocks */
+ uint32_t sb_logblocks; /* number of log blocks */
+
+ uint16_t sb_versionnum; /* header version == EXFS_SB_VERSION */
+ uint16_t sb_sectsize; /* volume sector size, bytes */
+ uint16_t sb_inodesize; /* inode size, bytes */
+ uint16_t sb_inopblock; /* inodes per block */
+ char sb_fname[12]; /* file system name */
+ uint8_t sb_blocklog; /* log2 of sb_blocksize */
+ uint8_t sb_sectlog; /* log2 of sb_sectsize */
+ uint8_t sb_inodelog; /* log2 of sb_inodesize */
+ uint8_t sb_inopblog; /* log2 of sb_inopblock */
+ uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */
+ uint8_t sb_rextslog; /* log2 of sb_rextents */
+ uint8_t sb_inprogress; /* mkfs is in progress, don't mount */
+ uint8_t sb_imax_pct; /* max % of fs for inode space */
+ /* statistics */
+ uint64_t sb_icount; /* allocated inodes */
+ uint64_t sb_ifree; /* free inodes */
+ uint64_t sb_fdblocks; /* free data blocks */
+ uint64_t sb_frextents; /* free realtime extents */
+
+ /* this is not all... but enough for libblkid */
+
+} __attribute__((packed));
+
+#define EXFS_MIN_BLOCKSIZE_LOG 9 /* i.e. 512 bytes */
+#define EXFS_MAX_BLOCKSIZE_LOG 16 /* i.e. 65536 bytes */
+#define EXFS_MIN_BLOCKSIZE (1 << EXFS_MIN_BLOCKSIZE_LOG)
+#define EXFS_MAX_BLOCKSIZE (1 << EXFS_MAX_BLOCKSIZE_LOG)
+#define EXFS_MIN_SECTORSIZE_LOG 9 /* i.e. 512 bytes */
+#define EXFS_MAX_SECTORSIZE_LOG 15 /* i.e. 32768 bytes */
+#define EXFS_MIN_SECTORSIZE (1 << EXFS_MIN_SECTORSIZE_LOG)
+#define EXFS_MAX_SECTORSIZE (1 << EXFS_MAX_SECTORSIZE_LOG)
+
+#define EXFS_DINODE_MIN_LOG 8
+#define EXFS_DINODE_MAX_LOG 11
+#define EXFS_DINODE_MIN_SIZE (1 << EXFS_DINODE_MIN_LOG)
+#define EXFS_DINODE_MAX_SIZE (1 << EXFS_DINODE_MAX_LOG)
+
+#define EXFS_MAX_RTEXTSIZE (1024 * 1024 * 1024) /* 1GB */
+#define EXFS_DFL_RTEXTSIZE (64 * 1024) /* 64kB */
+#define EXFS_MIN_RTEXTSIZE (4 * 1024) /* 4kB */
+
+#define EXFS_MIN_AG_BLOCKS 64
+#define EXFS_MAX_DBLOCKS(s) ((uint64_t)(s)->sb_agcount * (s)->sb_agblocks)
+#define EXFS_MIN_DBLOCKS(s) ((uint64_t)((s)->sb_agcount - 1) * \
+ (s)->sb_agblocks + EXFS_MIN_AG_BLOCKS)
+
+
+static void sb_from_disk(struct exfs_super_block *from,
+ struct exfs_super_block *to)
+{
+
+ to->sb_magicnum = be32_to_cpu(from->sb_magicnum);
+ to->sb_blocksize = be32_to_cpu(from->sb_blocksize);
+ to->sb_dblocks = be64_to_cpu(from->sb_dblocks);
+ to->sb_rblocks = be64_to_cpu(from->sb_rblocks);
+ to->sb_rextents = be64_to_cpu(from->sb_rextents);
+ to->sb_logstart = be64_to_cpu(from->sb_logstart);
+ to->sb_rootino = be64_to_cpu(from->sb_rootino);
+ to->sb_rbmino = be64_to_cpu(from->sb_rbmino);
+ to->sb_rsumino = be64_to_cpu(from->sb_rsumino);
+ to->sb_rextsize = be32_to_cpu(from->sb_rextsize);
+ to->sb_agblocks = be32_to_cpu(from->sb_agblocks);
+ to->sb_agcount = be32_to_cpu(from->sb_agcount);
+ to->sb_rbmblocks = be32_to_cpu(from->sb_rbmblocks);
+ to->sb_logblocks = be32_to_cpu(from->sb_logblocks);
+ to->sb_versionnum = be16_to_cpu(from->sb_versionnum);
+ to->sb_sectsize = be16_to_cpu(from->sb_sectsize);
+ to->sb_inodesize = be16_to_cpu(from->sb_inodesize);
+ to->sb_inopblock = be16_to_cpu(from->sb_inopblock);
+ to->sb_blocklog = from->sb_blocklog;
+ to->sb_sectlog = from->sb_sectlog;
+ to->sb_inodelog = from->sb_inodelog;
+ to->sb_inopblog = from->sb_inopblog;
+ to->sb_agblklog = from->sb_agblklog;
+ to->sb_rextslog = from->sb_rextslog;
+ to->sb_inprogress = from->sb_inprogress;
+ to->sb_imax_pct = from->sb_imax_pct;
+ to->sb_icount = be64_to_cpu(from->sb_icount);
+ to->sb_ifree = be64_to_cpu(from->sb_ifree);
+ to->sb_fdblocks = be64_to_cpu(from->sb_fdblocks);
+ to->sb_frextents = be64_to_cpu(from->sb_frextents);
+}
+
+static int exfs_verify_sb(struct exfs_super_block *ondisk)
+{
+ struct exfs_super_block sb, *sbp = &sb;
+
+ /* beXX_to_cpu(), but don't convert UUID and fsname! */
+ sb_from_disk(ondisk, sbp);
+
+ /* sanity checks, we don't want to rely on magic string only */
+ if (sbp->sb_agcount <= 0 ||
+ sbp->sb_sectsize < EXFS_MIN_SECTORSIZE ||
+ sbp->sb_sectsize > EXFS_MAX_SECTORSIZE ||
+ sbp->sb_sectlog < EXFS_MIN_SECTORSIZE_LOG ||
+ sbp->sb_sectlog > EXFS_MAX_SECTORSIZE_LOG ||
+ sbp->sb_sectsize != (1 << sbp->sb_sectlog) ||
+ sbp->sb_blocksize < EXFS_MIN_BLOCKSIZE ||
+ sbp->sb_blocksize > EXFS_MAX_BLOCKSIZE ||
+ sbp->sb_blocklog < EXFS_MIN_BLOCKSIZE_LOG ||
+ sbp->sb_blocklog > EXFS_MAX_BLOCKSIZE_LOG ||
+ sbp->sb_blocksize != (1ULL << sbp->sb_blocklog) ||
+ sbp->sb_inodesize < EXFS_DINODE_MIN_SIZE ||
+ sbp->sb_inodesize > EXFS_DINODE_MAX_SIZE ||
+ sbp->sb_inodelog < EXFS_DINODE_MIN_LOG ||
+ sbp->sb_inodelog > EXFS_DINODE_MAX_LOG ||
+ sbp->sb_inodesize != (1 << sbp->sb_inodelog) ||
+ (sbp->sb_blocklog - sbp->sb_inodelog != sbp->sb_inopblog) ||
+ (sbp->sb_rextsize * sbp->sb_blocksize > EXFS_MAX_RTEXTSIZE) ||
+ (sbp->sb_rextsize * sbp->sb_blocksize < EXFS_MIN_RTEXTSIZE) ||
+ (sbp->sb_imax_pct > 100 /* zero sb_imax_pct is valid */) ||
+ sbp->sb_dblocks == 0 ||
+ sbp->sb_dblocks > EXFS_MAX_DBLOCKS(sbp) ||
+ sbp->sb_dblocks < EXFS_MIN_DBLOCKS(sbp))
+ return 0;
+ return 1;
+}
+
+static int probe_exfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct exfs_super_block *xs;
+
+ xs = blkid_probe_get_sb(pr, mag, struct exfs_super_block);
+ if (!xs)
+ return errno ? -errno : 1;
+
+ if (!exfs_verify_sb(xs))
+ return 1;
+
+ if (*xs->sb_fname != '\0')
+ blkid_probe_set_label(pr, (unsigned char *) xs->sb_fname,
+ sizeof(xs->sb_fname));
+
+ blkid_probe_set_uuid(pr, xs->sb_uuid);
+
+ blkid_probe_set_fsblocksize(pr, be32_to_cpu(xs->sb_blocksize));
+ blkid_probe_set_block_size(pr, be32_to_cpu(xs->sb_blocksize));
+
+ return 0;
+}
+
+const struct blkid_idinfo exfs_idinfo =
+{
+ .name = "exfs",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_exfs,
+ .magics =
+ {
+ { .magic = "EXFS", .len = 4 },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/ext.c b/libblkid/src/superblocks/ext.c
new file mode 100644
index 0000000..885fec2
--- /dev/null
+++ b/libblkid/src/superblocks/ext.c
@@ -0,0 +1,395 @@
+/*
+ * 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 "superblocks.h"
+#include "crc32c.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[162];
+ uint32_t s_checksum;
+} __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
+#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400
+
+/* 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
+
+/*
+ * Starting in 2.6.29, ext4 can be used to support filesystems
+ * without a journal.
+ */
+#define EXT4_SUPPORTS_EXT2 KERNEL_VERSION(2, 6, 29)
+
+/*
+ * 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, sizeof(struct ext2_super_block));
+ if (!es)
+ return NULL;
+ if (le32_to_cpu(es->s_feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) {
+ uint32_t csum = crc32c(~0, es, offsetof(struct ext2_super_block, s_checksum));
+ if (!blkid_probe_verify_csum(pr, csum, le32_to_cpu(es->s_checksum)))
+ 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);
+ uint32_t s_feature_incompat = le32_to_cpu(es->s_feature_incompat);
+
+ DBG(PROBE, ul_debug("ext2_sb.compat = %08X:%08X:%08X",
+ le32_to_cpu(es->s_feature_compat),
+ s_feature_incompat,
+ le32_to_cpu(es->s_feature_ro_compat)));
+
+ if (*es->s_volume_name != '\0')
+ 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) &&
+ ((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));
+
+ uint32_t block_size = 0;
+ if (le32_to_cpu(es->s_log_block_size) < 32){
+ block_size = 1024U << le32_to_cpu(es->s_log_block_size);
+ blkid_probe_set_fsblocksize(pr, block_size);
+ blkid_probe_set_block_size(pr, block_size);
+ }
+
+ uint64_t fslastblock = le32_to_cpu(es->s_blocks_count) |
+ ((s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ?
+ (uint64_t) le32_to_cpu(es->s_blocks_count_hi) << 32 : 0);
+ blkid_probe_set_fslastblock(pr, fslastblock);
+
+ /* The total number of blocks is taken without substraction of overhead
+ * (journal, metadata). The ext4 has non-trivial overhead calculation
+ * viz. ext4_calculate_overhead(). Thefore, the FSSIZE would show number
+ * slightly higher than the real value (for example, calculated via
+ * statfs()).
+ */
+ uint64_t fssize = (uint64_t) block_size * le32_to_cpu(es->s_blocks_count);
+ blkid_probe_set_fssize(pr, fssize);
+}
+
+
+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 errno ? -errno : 1;
+ if (!(fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV))
+ return 1;
+
+ ext_get_info(pr, 2, es);
+ blkid_probe_set_uuid_as(pr, es->s_uuid, "LOGUUID");
+
+ 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 errno ? -errno : 1;
+
+ /* Distinguish between ext3 and ext2 */
+ if (fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
+ return 1;
+
+ /* Any features which ext2 doesn't understand */
+ if ((frc & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) ||
+ (fi & EXT2_FEATURE_INCOMPAT_UNSUPPORTED))
+ return 1;
+
+ 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 errno ? -errno : 1;
+
+ /* ext3 requires journal */
+ if (!(fc & EXT3_FEATURE_COMPAT_HAS_JOURNAL))
+ return 1;
+
+ /* Any features which ext3 doesn't understand */
+ if ((frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) ||
+ (fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
+ return 1;
+
+ 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 errno ? -errno : 1;
+
+ /* Distinguish from jbd */
+ if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+ return 1;
+
+ if (!(le32_to_cpu(es->s_flags) & EXT2_FLAGS_TEST_FILESYS))
+ return 1;
+
+ 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 errno ? -errno : 1;
+
+ /* Distinguish from jbd */
+ if (fi & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)
+ return 1;
+
+ /* Ext4 has at least one feature which ext3 doesn't understand */
+ if (!(frc & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) &&
+ !(fi & EXT3_FEATURE_INCOMPAT_UNSUPPORTED))
+ return 1;
+
+ /*
+ * 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)
+ return 1;
+
+ 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/libblkid/src/superblocks/f2fs.c b/libblkid/src/superblocks/f2fs.c
new file mode 100644
index 0000000..980111e
--- /dev/null
+++ b/libblkid/src/superblocks/f2fs.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2013 Alejandro Martinez Ruiz <alex@nowcomputing.com>
+ *
+ * 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"
+
+#define F2FS_MAGIC "\x10\x20\xF5\xF2"
+#define F2FS_MAGIC_OFF 0
+#define F2FS_UUID_SIZE 16
+#define F2FS_LABEL_SIZE 512
+#define F2FS_SB1_OFF 0x400
+#define F2FS_SB1_KBOFF (F2FS_SB1_OFF >> 10)
+#define F2FS_SB2_OFF 0x1400
+#define F2FS_SB2_KBOFF (F2FS_SB2_OFF >> 10)
+
+struct f2fs_super_block { /* According to version 1.1 */
+/* 0x00 */ uint32_t magic; /* Magic Number */
+/* 0x04 */ uint16_t major_ver; /* Major Version */
+/* 0x06 */ uint16_t minor_ver; /* Minor Version */
+/* 0x08 */ uint32_t log_sectorsize; /* log2 sector size in bytes */
+/* 0x0C */ uint32_t log_sectors_per_block; /* log2 # of sectors per block */
+/* 0x10 */ uint32_t log_blocksize; /* log2 block size in bytes */
+/* 0x14 */ uint32_t log_blocks_per_seg; /* log2 # of blocks per segment */
+/* 0x18 */ uint32_t segs_per_sec; /* # of segments per section */
+/* 0x1C */ uint32_t secs_per_zone; /* # of sections per zone */
+/* 0x20 */ uint32_t checksum_offset; /* checksum offset inside super block */
+/* 0x24 */ uint64_t block_count; /* total # of user blocks */
+/* 0x2C */ uint32_t section_count; /* total # of sections */
+/* 0x30 */ uint32_t segment_count; /* total # of segments */
+/* 0x34 */ uint32_t segment_count_ckpt; /* # of segments for checkpoint */
+/* 0x38 */ uint32_t segment_count_sit; /* # of segments for SIT */
+/* 0x3C */ uint32_t segment_count_nat; /* # of segments for NAT */
+/* 0x40 */ uint32_t segment_count_ssa; /* # of segments for SSA */
+/* 0x44 */ uint32_t segment_count_main; /* # of segments for main area */
+/* 0x48 */ uint32_t segment0_blkaddr; /* start block address of segment 0 */
+/* 0x4C */ uint32_t cp_blkaddr; /* start block address of checkpoint */
+/* 0x50 */ uint32_t sit_blkaddr; /* start block address of SIT */
+/* 0x54 */ uint32_t nat_blkaddr; /* start block address of NAT */
+/* 0x58 */ uint32_t ssa_blkaddr; /* start block address of SSA */
+/* 0x5C */ uint32_t main_blkaddr; /* start block address of main area */
+/* 0x60 */ uint32_t root_ino; /* root inode number */
+/* 0x64 */ uint32_t node_ino; /* node inode number */
+/* 0x68 */ uint32_t meta_ino; /* meta inode number */
+/* 0x6C */ uint8_t uuid[F2FS_UUID_SIZE]; /* 128-bit uuid for volume */
+/* 0x7C */ uint16_t volume_name[F2FS_LABEL_SIZE]; /* volume name */
+#if 0
+/* 0x47C */ uint32_t extension_count; /* # of extensions below */
+/* 0x480 */ uint8_t extension_list[64][8]; /* extension array */
+#endif
+} __attribute__((packed));
+
+static int f2fs_validate_checksum(blkid_probe pr, size_t sb_off,
+ const struct f2fs_super_block *sb)
+{
+ uint32_t csum_off = le32_to_cpu(sb->checksum_offset);
+ if (!csum_off)
+ return 1;
+ if (csum_off % sizeof(uint32_t) != 0)
+ return 0;
+ if (csum_off + sizeof(uint32_t) > 4096)
+ return 0;
+
+ unsigned char *csum_data = blkid_probe_get_buffer(pr,
+ sb_off + csum_off, sizeof(uint32_t));
+ if (!csum_data)
+ return 0;
+
+ uint32_t expected = le32_to_cpu(*(uint32_t *) csum_data);
+
+ unsigned char *csummed = blkid_probe_get_buffer(pr, sb_off, csum_off);
+ if (!csummed)
+ return 0;
+
+ uint32_t csum = ul_crc32(0xF2F52010, csummed, csum_off);
+
+ return blkid_probe_verify_csum(pr, csum, expected);
+}
+
+static int probe_f2fs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct f2fs_super_block *sb;
+ uint16_t vermaj, vermin;
+
+ sb = blkid_probe_get_sb(pr, mag, struct f2fs_super_block);
+ if (!sb)
+ return errno ? -errno : 1;
+
+ vermaj = le16_to_cpu(sb->major_ver);
+ vermin = le16_to_cpu(sb->minor_ver);
+
+ /* For version 1.0 we cannot know the correct sb structure */
+ if (vermaj == 1 && vermin == 0)
+ return 0;
+
+ if (!f2fs_validate_checksum(pr, mag->kboff << 10, sb))
+ return 1;
+
+ if (*((unsigned char *) sb->volume_name))
+ blkid_probe_set_utf8label(pr, (unsigned char *) sb->volume_name,
+ sizeof(sb->volume_name),
+ UL_ENCODE_UTF16LE);
+
+ blkid_probe_set_uuid(pr, sb->uuid);
+ blkid_probe_sprintf_version(pr, "%u.%u", vermaj, vermin);
+ if (le32_to_cpu(sb->log_blocksize) < 32){
+ uint32_t blocksize = 1U << le32_to_cpu(sb->log_blocksize);
+ blkid_probe_set_fsblocksize(pr, blocksize);
+ blkid_probe_set_block_size(pr, blocksize);
+ blkid_probe_set_fssize(pr, le64_to_cpu(sb->block_count) * blocksize);
+ }
+ return 0;
+}
+
+const struct blkid_idinfo f2fs_idinfo =
+{
+ .name = "f2fs",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_f2fs,
+ .magics =
+ {
+ {
+ .magic = F2FS_MAGIC,
+ .len = 4,
+ .kboff = F2FS_SB1_KBOFF,
+ .sboff = F2FS_MAGIC_OFF
+ },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/gfs.c b/libblkid/src/superblocks/gfs.c
new file mode 100644
index 0000000..445c0da
--- /dev/null
+++ b/libblkid/src/superblocks/gfs.c
@@ -0,0 +1,141 @@
+/*
+ * 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
+
+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 license 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 errno ? -errno : 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 inline int gfs2_format_is_valid(uint32_t format)
+{
+ return (format >= 1800 && format < 1900);
+}
+static inline int gfs2_multiformat_is_valid(uint32_t multi)
+{
+ return (multi >= 1900 && multi < 2000);
+}
+
+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 errno ? -errno : 1;
+
+ if (gfs2_format_is_valid(be32_to_cpu(sbd->sb_fs_format)) &&
+ gfs2_multiformat_is_valid(be32_to_cpu(sbd->sb_multihost_format)))
+ {
+ 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");
+ blkid_probe_set_fsblocksize(pr, be32_to_cpu(sbd->sb_bsize));
+ blkid_probe_set_block_size(pr, be32_to_cpu(sbd->sb_bsize));
+ 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/libblkid/src/superblocks/hfs.c b/libblkid/src/superblocks/hfs.c
new file mode 100644
index 0000000..68cb30e
--- /dev/null
+++ b/libblkid/src/superblocks/hfs.c
@@ -0,0 +1,341 @@
+/*
+ * 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));
+
+#define HFS_SECTOR_SIZE 512
+
+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[UL_MD5LENGTH] = {
+ 0xb3, 0xe2, 0x0f, 0x39, 0xf2, 0x92, 0x11, 0xd6,
+ 0x97, 0xa4, 0x00, 0x30, 0x65, 0x43, 0xec, 0xac
+ };
+ unsigned char uuid[UL_MD5LENGTH];
+ struct UL_MD5Context md5c;
+
+ if (memcmp(hfs_info, "\0\0\0\0\0\0\0\0", len) == 0)
+ return -1;
+
+ ul_MD5Init(&md5c);
+ ul_MD5Update(&md5c, hash_init, UL_MD5LENGTH);
+ ul_MD5Update(&md5c, hfs_info, len);
+ ul_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;
+ int size;
+
+ hfs = blkid_probe_get_sb(pr, mag, struct hfs_mdb);
+ if (!hfs)
+ return errno ? -errno : 1;
+
+ if ((memcmp(hfs->embed_sig, "H+", 2) == 0) ||
+ (memcmp(hfs->embed_sig, "HX", 2) == 0))
+ return 1; /* Not hfs, but an embedded HFS+ */
+
+ size = be32_to_cpu(hfs->al_blk_size);
+ if (!size || (size & (HFS_SECTOR_SIZE - 1))) {
+ DBG(LOWPROBE, ul_debug("\tbad allocation size - ignore"));
+ return 1;
+ }
+
+ hfs_set_uuid(pr, hfs->finder_info.id, sizeof(hfs->finder_info.id));
+
+ size = hfs->label_len;
+ if ((size_t) size > sizeof(hfs->label))
+ size = sizeof(hfs->label);
+ blkid_probe_set_label(pr, hfs->label, size);
+ 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 = 0;
+ 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 errno ? -errno : 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 errno ? -errno : 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;
+
+ blkid_probe_set_fsblocksize(pr, blocksize);
+ blkid_probe_set_block_size(pr, blocksize);
+
+ memcpy(extents, hfsplus->cat_file.extents, sizeof(extents));
+ cat_block = be32_to_cpu(extents[0].start_block);
+
+ buf = blkid_probe_get_buffer(pr,
+ off + ((uint64_t) cat_block * blocksize), 0x2000);
+ if (!buf)
+ return errno ? -errno : 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_size < sizeof(struct hfsplus_bnode_descriptor) +
+ sizeof(struct hfsplus_catalog_key) || 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 = ((uint64_t) ext_block_start + leaf_block) * blocksize;
+
+ buf = blkid_probe_get_buffer(pr,
+ (uint64_t) off + leaf_off,
+ leaf_node_size);
+ if (!buf)
+ return errno ? -errno : 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 ||
+ be16_to_cpu(key->unicode_len) > 255)
+ return 0;
+
+ blkid_probe_set_utf8label(pr, key->unicode,
+ be16_to_cpu(key->unicode_len) * 2,
+ UL_ENCODE_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/libblkid/src/superblocks/highpoint_raid.c b/libblkid/src/superblocks/highpoint_raid.c
new file mode 100644
index 0000000..2487930
--- /dev/null
+++ b/libblkid/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 errno ? -errno : 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 = {
+ /*
+ * Superblock 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/libblkid/src/superblocks/hpfs.c b/libblkid/src/superblocks/hpfs.c
new file mode 100644
index 0000000..09bf975
--- /dev/null
+++ b/libblkid/src/superblocks/hpfs.c
@@ -0,0 +1,124 @@
+/*
+ * 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 errno ? -errno : 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 errno ? -errno : 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 errno ? -errno : 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);
+ blkid_probe_set_fsblocksize(pr, 512);
+ blkid_probe_set_block_size(pr, 512);
+
+ 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/libblkid/src/superblocks/iso9660.c b/libblkid/src/superblocks/iso9660.c
new file mode 100644
index 0000000..536704b
--- /dev/null
+++ b/libblkid/src/superblocks/iso9660.c
@@ -0,0 +1,348 @@
+/*
+ * 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 <ctype.h>
+
+#include "superblocks.h"
+#include "cctype.h"
+#include "iso9660.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 unused2[32];
+ unsigned char logical_block_size[4];
+ unsigned char unused3[58];
+ unsigned char volume_set_id[128];
+ unsigned char publisher_id[128];
+ unsigned char data_preparer_id[128];
+ unsigned char application_id[128];
+ unsigned char unused4[111];
+ struct iso9660_date created;
+ struct iso9660_date modified;
+} __attribute__((packed));
+
+/* Boot Record */
+struct boot_record {
+ unsigned char vd_type;
+ unsigned char vd_id[5];
+ unsigned char vd_version;
+ unsigned char boot_system_id[32];
+ unsigned char boot_id[32];
+ unsigned char unused[1];
+} __attribute__((packed));
+
+#define ISO_SUPERBLOCK_OFFSET 0x8000
+#define ISO_SECTOR_SIZE 0x800
+#define ISO_VD_BOOT_RECORD 0x0
+#define ISO_VD_PRIMARY 0x1
+#define ISO_VD_SUPPLEMENTARY 0x2
+#define ISO_VD_END 0xff
+#define ISO_VD_MAX 16
+/* maximal string field size used anywhere in ISO; update if necessary */
+#define ISO_MAX_FIELDSIZ sizeof_member(struct iso_volume_descriptor, volume_set_id)
+
+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));
+
+/* 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 errno ? -errno : 1;
+
+ blkid_probe_set_fsblocksize(pr, ISO_SECTOR_SIZE);
+ blkid_probe_set_block_size(pr, ISO_SECTOR_SIZE);
+ 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;
+}
+
+static int is_str_empty(const unsigned char *str, size_t len)
+{
+ size_t i;
+
+ if (!str || !*str)
+ return 1;
+
+ for (i = 0; i < len; i++)
+ if (!isspace(str[i]))
+ return 0;
+ return 1;
+}
+
+static int is_utf16be_str_empty(unsigned char *utf16, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i += 2) {
+ if (utf16[i] != 0x0 || !isspace(utf16[i + 1]))
+ return 0;
+ }
+ return 1;
+}
+
+/* if @utf16 is prefix of @ascii (ignoring non-representable characters and upper-case conversion)
+ * then reconstruct prefix from @utf16 and @ascii, append suffix from @ascii, fill it into @out
+ * and returns length of bytes written into @out; otherwise returns zero */
+static size_t merge_utf16be_ascii(unsigned char *out, size_t out_len, const unsigned char *utf16, const unsigned char *ascii, size_t len)
+{
+ size_t o, a, u;
+
+ for (o = 0, a = 0, u = 0; u + 1 < len && a < len && o + 1 < out_len; o += 2, a++, u += 2) {
+ /* Surrogate pair with code point above U+FFFF */
+ if (utf16[u] >= 0xD8 && utf16[u] <= 0xDB && u + 3 < len &&
+ utf16[u + 2] >= 0xDC && utf16[u + 2] <= 0xDF) {
+ out[o++] = utf16[u++];
+ out[o++] = utf16[u++];
+ }
+ /* Value '_' is replacement for non-representable character */
+ if (ascii[a] == '_') {
+ out[o] = utf16[u];
+ out[o + 1] = utf16[u + 1];
+ } else if (utf16[u] == 0x00 && utf16[u + 1] == '_') {
+ out[o] = 0x00;
+ out[o + 1] = ascii[a];
+ } else if (utf16[u] == 0x00 && c_toupper(ascii[a]) == c_toupper(utf16[u + 1])) {
+ out[o] = 0x00;
+ out[o + 1] = c_isupper(ascii[a]) ? utf16[u + 1] : ascii[a];
+ } else {
+ return 0;
+ }
+ }
+
+ for (; a < len && o + 1 < out_len; o += 2, a++) {
+ out[o] = 0x00;
+ out[o + 1] = ascii[a];
+ }
+
+ return o;
+}
+
+/* iso9660 [+ Microsoft Joliet Extension] */
+static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct boot_record *boot = NULL;
+ struct iso_volume_descriptor *pvd = NULL;
+ struct iso_volume_descriptor *joliet = NULL;
+ /* space for merge_utf16be_ascii(ISO_ID_BUFSIZ bytes) */
+ unsigned char buf[ISO_MAX_FIELDSIZ * 5 / 2];
+ size_t len;
+ int is_unicode_empty;
+ int is_ascii_empty;
+ int i;
+ uint64_t off;
+
+ if (blkid_probe_get_hint(pr, mag->hoff, &off) < 0)
+ off = 0;
+
+ if (off % ISO_SECTOR_SIZE)
+ return 1;
+
+ if (strcmp(mag->magic, "CDROM") == 0)
+ return probe_iso9660_hsfs(pr, mag);
+
+ for (i = 0, off += ISO_SUPERBLOCK_OFFSET; i < ISO_VD_MAX && (!boot || !pvd || !joliet); i++, off += ISO_SECTOR_SIZE) {
+ unsigned char *desc =
+ blkid_probe_get_buffer(pr,
+ off,
+ max(sizeof(struct boot_record),
+ sizeof(struct iso_volume_descriptor)));
+
+ if (desc == NULL || desc[0] == ISO_VD_END)
+ break;
+ else if (!boot && desc[0] == ISO_VD_BOOT_RECORD)
+ boot = (struct boot_record *)desc;
+ else if (!pvd && desc[0] == ISO_VD_PRIMARY)
+ pvd = (struct iso_volume_descriptor *)desc;
+ else if (!joliet && desc[0] == ISO_VD_SUPPLEMENTARY) {
+ joliet = (struct iso_volume_descriptor *)desc;
+ if (memcmp(joliet->escape_sequences, "%/@", 3) != 0 &&
+ memcmp(joliet->escape_sequences, "%/C", 3) != 0 &&
+ memcmp(joliet->escape_sequences, "%/E", 3) != 0)
+ joliet = NULL;
+ }
+ }
+
+ if (!pvd)
+ return errno ? -errno : 1;
+
+ uint16_t logical_block_size = isonum_723(pvd->logical_block_size, false);
+ uint32_t space_size = isonum_733(pvd->space_size, false);
+
+ blkid_probe_set_fsblocksize(pr, logical_block_size);
+ blkid_probe_set_block_size(pr, logical_block_size);
+ blkid_probe_set_fssize(pr, (uint64_t) space_size * logical_block_size);
+
+ if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->system_id, pvd->system_id, sizeof(pvd->system_id))) != 0)
+ blkid_probe_set_utf8_id_label(pr, "SYSTEM_ID", buf, len, UL_ENCODE_UTF16BE);
+ else if (joliet)
+ blkid_probe_set_utf8_id_label(pr, "SYSTEM_ID", joliet->system_id, sizeof(joliet->system_id), UL_ENCODE_UTF16BE);
+ else
+ blkid_probe_set_id_label(pr, "SYSTEM_ID", pvd->system_id, sizeof(pvd->system_id));
+
+ if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->volume_set_id, pvd->volume_set_id, sizeof(pvd->volume_set_id))) != 0)
+ blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", buf, len, UL_ENCODE_UTF16BE);
+ else if (joliet)
+ blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", joliet->volume_set_id, sizeof(joliet->volume_set_id), UL_ENCODE_UTF16BE);
+ else
+ blkid_probe_set_id_label(pr, "VOLUME_SET_ID", pvd->volume_set_id, sizeof(pvd->volume_set_id));
+
+ is_ascii_empty = (is_str_empty(pvd->publisher_id, sizeof(pvd->publisher_id)) || pvd->publisher_id[0] == '_');
+ is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->publisher_id, sizeof(joliet->publisher_id)) || (joliet->publisher_id[0] == 0x00 && joliet->publisher_id[1] == '_'));
+ if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->publisher_id, pvd->publisher_id, sizeof(pvd->publisher_id))) != 0)
+ blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", buf, len, UL_ENCODE_UTF16BE);
+ else if (!is_unicode_empty)
+ blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", joliet->publisher_id, sizeof(joliet->publisher_id), UL_ENCODE_UTF16BE);
+ else if (!is_ascii_empty)
+ blkid_probe_set_id_label(pr, "PUBLISHER_ID", pvd->publisher_id, sizeof(pvd->publisher_id));
+
+ is_ascii_empty = (is_str_empty(pvd->data_preparer_id, sizeof(pvd->data_preparer_id)) || pvd->data_preparer_id[0] == '_');
+ is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->data_preparer_id, sizeof(joliet->data_preparer_id)) || (joliet->data_preparer_id[0] == 0x00 && joliet->data_preparer_id[1] == '_'));
+ if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->data_preparer_id, pvd->data_preparer_id, sizeof(pvd->data_preparer_id))) != 0)
+ blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", buf, len, UL_ENCODE_UTF16BE);
+ else if (!is_unicode_empty)
+ blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", joliet->data_preparer_id, sizeof(joliet->data_preparer_id), UL_ENCODE_UTF16BE);
+ else if (!is_ascii_empty)
+ blkid_probe_set_id_label(pr, "DATA_PREPARER_ID", pvd->data_preparer_id, sizeof(pvd->data_preparer_id));
+
+ is_ascii_empty = (is_str_empty(pvd->application_id, sizeof(pvd->application_id)) || pvd->application_id[0] == '_');
+ is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->application_id, sizeof(joliet->application_id)) || (joliet->application_id[0] == 0x00 && joliet->application_id[1] == '_'));
+ if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->application_id, pvd->application_id, sizeof(pvd->application_id))) != 0)
+ blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", buf, len, UL_ENCODE_UTF16BE);
+ else if (!is_unicode_empty)
+ blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", joliet->application_id, sizeof(joliet->application_id), UL_ENCODE_UTF16BE);
+ else if (!is_ascii_empty)
+ blkid_probe_set_id_label(pr, "APPLICATION_ID", pvd->application_id, sizeof(pvd->application_id));
+
+ /* create an UUID using the modified/created date */
+ if (! probe_iso9660_set_uuid(pr, &pvd->modified))
+ probe_iso9660_set_uuid(pr, &pvd->created);
+
+ if (boot)
+ blkid_probe_set_id_label(pr, "BOOT_SYSTEM_ID",
+ boot->boot_system_id,
+ sizeof(boot->boot_system_id));
+
+ if (joliet)
+ blkid_probe_set_version(pr, "Joliet Extension");
+
+ /* Label in Joliet is UNICODE (UTF16BE) but can contain only 16 characters. Label in PVD is
+ * subset of ASCII but can contain up to the 32 characters. Non-representable characters are
+ * stored as replacement character '_'. Label in Joliet is in most cases trimmed but UNICODE
+ * version of label in PVD. Based on these facts try to reconstruct original label if label
+ * in Joliet is prefix of the label in PVD (ignoring non-representable characters).
+ */
+ if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->volume_id, pvd->volume_id, sizeof(pvd->volume_id))) != 0)
+ blkid_probe_set_utf8label(pr, buf, len, UL_ENCODE_UTF16BE);
+ else if (joliet)
+ blkid_probe_set_utf8label(pr, joliet->volume_id, sizeof(joliet->volume_id), UL_ENCODE_UTF16BE);
+ else
+ blkid_probe_set_label(pr, pvd->volume_id, sizeof(pvd->volume_id));
+
+ 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, .hoff = "session_offset" },
+ { .magic = "CDROM", .len = 5, .kboff = 32, .sboff = 9, .hoff = "session_offset" },
+ { NULL }
+ }
+};
+
diff --git a/libblkid/src/superblocks/isw_raid.c b/libblkid/src/superblocks/isw_raid.c
new file mode 100644
index 0000000..81d53a1
--- /dev/null
+++ b/libblkid/src/superblocks/isw_raid.c
@@ -0,0 +1,67 @@
+/*
+ * 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;
+ unsigned int sector_size;
+ struct isw_metadata *isw;
+
+ if (pr->size < 0x10000)
+ return 1;
+ if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+ return 1;
+
+ sector_size = blkid_probe_get_sectorsize(pr);
+ off = ((pr->size / sector_size) - 2) * sector_size;
+
+ isw = (struct isw_metadata *)blkid_probe_get_buffer(pr,
+ off, sizeof(struct isw_metadata));
+ if (!isw)
+ return errno ? -errno : 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/libblkid/src/superblocks/jfs.c b/libblkid/src/superblocks/jfs.c
new file mode 100644
index 0000000..7b75ecb
--- /dev/null
+++ b/libblkid/src/superblocks/jfs.c
@@ -0,0 +1,75 @@
+/*
+ * 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 errno ? -errno : 1;
+ if (le16_to_cpu(js->js_l2bsize) > 31 || le16_to_cpu(js->js_l2pbsize) > 31)
+ 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 (*((char *) js->js_label) != '\0')
+ blkid_probe_set_label(pr, js->js_label, sizeof(js->js_label));
+ blkid_probe_set_uuid(pr, js->js_uuid);
+ blkid_probe_set_fsblocksize(pr, le32_to_cpu(js->js_bsize));
+ blkid_probe_set_block_size(pr, le32_to_cpu(js->js_bsize));
+ 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/libblkid/src/superblocks/jmicron_raid.c b/libblkid/src/superblocks/jmicron_raid.c
new file mode 100644
index 0000000..65e05b6
--- /dev/null
+++ b/libblkid/src/superblocks/jmicron_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"
+
+#define JM_SIGNATURE "JM"
+#define JM_MINOR_VERSION(_x) (le16_to_cpu((_x)->version) & 0xFF)
+#define JM_MAJOR_VERSION(_x) (le16_to_cpu((_x)->version) >> 8)
+#define JM_SPARES 2
+#define JM_MEMBERS 8
+
+struct jm_metadata {
+ int8_t signature[2]; /* 0x0 - 0x01 */
+
+ uint16_t version; /* 0x03 - 0x04 JMicron version */
+
+ uint16_t checksum; /* 0x04 - 0x05 */
+ uint8_t filler[10];
+
+ uint32_t identity; /* 0x10 - 0x13 */
+
+ struct {
+ uint32_t base; /* 0x14 - 0x17 */
+ uint32_t range; /* 0x18 - 0x1B range */
+ uint16_t range2; /* 0x1C - 0x1D range2 */
+ } segment;
+
+ int8_t name[16]; /* 0x20 - 0x2F */
+
+ uint8_t mode; /* 0x30 RAID level */
+ uint8_t block; /* 0x31 stride size (2=4K, 3=8K, ...) */
+ uint16_t attribute; /* 0x32 - 0x33 */
+ uint8_t filler1[4];
+
+ uint32_t spare[JM_SPARES]; /* 0x38 - 0x3F */
+ uint32_t member[JM_MEMBERS]; /* 0x40 - 0x5F */
+
+ uint8_t filler2[0x20];
+} __attribute__ ((packed));
+
+static int jm_checksum(blkid_probe pr, const struct jm_metadata *jm)
+{
+ size_t count = sizeof(*jm) / sizeof(uint16_t);
+ uint16_t sum = 0;
+ unsigned char *ptr = (unsigned char *) jm;
+
+ while (count--) {
+ uint16_t val;
+
+ memcpy(&val, ptr, sizeof(uint16_t));
+ sum += le16_to_cpu(val);
+
+ ptr += sizeof(uint16_t);
+ }
+
+ return blkid_probe_verify_csum(pr, sum == 0 || sum == 1, 1);
+}
+
+static int probe_jmraid(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ uint64_t off;
+ const 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 errno ? -errno : 1;
+
+ if (memcmp(jm->signature, JM_SIGNATURE, sizeof(JM_SIGNATURE) - 1) != 0)
+ return 1;
+
+ if (!jm_checksum(pr, jm))
+ return 1;
+
+ if (jm->mode > 5)
+ return 1;
+
+ if (blkid_probe_sprintf_version(pr, "%u.%u",
+ JM_MAJOR_VERSION(jm), JM_MINOR_VERSION(jm)) != 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/libblkid/src/superblocks/linux_raid.c b/libblkid/src/superblocks/linux_raid.c
new file mode 100644
index 0000000..360cd4e
--- /dev/null
+++ b/libblkid/src/superblocks/linux_raid.c
@@ -0,0 +1,295 @@
+/*
+ * 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 bits 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, uint64_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 errno ? -errno : 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 < size + MD_RESERVED_BYTES)
+ /* device is too small */
+ return 1;
+
+ if (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 raid1_verify_csum(blkid_probe pr, off_t off,
+ const struct mdp1_super_block *mdp1)
+{
+ size_t csummed_size = sizeof(struct mdp1_super_block)
+ + le32_to_cpu(mdp1->max_dev) * sizeof(mdp1->dev_roles[0]);
+ unsigned char *csummed = blkid_probe_get_buffer(pr, off, csummed_size);
+ if (!csummed)
+ return 1;
+
+ memset(csummed + offsetof(struct mdp1_super_block, sb_csum), 0,
+ sizeof(mdp1->sb_csum));
+
+ uint64_t csum = 0;
+
+ while (csummed_size >= 4) {
+ csum += le32_to_cpu(*(uint32_t *) csummed);
+ csummed_size -= 4;
+ csummed += 4;
+ }
+ if (csummed_size == 2)
+ csum += le16_to_cpu(*(uint16_t *) csummed);
+
+ csum = (csum >> 32) + (uint32_t) csum;
+ return blkid_probe_verify_csum(pr, csum, le32_to_cpu(mdp1->sb_csum));
+}
+
+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 errno ? -errno : 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 (!raid1_verify_csum(pr, off, mdp1))
+ 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;
+}
+
+static int probe_raid(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ const char *ver = NULL;
+ int ret = BLKID_PROBE_NONE;
+
+ 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;
+ ret = probe_raid0(pr, sboff);
+ if (ret < 1)
+ return ret; /* error */
+
+ /* version 1.0 at the end of the device */
+ sboff = (pr->size & ~(0x1000 - 1)) - 0x2000;
+ ret = probe_raid1(pr, sboff);
+ if (ret < 0)
+ return ret; /* error */
+ if (ret == 0)
+ ver = "1.0";
+ }
+
+ if (!ver) {
+ /* version 1.1 at the start of the device */
+ ret = probe_raid1(pr, 0);
+ if (ret == 0)
+ ver = "1.1";
+
+ /* version 1.2 at 4k offset from the start */
+ else if (ret == BLKID_PROBE_NONE) {
+ ret = probe_raid1(pr, 0x1000);
+ if (ret == 0)
+ ver = "1.2";
+ }
+ }
+
+ if (ver) {
+ blkid_probe_set_version(pr, ver);
+ return BLKID_PROBE_OK;
+ }
+ return ret;
+}
+
+
+const struct blkid_idinfo linuxraid_idinfo = {
+ .name = "linux_raid_member",
+ .usage = BLKID_USAGE_RAID,
+ .probefunc = probe_raid,
+ .magics = BLKID_NONE_MAGIC
+};
+
+
diff --git a/libblkid/src/superblocks/lsi_raid.c b/libblkid/src/superblocks/lsi_raid.c
new file mode 100644
index 0000000..697b0fe
--- /dev/null
+++ b/libblkid/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 errno ? -errno : 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/libblkid/src/superblocks/luks.c b/libblkid/src/superblocks/luks.c
new file mode 100644
index 0000000..0230b34
--- /dev/null
+++ b/libblkid/src/superblocks/luks.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2018 Milan Broz <gmazyland@gmail.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
+#define LUKS2_LABEL_L 48
+#define LUKS2_SALT_L 64
+#define LUKS2_CHECKSUM_ALG_L 32
+#define LUKS2_CHECKSUM_L 64
+
+#define LUKS_MAGIC "LUKS\xba\xbe"
+#define LUKS_MAGIC_2 "SKUL\xba\xbe"
+
+/* Offsets for secondary header (for scan if primary header is corrupted). */
+#define LUKS2_HDR2_OFFSETS { 0x04000, 0x008000, 0x010000, 0x020000, \
+ 0x40000, 0x080000, 0x100000, 0x200000, 0x400000 }
+
+static const uint64_t secondary_offsets[] = LUKS2_HDR2_OFFSETS;
+
+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));
+
+struct luks2_phdr {
+ char magic[LUKS_MAGIC_L];
+ uint16_t version;
+ uint64_t hdr_size; /* in bytes, including JSON area */
+ uint64_t seqid; /* increased on every update */
+ char label[LUKS2_LABEL_L];
+ char checksum_alg[LUKS2_CHECKSUM_ALG_L];
+ uint8_t salt[LUKS2_SALT_L]; /* unique for every header/offset */
+ char uuid[UUID_STRING_L];
+ char subsystem[LUKS2_LABEL_L]; /* owner subsystem label */
+ uint64_t hdr_offset; /* offset from device start in bytes */
+ char _padding[184];
+ uint8_t csum[LUKS2_CHECKSUM_L];
+ /* Padding to 4k, then JSON area */
+} __attribute__ ((packed));
+
+static int luks_attributes(blkid_probe pr, struct luks2_phdr *header, uint64_t offset)
+{
+ int version;
+ struct luks_phdr *header_v1;
+
+ if (blkid_probe_set_magic(pr, offset, LUKS_MAGIC_L, (unsigned char *) &header->magic))
+ return BLKID_PROBE_NONE;
+
+ version = be16_to_cpu(header->version);
+ blkid_probe_sprintf_version(pr, "%u", version);
+
+ if (version == 1) {
+ header_v1 = (struct luks_phdr *)header;
+ blkid_probe_strncpy_uuid(pr,
+ (unsigned char *) header_v1->uuid, UUID_STRING_L);
+ } else if (version == 2) {
+ blkid_probe_strncpy_uuid(pr,
+ (unsigned char *) header->uuid, UUID_STRING_L);
+ blkid_probe_set_label(pr,
+ (unsigned char *) header->label, LUKS2_LABEL_L);
+ blkid_probe_set_id_label(pr, "SUBSYSTEM",
+ (unsigned char *) header->subsystem, LUKS2_LABEL_L);
+ }
+
+ return BLKID_PROBE_OK;
+}
+
+static int probe_luks(blkid_probe pr, const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ struct luks2_phdr *header;
+ size_t i;
+
+ header = (struct luks2_phdr *) blkid_probe_get_buffer(pr, 0, sizeof(struct luks2_phdr));
+ if (!header)
+ return errno ? -errno : BLKID_PROBE_NONE;
+
+ if (!memcmp(header->magic, LUKS_MAGIC, LUKS_MAGIC_L)) {
+ /* LUKS primary header was found. */
+ return luks_attributes(pr, header, 0);
+ }
+
+ /* No primary header, scan for known offsets of LUKS2 secondary header. */
+ for (i = 0; i < ARRAY_SIZE(secondary_offsets); i++) {
+ header = (struct luks2_phdr *) blkid_probe_get_buffer(pr,
+ secondary_offsets[i], sizeof(struct luks2_phdr));
+
+ if (!header)
+ return errno ? -errno : BLKID_PROBE_NONE;
+
+ if (!memcmp(header->magic, LUKS_MAGIC_2, LUKS_MAGIC_L))
+ return luks_attributes(pr, header, secondary_offsets[i]);
+ }
+
+ return BLKID_PROBE_NONE;
+}
+
+const struct blkid_idinfo luks_idinfo =
+{
+ .name = "crypto_LUKS",
+ .usage = BLKID_USAGE_CRYPTO,
+ .probefunc = probe_luks,
+ .magics = BLKID_NONE_MAGIC
+};
diff --git a/libblkid/src/superblocks/lvm.c b/libblkid/src/superblocks/lvm.c
new file mode 100644
index 0000000..eea74d6
--- /dev/null
+++ b/libblkid/src/superblocks/lvm.c
@@ -0,0 +1,264 @@
+/*
+ * 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 <gmazyland@gmail.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 errno ? -errno : 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 (!blkid_probe_verify_csum(
+ pr, lvm2_calc_crc(
+ &label->offset_xl, LVM2_LABEL_SIZE -
+ ((char *) &label->offset_xl - (char *) label)),
+ le32_to_cpu(label->crc_xl)))
+ 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 between 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 errno ? -errno : 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 errno ? -errno : 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;
+}
+
+struct integrity_sb {
+ uint8_t magic[8];
+ uint8_t version;
+ int8_t log2_interleave_sectors;
+ uint16_t integrity_tag_size;
+ uint32_t journal_sections;
+ uint64_t provided_data_sectors;
+ uint32_t flags;
+ uint8_t log2_sectors_per_block;
+} __attribute__ ((packed));
+
+static int probe_integrity(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct integrity_sb *sb;
+
+ sb = blkid_probe_get_sb(pr, mag, struct integrity_sb);
+ if (sb == NULL)
+ return errno ? -errno : 1;
+
+ if (!sb->version)
+ return 1;
+
+ blkid_probe_sprintf_version(pr, "%u", sb->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 }
+ }
+};
+
+const struct blkid_idinfo integrity_idinfo =
+{
+ .name = "DM_integrity",
+ .usage = BLKID_USAGE_CRYPTO,
+ .probefunc = probe_integrity,
+ .magics =
+ {
+ { .magic = "integrt\0", .len = 8 },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/minix.c b/libblkid/src/superblocks/minix.c
new file mode 100644
index 0000000..c68ade9
--- /dev/null
+++ b/libblkid/src/superblocks/minix.c
@@ -0,0 +1,190 @@
+/*
+ * 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-2013 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"
+
+#define minix_swab16(doit, num) ((uint16_t) (doit ? swab16(num) : num))
+#define minix_swab32(doit, num) ((uint32_t) (doit ? swab32(num) : num))
+
+static int get_minix_version(const unsigned char *data, int *other_endian)
+{
+ const struct minix_super_block *sb = (const struct minix_super_block *) data;
+ const struct minix3_super_block *sb3 = (const struct minix3_super_block *) data;
+ int version = 0;
+ char *endian;
+
+ *other_endian = 0;
+
+ switch (sb->s_magic) {
+ case MINIX_SUPER_MAGIC:
+ case MINIX_SUPER_MAGIC2:
+ version = 1;
+ break;
+ case MINIX2_SUPER_MAGIC:
+ case MINIX2_SUPER_MAGIC2:
+ version = 2;
+ break;
+ default:
+ if (sb3->s_magic == MINIX3_SUPER_MAGIC)
+ version = 3;
+ break;
+ }
+
+ if (!version) {
+ *other_endian = 1;
+
+ switch (swab16(sb->s_magic)) {
+ case MINIX_SUPER_MAGIC:
+ case MINIX_SUPER_MAGIC2:
+ version = 1;
+ break;
+ case MINIX2_SUPER_MAGIC:
+ case MINIX2_SUPER_MAGIC2:
+ version = 2;
+ break;
+ default:
+ if (sb3->s_magic == MINIX3_SUPER_MAGIC)
+ version = 3;
+ break;
+ }
+ }
+ if (!version)
+ return -1;
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ endian = *other_endian ? "LE" : "BE";
+#else
+ endian = *other_endian ? "BE" : "LE";
+#endif
+ DBG(LOWPROBE, ul_debug("minix version %d detected [%s]", version,
+ endian));
+ return version;
+}
+
+static int probe_minix(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ unsigned char *ext;
+ const unsigned char *data;
+ int version = 0, swabme = 0;
+ unsigned long zones, ninodes, imaps, zmaps;
+ off_t firstz;
+ size_t zone_size;
+ unsigned block_size;
+
+ data = blkid_probe_get_buffer(pr, 1024,
+ max(sizeof(struct minix_super_block),
+ sizeof(struct minix3_super_block)));
+ if (!data)
+ return errno ? -errno : 1;
+ version = get_minix_version(data, &swabme);
+ switch (version) {
+ case 1:
+ case 2: {
+ const struct minix_super_block *sb = (const struct minix_super_block *) data;
+
+ uint16_t state = minix_swab16(swabme, sb->s_state);
+ if ((state & (MINIX_VALID_FS | MINIX_ERROR_FS)) != state)
+ return 1;
+
+ zones = version == 2 ? minix_swab32(swabme, sb->s_zones) :
+ minix_swab16(swabme, sb->s_nzones);
+ ninodes = minix_swab16(swabme, sb->s_ninodes);
+ imaps = minix_swab16(swabme, sb->s_imap_blocks);
+ zmaps = minix_swab16(swabme, sb->s_zmap_blocks);
+ firstz = minix_swab16(swabme, sb->s_firstdatazone);
+ zone_size = sb->s_log_zone_size;
+ block_size = 1024;
+ break;
+ }
+ case 3: {
+ const struct minix3_super_block *sb = (const struct minix3_super_block *) data;
+
+ zones = minix_swab32(swabme, sb->s_zones);
+ ninodes = minix_swab32(swabme, sb->s_ninodes);
+ imaps = minix_swab16(swabme, sb->s_imap_blocks);
+ zmaps = minix_swab16(swabme, sb->s_zmap_blocks);
+ firstz = minix_swab16(swabme, sb->s_firstdatazone);
+ zone_size = sb->s_log_zone_size;
+ block_size = minix_swab16(swabme, sb->s_blocksize);
+
+ break;
+ }
+ default:
+ return 1;
+ }
+
+ /* sanity checks to be sure that the FS is really minix.
+ * see disk-utils/fsck.minix.c read_superblock
+ */
+ if (zone_size != 0 || ninodes == 0 || ninodes == UINT32_MAX)
+ return 1;
+ if (imaps * MINIX_BLOCK_SIZE * 8 < ninodes + 1)
+ return 1;
+ if (firstz > (off_t) zones)
+ return 1;
+ if (zmaps * MINIX_BLOCK_SIZE * 8 < zones - firstz + 1)
+ 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)
+ return errno ? -errno : 1;
+
+ if (memcmp(ext, "\123\357", 2) == 0)
+ return 1;
+
+ blkid_probe_sprintf_version(pr, "%d", version);
+ blkid_probe_set_fsblocksize(pr, block_size);
+ blkid_probe_set_block_size(pr, block_size);
+ blkid_probe_set_fsendianness(pr, !swabme ?
+ BLKID_ENDIANNESS_NATIVE : BLKID_ENDIANNESS_OTHER);
+ return 0;
+}
+
+const struct blkid_idinfo minix_idinfo =
+{
+ .name = "minix",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_minix,
+ .magics =
+ {
+ /* version 1 - LE */
+ { .magic = "\177\023", .len = 2, .kboff = 1, .sboff = 0x10 },
+ { .magic = "\217\023", .len = 2, .kboff = 1, .sboff = 0x10 },
+
+ /* version 1 - BE */
+ { .magic = "\023\177", .len = 2, .kboff = 1, .sboff = 0x10 },
+ { .magic = "\023\217", .len = 2, .kboff = 1, .sboff = 0x10 },
+
+ /* version 2 - LE */
+ { .magic = "\150\044", .len = 2, .kboff = 1, .sboff = 0x10 },
+ { .magic = "\170\044", .len = 2, .kboff = 1, .sboff = 0x10 },
+
+ /* version 2 - BE */
+ { .magic = "\044\150", .len = 2, .kboff = 1, .sboff = 0x10 },
+ { .magic = "\044\170", .len = 2, .kboff = 1, .sboff = 0x10 },
+
+ /* version 3 - LE */
+ { .magic = "\132\115", .len = 2, .kboff = 1, .sboff = 0x18 },
+
+ /* version 3 - BE */
+ { .magic = "\115\132", .len = 2, .kboff = 1, .sboff = 0x18 },
+
+ { NULL }
+ }
+};
+
diff --git a/libblkid/src/superblocks/mpool.c b/libblkid/src/superblocks/mpool.c
new file mode 100644
index 0000000..b27569e
--- /dev/null
+++ b/libblkid/src/superblocks/mpool.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 Micron Technology, Inc.
+ *
+ * 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 "crc32c.h"
+#include "superblocks.h"
+
+#define MAX_MPOOL_NAME_LEN 32
+
+struct omf_sb_descriptor {
+ uint64_t osb_magic;
+ uint8_t osb_name[MAX_MPOOL_NAME_LEN];
+ unsigned char osb_poolid[16]; /* UUID of pool this drive belongs to */
+ uint16_t osb_vers;
+ uint32_t osb_gen;
+ uint32_t osb_cksum1; /* crc32c of the preceding fields */
+} __attribute__((packed));
+
+static int probe_mpool(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct omf_sb_descriptor *osd;
+ uint32_t sb_crc;
+
+ osd = blkid_probe_get_sb(pr, mag, struct omf_sb_descriptor);
+ if (!osd)
+ return errno ? -errno : 1;
+
+ sb_crc = crc32c(~0L, (const void *)osd,
+ offsetof(struct omf_sb_descriptor, osb_cksum1));
+ sb_crc ^= ~0L;
+
+ if (!blkid_probe_verify_csum(pr, sb_crc, le32_to_cpu(osd->osb_cksum1)))
+ return 1;
+
+ blkid_probe_set_label(pr, osd->osb_name, sizeof(osd->osb_name));
+ blkid_probe_set_uuid(pr, osd->osb_poolid);
+
+ return 0;
+}
+
+/* "mpoolDev" in ASCII */
+#define MPOOL_SB_MAGIC "\x6D\x70\x6f\x6f\x6c\x44\x65\x76"
+
+const struct blkid_idinfo mpool_idinfo =
+{
+ .name = "mpool",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_mpool,
+ .magics =
+ {
+ { .magic = MPOOL_SB_MAGIC, .len = 8},
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/netware.c b/libblkid/src/superblocks/netware.c
new file mode 100644
index 0000000..af81cf5
--- /dev/null
+++ b/libblkid/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 errno ? -errno : 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/libblkid/src/superblocks/nilfs.c b/libblkid/src/superblocks/nilfs.c
new file mode 100644
index 0000000..0226da5
--- /dev/null
+++ b/libblkid/src/superblocks/nilfs.c
@@ -0,0 +1,176 @@
+/*
+ * 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];
+} __attribute__((__packed__));
+
+#define NILFS_SB_MAGIC 0x3434
+#define NILFS_SB_OFFSET 0x400
+#define NILFS_SBB_OFFSET(_sz) ((((_sz) / 0x200) - 8) * 0x200)
+
+static int nilfs_valid_sb(blkid_probe pr, struct nilfs_super_block *sb, int is_bak)
+{
+ static unsigned char sum[4];
+ const int sumoff = offsetof(struct nilfs_super_block, s_sum);
+ size_t bytes;
+ const size_t crc_start = sumoff + 4;
+ uint32_t crc;
+
+ if (!sb || le16_to_cpu(sb->s_magic) != NILFS_SB_MAGIC)
+ return 0;
+
+ if (is_bak && blkid_probe_is_wholedisk(pr) &&
+ sb->s_dev_size != pr->size)
+ return 0;
+
+ bytes = le16_to_cpu(sb->s_bytes);
+ /* ensure that no underrun can happen in the length parameter
+ * of the crc32 call or more data are processed than read into
+ * sb */
+ if (bytes < crc_start || bytes > sizeof(struct nilfs_super_block))
+ return 0;
+
+ crc = ul_crc32(le32_to_cpu(sb->s_crc_seed), (unsigned char *)sb, sumoff);
+ crc = ul_crc32(crc, sum, 4);
+ crc = ul_crc32(crc, (unsigned char *)sb + crc_start, bytes - crc_start);
+
+ return blkid_probe_verify_csum(pr, crc, le32_to_cpu(sb->s_sum));
+}
+
+static int probe_nilfs2(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ struct nilfs_super_block *sb, *sbp, *sbb;
+ int valid[2], swp = 0;
+ uint64_t magoff;
+
+ /* primary */
+ sbp = (struct nilfs_super_block *) blkid_probe_get_buffer(
+ pr, NILFS_SB_OFFSET, sizeof(struct nilfs_super_block));
+ if (!sbp)
+ return errno ? -errno : 1;
+
+ valid[0] = nilfs_valid_sb(pr, sbp, 0);
+
+
+ /* backup */
+ sbb = (struct nilfs_super_block *) blkid_probe_get_buffer(
+ pr, NILFS_SBB_OFFSET(pr->size), sizeof(struct nilfs_super_block));
+ if (!sbb) {
+ valid[1] = 0;
+
+ /* If the primary block is valid then continue and ignore also
+ * I/O errors for backup block. Note the this is probably CD
+ * where I/O errors and the end of the disk/session are "normal".
+ */
+ if (!valid[0])
+ return errno ? -errno : 1;
+ } else
+ valid[1] = nilfs_valid_sb(pr, sbb, 1);
+
+
+ /*
+ * Compare two super blocks and set 1 in swp if the secondary
+ * super block is valid and newer. Otherwise, set 0 in swp.
+ */
+ if (!valid[0] && !valid[1])
+ return 1;
+
+ swp = valid[1] && (!valid[0] ||
+ le64_to_cpu(sbp->s_last_cno) >
+ le64_to_cpu(sbb->s_last_cno));
+ sb = swp ? sbb : sbp;
+
+ DBG(LOWPROBE, ul_debug("nilfs2: primary=%d, backup=%d, swap=%d",
+ valid[0], valid[1], swp));
+
+ if (*(sb->s_volume_name) != '\0')
+ 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));
+
+ magoff = swp ? NILFS_SBB_OFFSET(pr->size) : NILFS_SB_OFFSET;
+ magoff += offsetof(struct nilfs_super_block, s_magic);
+
+ if (blkid_probe_set_magic(pr, magoff, sizeof(sb->s_magic),
+ (unsigned char *) &sb->s_magic))
+ return 1;
+
+ if (le32_to_cpu(sb->s_log_block_size) < 32){
+ blkid_probe_set_fsblocksize(pr, 1024U << le32_to_cpu(sb->s_log_block_size));
+ blkid_probe_set_block_size(pr, 1024U << le32_to_cpu(sb->s_log_block_size));
+ }
+
+ return 0;
+}
+
+const struct blkid_idinfo nilfs2_idinfo =
+{
+ .name = "nilfs2",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_nilfs2,
+ /* default min.size is 128MiB, but 1MiB for "mkfs.nilfs2 -b 1024 -B 16" */
+ .minsz = (1024 * 1024),
+ .magics = BLKID_NONE_MAGIC
+};
diff --git a/libblkid/src/superblocks/ntfs.c b/libblkid/src/superblocks/ntfs.c
new file mode 100644
index 0000000..ab8c921
--- /dev/null
+++ b/libblkid/src/superblocks/ntfs.c
@@ -0,0 +1,260 @@
+/*
+ * 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
+/* Windows 10 Creators edition has extended the cluster size limit to 2MB */
+#define NTFS_MAX_CLUSTER_SIZE (2 * 1024 * 1024)
+
+#define MFT_RECORD_ATTR_VOLUME_NAME 0x60
+#define MFT_RECORD_ATTR_END 0xffffffff
+
+static int __probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag, int save_info)
+{
+ struct ntfs_super_block *ns;
+ struct master_file_table_record *mft;
+
+ uint32_t sectors_per_cluster, mft_record_size;
+ uint16_t sector_size;
+ uint64_t nr_clusters, off, attr_off;
+ unsigned char *buf_mft;
+
+ ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block);
+ if (!ns)
+ return errno ? -errno : 1;
+
+ /*
+ * Check bios parameters block
+ */
+ sector_size = le16_to_cpu(ns->bpb.sector_size);
+
+ if (sector_size < 256 || sector_size > 4096 || !is_power_of_2(sector_size))
+ return 1;
+
+ switch (ns->bpb.sectors_per_cluster) {
+ case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
+ sectors_per_cluster = ns->bpb.sectors_per_cluster;
+ break;
+ default:
+ if ((ns->bpb.sectors_per_cluster < 240)
+ || (ns->bpb.sectors_per_cluster > 249))
+ return 1;
+ sectors_per_cluster = 1 << (256 - ns->bpb.sectors_per_cluster);
+ }
+
+ if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) *
+ 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 {
+ int8_t mft_record_size_shift = 0 - ns->clusters_per_mft_record;
+ if (mft_record_size_shift < 0 || mft_record_size_shift >= 31)
+ return 1;
+ mft_record_size = 1 << mft_record_size_shift;
+ }
+
+ 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(LOWPROBE, ul_debug("NTFS: sector_size=%"PRIu16", mft_record_size=%"PRIu32", "
+ "sectors_per_cluster=%"PRIu32", nr_clusters=%"PRIu64" "
+ "cluster_offset=%"PRIu64"",
+ sector_size, mft_record_size,
+ sectors_per_cluster, nr_clusters,
+ off));
+
+ if (mft_record_size < 4)
+ return 1;
+
+ buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
+ if (!buf_mft)
+ return errno ? -errno : 1;
+
+ if (memcmp(buf_mft, "FILE", 4) != 0)
+ return 1;
+
+ off += MFT_RECORD_VOLUME * mft_record_size;
+
+ buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
+ if (!buf_mft)
+ return errno ? -errno : 1;
+
+ if (memcmp(buf_mft, "FILE", 4) != 0)
+ return 1;
+
+ /* return if caller does not care about UUID and LABEL */
+ if (!save_info)
+ return 0;
+
+ mft = (struct master_file_table_record *) buf_mft;
+ attr_off = le16_to_cpu(mft->attrs_offset);
+
+ while (attr_off + sizeof(struct file_attribute) <= 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 (le32_to_cpu(attr->type) == (uint32_t) MFT_RECORD_ATTR_END)
+ break;
+ if (le32_to_cpu(attr->type) == (uint32_t) 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;
+
+ if (attr_off + val_off + val_len <= mft_record_size)
+ blkid_probe_set_utf8label(pr, val, val_len,
+ UL_ENCODE_UTF16LE);
+ break;
+ }
+
+ attr_off += attr_len;
+ }
+
+
+ blkid_probe_set_fsblocksize(pr, sector_size * sectors_per_cluster);
+ blkid_probe_set_block_size(pr, sector_size);
+ blkid_probe_set_fssize(pr, le64_to_cpu(ns->number_of_sectors) * sector_size);
+
+ blkid_probe_sprintf_uuid(pr,
+ (unsigned char *) &ns->volume_serial,
+ sizeof(ns->volume_serial),
+ "%016" PRIX64, le64_to_cpu(ns->volume_serial));
+ return 0;
+}
+
+static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ return __probe_ntfs(pr, mag, 1);
+}
+
+int blkid_probe_is_ntfs(blkid_probe pr)
+{
+ const struct blkid_idmag *mag = NULL;
+ int rc;
+
+ rc = blkid_probe_get_idmag(pr, &ntfs_idinfo, NULL, &mag);
+ if (rc < 0)
+ return rc; /* error */
+ if (rc != BLKID_PROBE_OK || !mag)
+ return 0;
+
+ return __probe_ntfs(pr, mag, 0) == 0 ? 1 : 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/libblkid/src/superblocks/nvidia_raid.c b/libblkid/src/superblocks/nvidia_raid.c
new file mode 100644
index 0000000..d930f72
--- /dev/null
+++ b/libblkid/src/superblocks/nvidia_raid.c
@@ -0,0 +1,78 @@
+/*
+ * 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 "
+#define NVIDIA_SUPERBLOCK_SIZE 120
+
+
+static int nvraid_verify_checksum(blkid_probe pr, const struct nv_metadata *nv)
+{
+ uint32_t csum = le32_to_cpu(nv->chksum);
+ for (size_t i = 0; i < le32_to_cpu(nv->size); i++)
+ csum += le32_to_cpu(((uint32_t *) nv)[i]);
+ return blkid_probe_verify_csum(pr, csum, le32_to_cpu(nv->chksum));
+}
+
+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,
+ NVIDIA_SUPERBLOCK_SIZE);
+ if (!nv)
+ return errno ? -errno : 1;
+
+ if (memcmp(nv->vendor, NVIDIA_SIGNATURE, sizeof(NVIDIA_SIGNATURE)-1) != 0)
+ return 1;
+ if (le32_to_cpu(nv->size) * 4 != NVIDIA_SUPERBLOCK_SIZE)
+ return 1;
+ if (!nvraid_verify_checksum(pr, nv))
+ 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/libblkid/src/superblocks/ocfs.c b/libblkid/src/superblocks/ocfs.c
new file mode 100644
index 0000000..e213d66
--- /dev/null
+++ b/libblkid/src/superblocks/ocfs.c
@@ -0,0 +1,220 @@
+/*
+ * 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 errno ? -errno : 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 errno ? -errno : 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"));
+
+ if (ocfslabellen(ovl) < sizeof(ovl.label))
+ blkid_probe_set_label(pr, (unsigned char *) ovl.label,
+ ocfslabellen(ovl));
+ if (ocfsmountlen(ovh) < sizeof(ovh.mount))
+ 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 errno ? -errno : 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));
+
+ if (le32_to_cpu(osb->s_blocksize_bits) < 32){
+ blkid_probe_set_fsblocksize(pr, 1U << le32_to_cpu(osb->s_blocksize_bits));
+ blkid_probe_set_block_size(pr, 1U << le32_to_cpu(osb->s_blocksize_bits));
+ }
+
+ 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 errno ? -errno : 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/libblkid/src/superblocks/promise_raid.c b/libblkid/src/superblocks/promise_raid.c
new file mode 100644
index 0000000..75f3a20
--- /dev/null
+++ b/libblkid/src/superblocks/promise_raid.c
@@ -0,0 +1,77 @@
+/*
+ * 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__)))
+{
+ size_t i;
+ static unsigned int sectors[] = {
+ 63, 255, 256, 16, 399, 591, 675, 735, 911, 974, 991, 951, 3087
+ };
+ uint64_t nsectors;
+
+ if (pr->size < 0x40000)
+ return 1;
+ if (!S_ISREG(pr->mode) && !blkid_probe_is_wholedisk(pr))
+ return 1;
+
+ nsectors = pr->size >> 9;
+
+ for (i = 0; i < ARRAY_SIZE(sectors); i++) {
+ uint64_t off;
+ struct promise_metadata *pdc;
+
+ if (nsectors < sectors[i])
+ return 1;
+
+ off = (nsectors - sectors[i]) << 9;
+ pdc = (struct promise_metadata *)
+ blkid_probe_get_buffer(pr,
+ off,
+ sizeof(struct promise_metadata));
+ if (!pdc)
+ return errno ? -errno : 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/libblkid/src/superblocks/refs.c b/libblkid/src/superblocks/refs.c
new file mode 100644
index 0000000..ea81f20
--- /dev/null
+++ b/libblkid/src/superblocks/refs.c
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2013 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"
+
+
+const struct blkid_idinfo refs_idinfo =
+{
+ .name = "ReFS",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .magics =
+ {
+ { .magic = "\000\000\000ReFS\000", .len = 8 },
+ { NULL }
+ }
+};
+
diff --git a/libblkid/src/superblocks/reiserfs.c b/libblkid/src/superblocks/reiserfs.c
new file mode 100644
index 0000000..23d10e2
--- /dev/null
+++ b/libblkid/src/superblocks/reiserfs.c
@@ -0,0 +1,138 @@
+/*
+ * 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];
+ uint8_t rs4_dummy[3];
+ uint8_t rs4_blocksize;
+ 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 errno ? -errno : 1;
+
+ blocksize = le16_to_cpu(rs->rs_blocksize);
+
+ /* The blocksize must be at least 512B */
+ if ((blocksize >> 9) == 0)
+ return 1;
+
+ /* 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 1;
+
+ /* 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");
+
+ blkid_probe_set_fsblocksize(pr, blocksize);
+ blkid_probe_set_block_size(pr, blocksize);
+
+ return 0;
+}
+
+static int probe_reiser4(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct reiser4_super_block *rs4;
+ unsigned int blocksize;
+
+ rs4 = blkid_probe_get_sb(pr, mag, struct reiser4_super_block);
+ if (!rs4)
+ return errno ? -errno : 1;
+
+ blocksize = rs4->rs4_blocksize * 256;
+
+ 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");
+
+ blkid_probe_set_fsblocksize(pr, blocksize);
+ blkid_probe_set_block_size(pr, blocksize);
+
+ 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/libblkid/src/superblocks/romfs.c b/libblkid/src/superblocks/romfs.c
new file mode 100644
index 0000000..456cbfb
--- /dev/null
+++ b/libblkid/src/superblocks/romfs.c
@@ -0,0 +1,84 @@
+/*
+ * 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_full_size;
+ uint32_t ros_checksum;
+ unsigned char ros_volume[16];
+} __attribute__((packed));
+
+static int romfs_verify_csum(blkid_probe pr, const struct blkid_idmag *mag,
+ const struct romfs_super_block *ros)
+{
+ uint32_t csummed_size = min((uint32_t) 512,
+ be32_to_cpu(ros->ros_full_size));
+ unsigned char *csummed;
+ uint32_t csum;
+
+ if (csummed_size % sizeof(uint32_t) != 0)
+ return 0;
+
+ csummed = blkid_probe_get_sb_buffer(pr, mag, csummed_size);
+ if (!csummed)
+ return 0;
+
+ csum = 0;
+ while (csummed_size) {
+ csum += be32_to_cpu(*(uint32_t *) csummed);
+ csummed_size -= sizeof(uint32_t);
+ csummed += sizeof(uint32_t);
+ }
+ return blkid_probe_verify_csum(pr, csum, 0);
+}
+
+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 errno ? -errno : 1;
+
+ if (!romfs_verify_csum(pr, mag, ros))
+ return 1;
+
+ if (*((char *) ros->ros_volume) != '\0')
+ blkid_probe_set_label(pr, ros->ros_volume,
+ sizeof(ros->ros_volume));
+
+ blkid_probe_set_fsblocksize(pr, 1024);
+ blkid_probe_set_fssize(pr, be32_to_cpu(ros->ros_full_size));
+ blkid_probe_set_block_size(pr, 1024);
+
+ 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/libblkid/src/superblocks/silicon_raid.c b/libblkid/src/superblocks/silicon_raid.c
new file mode 100644
index 0000000..399eea8
--- /dev/null
+++ b/libblkid/src/superblocks/silicon_raid.c
@@ -0,0 +1,134 @@
+/*
+ * 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];
+ int8_t diskname[0x56 - 0x36];
+ int8_t unknown1[0x60 - 0x56];
+ uint32_t magic;
+ int8_t unknown1a[0x6C - 0x64];
+ uint32_t array_sectors_low;
+ uint32_t array_sectors_high;
+ int8_t unknown2[0x78 - 0x74];
+ uint32_t thisdisk_sectors;
+ int8_t unknown3[0x100 - 0x7C];
+ int8_t unknown4[0x104 - 0x100];
+ uint16_t product_id;
+ uint16_t vendor_id;
+ uint16_t minor_ver;
+ uint16_t major_ver;
+ uint8_t seconds;
+ uint8_t minutes;
+ uint8_t hour;
+ uint8_t day;
+ uint8_t month;
+ uint8_t year;
+ uint16_t raid0_stride;
+ int8_t unknown6[0x116 - 0x114];
+ uint8_t disk_number;
+ uint8_t type; /* SILICON_TYPE_* */
+ int8_t drives_per_striped_set;
+ int8_t striped_set_number;
+ int8_t drives_per_mirrored_set;
+ int8_t mirrored_set_number;
+ uint32_t rebuild_ptr_low;
+ uint32_t rebuild_ptr_high;
+ uint32_t incarnation_no;
+ uint8_t member_status;
+ uint8_t mirrored_set_state; /* SILICON_MIRROR_* */
+ uint8_t reported_device_location;
+ uint8_t idechannel;
+ uint8_t auto_rebuild;
+ uint8_t unknown8;
+ uint8_t text_type[0x13E - 0x12E];
+ uint16_t checksum1;
+ int8_t assumed_zeros[0x1FE - 0x140];
+ uint16_t checksum2;
+} __attribute__((packed));
+
+#define SILICON_MAGIC 0x2F000000
+
+static uint16_t silraid_checksum(struct silicon_metadata *sil)
+{
+ int sum = 0;
+ unsigned short count = offsetof(struct silicon_metadata, checksum1) / 2;
+ unsigned char *ptr = (unsigned char *) sil;
+
+ while (count--) {
+ uint16_t val;
+
+ memcpy(&val, ptr, sizeof(uint16_t));
+ sum += le16_to_cpu(val);
+
+ ptr += sizeof(uint16_t);
+ }
+
+ return (-sum & 0xFFFF);
+}
+
+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 errno ? -errno : 1;
+
+ if (le32_to_cpu(sil->magic) != SILICON_MAGIC)
+ return 1;
+ if (sil->disk_number >= 8)
+ return 1;
+ if (!blkid_probe_verify_csum(pr, silraid_checksum(sil), le16_to_cpu(sil->checksum1)))
+ 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/libblkid/src/superblocks/squashfs.c b/libblkid/src/superblocks/squashfs.c
new file mode 100644
index 0000000..92ab77a
--- /dev/null
+++ b/libblkid/src/superblocks/squashfs.c
@@ -0,0 +1,121 @@
+/*
+ * 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 magic;
+ uint32_t inode_count;
+ uint32_t mod_time;
+ uint32_t block_size;
+ uint32_t frag_count;
+ uint16_t compressor;
+ uint16_t block_log;
+ uint16_t flags;
+ uint16_t id_count;
+ uint16_t version_major;
+ uint16_t version_minor;
+ uint64_t root_inode;
+ uint64_t bytes_used;
+ uint64_t id_table;
+ uint64_t xattr_table;
+ uint64_t inode_table;
+ uint64_t dir_table;
+ uint64_t frag_table;
+ uint64_t export_table;
+} __attribute__((packed));
+
+static int probe_squashfs(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct sqsh_super_block *sq;
+ uint16_t vermaj;
+ uint16_t vermin;
+
+ sq = blkid_probe_get_sb(pr, mag, struct sqsh_super_block);
+ if (!sq)
+ return errno ? -errno : 1;
+
+ vermaj = le16_to_cpu(sq->version_major);
+ vermin = le16_to_cpu(sq->version_minor);
+ if (vermaj < 4)
+ return 1;
+
+ blkid_probe_sprintf_version(pr, "%u.%u", vermaj, vermin);
+ blkid_probe_set_fsblocksize(pr, le32_to_cpu(sq->block_size));
+ blkid_probe_set_block_size(pr, le32_to_cpu(sq->block_size));
+ blkid_probe_set_fssize(pr, le64_to_cpu(sq->bytes_used));
+
+ return 0;
+}
+
+static int probe_squashfs3(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct sqsh_super_block *sq;
+ uint16_t vermaj;
+ uint16_t vermin;
+ enum BLKID_ENDIANNESS endianness;
+
+ sq = blkid_probe_get_sb(pr, mag, struct sqsh_super_block);
+ if (!sq)
+ return errno ? -errno : 1;
+
+ if (strcmp(mag->magic, "sqsh") == 0) {
+ vermaj = be16_to_cpu(sq->version_major);
+ vermin = be16_to_cpu(sq->version_minor);
+ endianness = BLKID_ENDIANNESS_BIG;
+ } else {
+ vermaj = le16_to_cpu(sq->version_major);
+ vermin = le16_to_cpu(sq->version_minor);
+ endianness = BLKID_ENDIANNESS_LITTLE;
+ }
+
+ if (vermaj > 3)
+ return 1;
+
+ blkid_probe_sprintf_version(pr, "%u.%u", vermaj, vermin);
+
+ blkid_probe_set_fsblocksize(pr, 1024);
+ blkid_probe_set_block_size(pr, 1024);
+ blkid_probe_set_fsendianness(pr, endianness);
+
+ return 0;
+}
+
+const struct blkid_idinfo squashfs_idinfo =
+{
+ .name = "squashfs",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_squashfs,
+ .magics =
+ {
+ { .magic = "hsqs", .len = 4 },
+ { NULL }
+ }
+};
+
+const struct blkid_idinfo squashfs3_idinfo =
+{
+ .name = "squashfs3",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_squashfs3,
+ .magics =
+ {
+ { .magic = "sqsh", .len = 4 }, /* big endian */
+ { .magic = "hsqs", .len = 4 }, /* little endian */
+ { NULL }
+ }
+};
+
diff --git a/libblkid/src/superblocks/stratis.c b/libblkid/src/superblocks/stratis.c
new file mode 100644
index 0000000..fe4a452
--- /dev/null
+++ b/libblkid/src/superblocks/stratis.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 Tony Asleson <tasleson@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+/*
+ * Specification for on disk format
+ * https://stratis-storage.github.io/StratisSoftwareDesign.pdf
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include "superblocks.h"
+#include "crc32c.h"
+
+/* Macro to avoid error in struct declaration. */
+#define STRATIS_UUID_LEN 32
+/* Contains 4 hyphens and trailing null byte. */
+const int STRATIS_UUID_STR_LEN = 37;
+
+struct stratis_sb {
+ uint32_t crc32;
+ uint8_t magic[16];
+ uint64_t sectors;
+ uint8_t reserved[4];
+ uint8_t pool_uuid[STRATIS_UUID_LEN];
+ uint8_t dev_uuid[STRATIS_UUID_LEN];
+ uint64_t mda_size;
+ uint64_t reserved_size;
+ uint64_t flags;
+ uint64_t initialization_time;
+} __attribute__ ((__packed__));
+
+#define BS 512
+#define FIRST_COPY_OFFSET BS
+#define SECOND_COPY_OFFSET (BS * 9)
+#define SB_AREA_SIZE (BS * 16)
+
+const char STRATIS_MAGIC[] = "!Stra0tis\x86\xff\x02^\x41rh";
+#define MAGIC_LEN (sizeof(STRATIS_MAGIC) - 1)
+
+#define _MAGIC_OFFSET (offsetof(const struct stratis_sb, magic))
+#define MAGIC_OFFSET_COPY_1 (FIRST_COPY_OFFSET + _MAGIC_OFFSET)
+#define MAGIC_OFFSET_COPY_2 (SECOND_COPY_OFFSET + _MAGIC_OFFSET)
+
+static int stratis_valid_sb(uint8_t *p)
+{
+ const struct stratis_sb *stratis = (const struct stratis_sb *)p;
+ uint32_t crc = 0;
+
+ /* generate CRC from byte position 4 for length 508 == 512 byte sector */
+ crc = crc32c(~0L, p + sizeof(stratis->crc32),
+ BS - sizeof(stratis->crc32));
+ crc ^= ~0L;
+
+ return crc == le32_to_cpu(stratis->crc32);
+}
+
+/*
+ * Format UUID string representation to include hyphens given that Stratis stores
+ * UUIDs without hyphens in the superblock to keep the UUID length a power of 2.
+ */
+static void stratis_format_uuid(const unsigned char *src_uuid,
+ unsigned char *dst_uuid)
+{
+ int i;
+
+ for (i = 0; i < STRATIS_UUID_LEN; i++) {
+ *dst_uuid++ = *src_uuid++;
+ if (i == 7 || i == 11 || i == 15 || i == 19)
+ *dst_uuid ++ = '-';
+ }
+ *dst_uuid = '\0';
+}
+
+static int probe_stratis(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ const struct stratis_sb *stratis = NULL;
+ uint8_t *buf = blkid_probe_get_buffer(pr, 0, SB_AREA_SIZE);
+ unsigned char uuid[STRATIS_UUID_STR_LEN];
+
+ if (!buf)
+ return errno ? -errno : 1;
+
+ if (stratis_valid_sb(buf + FIRST_COPY_OFFSET)) {
+ stratis = (const struct stratis_sb *)(buf + FIRST_COPY_OFFSET);
+ } else {
+ if (!stratis_valid_sb(buf + SECOND_COPY_OFFSET))
+ return 1;
+
+ stratis = (const struct stratis_sb *)
+ (buf + SECOND_COPY_OFFSET);
+ }
+
+ stratis_format_uuid(stratis->dev_uuid, uuid);
+ blkid_probe_strncpy_uuid(pr, uuid, sizeof(uuid));
+
+ stratis_format_uuid(stratis->pool_uuid, uuid);
+ blkid_probe_set_value(pr, "POOL_UUID", uuid, sizeof(uuid));
+
+ blkid_probe_sprintf_value(pr, "BLOCKDEV_SECTORS", "%" PRIu64,
+ le64_to_cpu(stratis->sectors));
+ blkid_probe_sprintf_value(pr, "BLOCKDEV_INITTIME", "%" PRIu64,
+ le64_to_cpu(stratis->initialization_time));
+ return 0;
+}
+
+const struct blkid_idinfo stratis_idinfo = {
+ .name = "stratis",
+ .usage = BLKID_USAGE_RAID,
+ .probefunc = probe_stratis,
+ .minsz = SB_AREA_SIZE,
+ .magics = {
+ { .magic = STRATIS_MAGIC, .len = MAGIC_LEN,
+ .sboff = MAGIC_OFFSET_COPY_1},
+ { .magic = STRATIS_MAGIC, .len = MAGIC_LEN,
+ .sboff = MAGIC_OFFSET_COPY_2},
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/superblocks.c b/libblkid/src/superblocks/superblocks.c
new file mode 100644
index 0000000..96bc0ef
--- /dev/null
+++ b/libblkid/src/superblocks/superblocks.c
@@ -0,0 +1,951 @@
+/*
+ * 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 <inttypes.h>
+#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)
+ *
+ * @LOGUUID: external log UUID (e.g. xfs)
+ *
+ * @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 filesystem (implemented for XFS/BTRFS/Ext only)
+ *
+ * @FSLASTBLOCK: last fsblock/total number of fsblocks
+ *
+ * @FSBLOCKSIZE: file system block size
+ *
+ * @SYSTEM_ID: ISO9660 system identifier
+ *
+ * @PUBLISHER_ID: ISO9660 publisher identifier
+ *
+ * @APPLICATION_ID: ISO9660 application identifier
+ *
+ * @BOOT_SYSTEM_ID: ISO9660 boot system identifier
+ *
+ * @BLOCK_SIZE: minimal block size accessible by file system
+ */
+
+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[] =
+{
+ /* In case the volume is locked with OPAL we are going to get
+ * an I/O error when reading past the LUKS header, so try it
+ * first. */
+ &luks_idinfo,
+
+ /* 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,
+
+ &bcache_idinfo,
+ &bcachefs_idinfo,
+ &bluestore_idinfo,
+ &drbd_idinfo,
+ &drbdmanage_idinfo,
+ &drbdproxy_datalog_idinfo,
+ &lvm2_idinfo,
+ &lvm1_idinfo,
+ &snapcow_idinfo,
+ &verity_hash_idinfo,
+ &integrity_idinfo,
+ &vmfs_volume_idinfo,
+ &ubi_idinfo,
+ &vdo_idinfo,
+ &stratis_idinfo,
+ &bitlocker_idinfo,
+ &cs_fvault2_idinfo,
+
+ /* Filesystems */
+ &vfat_idinfo,
+ &swsuspend_idinfo,
+ &swap_idinfo,
+ &xfs_idinfo,
+ &xfs_log_idinfo,
+ &exfs_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,
+ &refs_idinfo,
+ &cramfs_idinfo,
+ &romfs_idinfo,
+ &minix_idinfo,
+ &gfs_idinfo,
+ &gfs2_idinfo,
+ &ocfs_idinfo,
+ &ocfs2_idinfo,
+ &oracleasm_idinfo,
+ &vxfs_idinfo,
+ &squashfs_idinfo,
+ &squashfs3_idinfo,
+ &netware_idinfo,
+ &btrfs_idinfo,
+ &ubifs_idinfo,
+ &bfs_idinfo,
+ &vmfs_fs_idinfo,
+ &befs_idinfo,
+ &nilfs2_idinfo,
+ &exfat_idinfo,
+ &f2fs_idinfo,
+ &mpool_idinfo,
+ &apfs_idinfo,
+ &zonefs_idinfo,
+ &erofs_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)
+{
+ 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)
+{
+ 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;
+
+ 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(LOWPROBE, ul_debug("a new probing usage-filter initialized"));
+ return 0;
+}
+
+/**
+ * blkid_known_fstype:
+ * @fstype: filesystem name
+ *
+ * Returns: 1 for known filesystems, or 0 for unknown filesystem.
+ */
+int blkid_known_fstype(const char *fstype)
+{
+ size_t i;
+
+ 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;
+ int rc = BLKID_PROBE_NONE;
+
+ if (chn->idx < -1)
+ return -EINVAL;
+
+ blkid_probe_chain_reset_values(pr, chn);
+
+ if (pr->flags & BLKID_FL_NOSCAN_DEV) {
+ DBG(LOWPROBE, ul_debug("*** ignore (noscan flag)"));
+ return BLKID_PROBE_NONE;
+ }
+
+ 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 */
+ DBG(LOWPROBE, ul_debug("*** ignore (size <= 1024)"));
+ return BLKID_PROBE_NONE;
+ }
+
+ DBG(LOWPROBE, ul_debug("--> starting probing loop [SUBLKS idx=%d]",
+ chn->idx));
+
+ 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;
+ uint64_t off = 0;
+
+ chn->idx = i;
+ id = idinfos[i];
+
+ if (chn->fltr && blkid_bmp_get_item(chn->fltr, i)) {
+ DBG(LOWPROBE, ul_debug("filter out: %s", id->name));
+ rc = BLKID_PROBE_NONE;
+ continue;
+ }
+
+ if (id->minsz && (unsigned)id->minsz > pr->size) {
+ rc = BLKID_PROBE_NONE;
+ 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)) {
+ rc = BLKID_PROBE_NONE;
+ continue;
+ }
+
+ /* don't probe for RAIDs on floppies */
+ if ((id->usage & BLKID_USAGE_RAID) && blkid_probe_is_tiny(pr)) {
+ rc = BLKID_PROBE_NONE;
+ continue;
+ }
+
+ DBG(LOWPROBE, ul_debug("[%zd] %s:", i, id->name));
+
+ rc = blkid_probe_get_idmag(pr, id, &off, &mag);
+ if (rc < 0)
+ break;
+ if (rc != BLKID_PROBE_OK)
+ continue;
+
+ /* final check by probing function */
+ if (id->probefunc) {
+ DBG(LOWPROBE, ul_debug("\tcall probefunc()"));
+ errno = 0;
+ rc = id->probefunc(pr, mag);
+ if (rc != BLKID_PROBE_OK) {
+ blkid_probe_chain_reset_values(pr, chn);
+ if (rc < 0)
+ break;
+ continue;
+ }
+ }
+
+ /* all checks passed */
+ if (chn->flags & BLKID_SUBLKS_TYPE)
+ rc = blkid_probe_set_value(pr, "TYPE",
+ (const 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,
+ (const unsigned char *) mag->magic);
+ if (rc) {
+ blkid_probe_chain_reset_values(pr, chn);
+ DBG(LOWPROBE, ul_debug("failed to set result -- ignore"));
+ continue;
+ }
+
+ DBG(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [SUBLKS idx=%d]",
+ id->name, chn->idx));
+ return BLKID_PROBE_OK;
+ }
+
+ DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed=%d) [SUBLKS idx=%d]",
+ rc, chn->idx));
+ return rc;
+}
+
+/*
+ * This is the same function as blkid_do_probe(), but returns only one result
+ * (cannot be used in while()) and checks for ambivalent 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 list_head vals;
+ int idx = -1;
+ int count = 0;
+ int intol = 0;
+ int rc;
+
+ INIT_LIST_HEAD(&vals);
+
+ if (pr->flags & BLKID_FL_NOSCAN_DEV)
+ return BLKID_PROBE_NONE;
+
+ while ((rc = superblocks_probe(pr, chn)) == 0) {
+
+ if (blkid_probe_is_tiny(pr) && !count)
+ return BLKID_PROBE_OK; /* floppy or so -- returns the first result. */
+
+ 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 */
+ blkid_probe_chain_save_values(pr, chn, &vals);
+ idx = chn->idx;
+ }
+ }
+
+ if (rc < 0)
+ goto done; /* error */
+
+ if (count > 1 && intol) {
+ DBG(LOWPROBE, ul_debug("ERROR: superblocks chain: "
+ "ambivalent result detected (%d filesystems)!",
+ count));
+ rc = BLKID_PROBE_AMBIGUOUS; /* error, ambivalent result (more FS) */
+ goto done;
+ }
+ if (!count) {
+ rc = BLKID_PROBE_NONE;
+ goto done;
+ }
+
+ if (idx != -1) {
+ /* restore the first result */
+ blkid_probe_chain_reset_values(pr, chn);
+ blkid_probe_append_values_list(pr, &vals);
+ chn->idx = idx;
+ }
+
+ /*
+ * The RAID device could be partitioned. The problem are RAID1 devices
+ * where the partition table is visible from underlying 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;
+
+ rc = BLKID_PROBE_OK;
+done:
+ blkid_probe_free_values_list(&vals);
+ return rc;
+}
+
+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",
+ (const 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;
+}
+
+int blkid_probe_set_block_size(blkid_probe pr, unsigned block_size)
+{
+ return blkid_probe_sprintf_value(pr, "BLOCK_SIZE", "%u", block_size);
+}
+
+static int blkid_probe_set_usage(blkid_probe pr, int usage)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ const 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",
+ (const unsigned char *) u, strlen(u) + 1);
+}
+
+int blkid_probe_set_fssize(blkid_probe pr, uint64_t size)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+ if (!(chn->flags & BLKID_SUBLKS_FSINFO))
+ return 0;
+
+ return blkid_probe_sprintf_value(pr, "FSSIZE", "%" PRIu64, size);
+}
+
+int blkid_probe_set_fslastblock(blkid_probe pr, uint64_t lastblock)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+ if (!(chn->flags & BLKID_SUBLKS_FSINFO))
+ return 0;
+
+ return blkid_probe_sprintf_value(pr, "FSLASTBLOCK", "%" PRIu64,
+ lastblock);
+}
+
+int blkid_probe_set_fsblocksize(blkid_probe pr, uint32_t block_size)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+ if (!(chn->flags & BLKID_SUBLKS_FSINFO))
+ return 0;
+
+ return blkid_probe_sprintf_value(pr, "FSBLOCKSIZE", "%" PRIu32,
+ block_size);
+}
+
+int blkid_probe_set_fsendianness(blkid_probe pr, enum BLKID_ENDIANNESS endianness)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ const char *value;
+
+ if (!(chn->flags & BLKID_SUBLKS_FSINFO))
+ return 0;
+
+ switch (endianness) {
+ case BLKID_ENDIANNESS_LITTLE:
+ value = "LITTLE";
+ break;
+ case BLKID_ENDIANNESS_BIG:
+ value = "BIG";
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return blkid_probe_set_value(pr, "ENDIANNESS",
+ (const unsigned char *) value, strlen(value) + 1);
+
+}
+
+int blkid_probe_set_id_label(blkid_probe pr, const char *name,
+ const unsigned char *data, size_t len)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ struct blkid_prval *v;
+ int rc = 0;
+
+ if (!(chn->flags & BLKID_SUBLKS_LABEL))
+ return 0;
+
+ v = blkid_probe_assign_value(pr, name);
+ if (!v)
+ return -ENOMEM;
+
+ rc = blkid_probe_value_set_data(v, data, len);
+ if (!rc) {
+ /* remove white spaces */
+ v->len = blkid_rtrim_whitespace(v->data) + 1;
+ if (v->len > 1)
+ v->len = blkid_ltrim_whitespace(v->data) + 1;
+ if (v->len > 1)
+ return 0;
+ }
+
+ blkid_probe_free_value(v);
+ return rc;
+
+}
+
+int blkid_probe_set_utf8_id_label(blkid_probe pr, const char *name,
+ const unsigned char *data, size_t len, int enc)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ struct blkid_prval *v;
+ int rc = 0;
+
+ if (!(chn->flags & BLKID_SUBLKS_LABEL))
+ return 0;
+
+ v = blkid_probe_assign_value(pr, name);
+ if (!v)
+ return -ENOMEM;
+
+ v->len = (len * 3) + 1;
+ v->data = calloc(1, v->len);
+ if (!v->data)
+ rc = -ENOMEM;
+
+ if (!rc) {
+ ul_encode_to_utf8(enc, v->data, v->len, data, len);
+ v->len = blkid_rtrim_whitespace(v->data) + 1;
+ if (v->len > 1)
+ v->len = blkid_ltrim_whitespace(v->data) + 1;
+ if (v->len > 1)
+ return 0;
+ }
+
+ blkid_probe_free_value(v);
+ return rc;
+}
+
+int blkid_probe_set_label(blkid_probe pr, const unsigned char *label, size_t len)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ struct blkid_prval *v;
+ int rc = 0;
+
+ if ((chn->flags & BLKID_SUBLKS_LABELRAW) &&
+ (rc = blkid_probe_set_value(pr, "LABEL_RAW", label, len)) < 0)
+ return rc;
+
+ if (!(chn->flags & BLKID_SUBLKS_LABEL))
+ return 0;
+
+ v = blkid_probe_assign_value(pr, "LABEL");
+ if (!v)
+ return -ENOMEM;
+
+ rc = blkid_probe_value_set_data(v, label, len);
+ if (!rc) {
+ v->len = blkid_rtrim_whitespace(v->data) + 1;
+ if (v->len > 1)
+ return 0;
+ }
+
+ blkid_probe_free_value(v);
+ return rc;
+}
+
+int blkid_probe_set_utf8label(blkid_probe pr, const unsigned char *label,
+ size_t len, int enc)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ struct blkid_prval *v;
+ int rc = 0;
+
+ if ((chn->flags & BLKID_SUBLKS_LABELRAW) &&
+ (rc = blkid_probe_set_value(pr, "LABEL_RAW", label, len)) < 0)
+ return rc;
+
+ if (!(chn->flags & BLKID_SUBLKS_LABEL))
+ return 0;
+
+ v = blkid_probe_assign_value(pr, "LABEL");
+ if (!v)
+ return -ENOMEM;
+
+ v->len = (len * 3) + 1;
+ v->data = calloc(1, v->len);
+ if (!v->data)
+ rc = -ENOMEM;
+ if (!rc) {
+ ul_encode_to_utf8(enc, v->data, v->len, label, len);
+ v->len = blkid_rtrim_whitespace(v->data) + 1;
+ if (v->len > 1)
+ return 0;
+ }
+
+ blkid_probe_free_value(v);
+ return rc;
+}
+
+int blkid_probe_sprintf_uuid(blkid_probe pr, const unsigned char *uuid,
+ size_t len, const char *fmt, ...)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ va_list ap;
+ int rc = 0;
+
+ if (blkid_uuid_is_empty(uuid, len))
+ return 0;
+
+ if ((chn->flags & BLKID_SUBLKS_UUIDRAW) &&
+ (rc = blkid_probe_set_value(pr, "UUID_RAW", uuid, len)) < 0)
+ return rc;
+
+ if (!(chn->flags & BLKID_SUBLKS_UUID))
+ return 0;
+
+ va_start(ap, fmt);
+ rc = blkid_probe_vsprintf_value(pr, "UUID", fmt, ap);
+ va_end(ap);
+
+ return rc;
+}
+
+/* function to set UUIDs that are in superblocks stored as strings */
+int blkid_probe_strncpy_uuid(blkid_probe pr, const unsigned char *str, size_t len)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ struct blkid_prval *v;
+ int rc = 0;
+
+ if (str == NULL || *str == '\0')
+ return -EINVAL;
+
+ if (!len)
+ len = strlen((const char *) str);
+
+ if ((chn->flags & BLKID_SUBLKS_UUIDRAW) &&
+ (rc = blkid_probe_set_value(pr, "UUID_RAW", str, len)) < 0)
+ return rc;
+
+ if (!(chn->flags & BLKID_SUBLKS_UUID))
+ return 0;
+
+ v = blkid_probe_assign_value(pr, "UUID");
+ if (!v)
+ rc= -ENOMEM;
+ if (!rc)
+ rc = blkid_probe_value_set_data(v, str, len);
+ if (!rc) {
+ v->len = blkid_rtrim_whitespace(v->data) + 1;
+ if (v->len > 1)
+ return 0;
+ }
+
+ blkid_probe_free_value(v);
+ return rc;
+}
+
+/* default _set_uuid function to set DCE UUIDs */
+int blkid_probe_set_uuid_as(blkid_probe pr, const unsigned char *uuid, const char *name)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+ struct blkid_prval *v;
+ int rc = 0;
+
+ if (blkid_uuid_is_empty(uuid, 16))
+ return 0;
+
+ if (!name) {
+ if ((chn->flags & BLKID_SUBLKS_UUIDRAW) &&
+ (rc = blkid_probe_set_value(pr, "UUID_RAW", uuid, 16)) < 0)
+ return rc;
+
+ if (!(chn->flags & BLKID_SUBLKS_UUID))
+ return 0;
+
+ v = blkid_probe_assign_value(pr, "UUID");
+ } else
+ v = blkid_probe_assign_value(pr, name);
+
+ if (!v)
+ return -ENOMEM;
+
+ v->len = UUID_STR_LEN;
+ v->data = calloc(1, v->len);
+ if (!v->data)
+ rc = -ENOMEM;
+
+ if (!rc) {
+ blkid_unparse_uuid(uuid, (char *) v->data, v->len);
+ return 0;
+ }
+
+ blkid_probe_free_value(v);
+ return rc;
+}
+
+int blkid_probe_set_uuid(blkid_probe pr, const 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_type().
+ */
+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/libblkid/src/superblocks/superblocks.h b/libblkid/src/superblocks/superblocks.h
new file mode 100644
index 0000000..a990058
--- /dev/null
+++ b/libblkid/src/superblocks/superblocks.h
@@ -0,0 +1,136 @@
+/*
+ * 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"
+
+enum BLKID_ENDIANNESS {
+ BLKID_ENDIANNESS_LITTLE,
+ BLKID_ENDIANNESS_BIG,
+};
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define BLKID_ENDIANNESS_NATIVE BLKID_ENDIANNESS_LITTLE
+#define BLKID_ENDIANNESS_OTHER BLKID_ENDIANNESS_BIG
+#else
+#define BLKID_ENDIANNESS_NATIVE BLKID_ENDIANNESS_BIG
+#define BLKID_ENDIANNESS_OTHER BLKID_ENDIANNESS_LITTLE
+#endif
+
+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 xfs_log_idinfo;
+extern const struct blkid_idinfo exfs_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 refs_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 integrity_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 squashfs3_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 ubi_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 bluestore_idinfo;
+extern const struct blkid_idinfo drbd_idinfo;
+extern const struct blkid_idinfo drbdmanage_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;
+extern const struct blkid_idinfo f2fs_idinfo;
+extern const struct blkid_idinfo bcache_idinfo;
+extern const struct blkid_idinfo bcachefs_idinfo;
+extern const struct blkid_idinfo mpool_idinfo;
+extern const struct blkid_idinfo vdo_idinfo;
+extern const struct blkid_idinfo stratis_idinfo;
+extern const struct blkid_idinfo bitlocker_idinfo;
+extern const struct blkid_idinfo apfs_idinfo;
+extern const struct blkid_idinfo zonefs_idinfo;
+extern const struct blkid_idinfo erofs_idinfo;
+extern const struct blkid_idinfo cs_fvault2_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, const unsigned char *label, size_t len);
+extern int blkid_probe_set_utf8label(blkid_probe pr, const unsigned char *label,
+ size_t len, int enc);
+extern int blkid_probe_sprintf_uuid(blkid_probe pr, const unsigned char *uuid,
+ size_t len, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)));
+extern int blkid_probe_strncpy_uuid(blkid_probe pr, const unsigned char *str, size_t len);
+
+extern int blkid_probe_set_uuid(blkid_probe pr, const unsigned char *uuid);
+extern int blkid_probe_set_uuid_as(blkid_probe pr, const unsigned char *uuid, const char *name);
+
+extern int blkid_probe_set_id_label(blkid_probe pr, const char *name,
+ const unsigned char *data, size_t len);
+extern int blkid_probe_set_utf8_id_label(blkid_probe pr, const char *name,
+ const unsigned char *data, size_t len, int enc);
+
+int blkid_probe_set_block_size(blkid_probe pr, unsigned block_size);
+int blkid_probe_set_fssize(blkid_probe pr, uint64_t size);
+int blkid_probe_set_fslastblock(blkid_probe pr, uint64_t lastblock);
+int blkid_probe_set_fsblocksize(blkid_probe pr, uint32_t block_size);
+int blkid_probe_set_fsendianness(blkid_probe pr, enum BLKID_ENDIANNESS endianness);
+
+extern int blkid_probe_is_bitlocker(blkid_probe pr);
+extern int blkid_probe_is_ntfs(blkid_probe pr);
+
+#endif /* _BLKID_SUPERBLOCKS_H */
diff --git a/libblkid/src/superblocks/swap.c b/libblkid/src/superblocks/swap.c
new file mode 100644
index 0000000..67224bc
--- /dev/null
+++ b/libblkid/src/superblocks/swap.c
@@ -0,0 +1,196 @@
+/*
+ * 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 void swap_set_info_swap1(blkid_probe pr,
+ const struct blkid_idmag *mag,
+ const struct swap_header_v1_2 *hdr)
+{
+ enum BLKID_ENDIANNESS endianness = le32_to_cpu(hdr->version) == 1 ?
+ BLKID_ENDIANNESS_LITTLE : BLKID_ENDIANNESS_BIG;
+ blkid_probe_set_fsendianness(pr, endianness);
+
+ uint32_t pagesize = mag->sboff + mag->len;
+ blkid_probe_set_fsblocksize(pr, pagesize);
+
+ uint32_t lastpage = endianness == BLKID_ENDIANNESS_LITTLE ?
+ le32_to_cpu(hdr->lastpage) : be32_to_cpu(hdr->lastpage);
+ blkid_probe_set_fssize(pr, (uint64_t) pagesize * lastpage);
+}
+
+static int swap_set_info(blkid_probe pr, const struct blkid_idmag *mag,
+ 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 errno ? -errno : 1;
+
+ /* SWAPSPACE2 - check for wrong version or zeroed pagecount */
+ if (strcmp(version, "1") == 0) {
+ if (hdr->version != 1 && swab32(hdr->version) != 1) {
+ DBG(LOWPROBE, ul_debug("incorrect swap version"));
+ return 1;
+ }
+ if (hdr->lastpage == 0) {
+ DBG(LOWPROBE, ul_debug("not set last swap page"));
+ return 1;
+ }
+ swap_set_info_swap1(pr, mag, hdr);
+ }
+
+ /* 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 errno ? -errno : 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, "0");
+ return 0;
+
+ }
+
+ if (!memcmp(mag->magic, "SWAPSPACE2", mag->len))
+ return swap_set_info(pr, mag, "1");
+
+ 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, mag, "s1suspend");
+ if (!memcmp(mag->magic, "S2SUSPEND", mag->len))
+ return swap_set_info(pr, mag, "s2suspend");
+ if (!memcmp(mag->magic, "ULSUSPEND", mag->len))
+ return swap_set_info(pr, mag, "ulsuspend");
+ if (!memcmp(mag->magic, TOI_MAGIC_STRING, TOI_MAGIC_STRLEN))
+ return swap_set_info(pr, mag, "tuxonice");
+ if (!memcmp(mag->magic, "LINHIB0001", mag->len))
+ return swap_set_info(pr, mag, "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 =
+ {
+ { .magic = "SWAP-SPACE", .len = 10, .sboff = 0xff6 },
+ { .magic = "SWAPSPACE2", .len = 10, .sboff = 0xff6 },
+ { .magic = "SWAP-SPACE", .len = 10, .sboff = 0x1ff6 },
+ { .magic = "SWAPSPACE2", .len = 10, .sboff = 0x1ff6 },
+ { .magic = "SWAP-SPACE", .len = 10, .sboff = 0x3ff6 },
+ { .magic = "SWAPSPACE2", .len = 10, .sboff = 0x3ff6 },
+ { .magic = "SWAP-SPACE", .len = 10, .sboff = 0x7ff6 },
+ { .magic = "SWAPSPACE2", .len = 10, .sboff = 0x7ff6 },
+ { .magic = "SWAP-SPACE", .len = 10, .sboff = 0xfff6 },
+ { .magic = "SWAPSPACE2", .len = 10, .sboff = 0xfff6 },
+ { NULL }
+ }
+};
+
+
+const struct blkid_idinfo swsuspend_idinfo =
+{
+ .name = "swsuspend",
+ .usage = BLKID_USAGE_OTHER,
+ .probefunc = probe_swsuspend,
+ .minsz = 10 * 4096, /* 10 pages */
+ .magics =
+ {
+ { .magic = TOI_MAGIC_STRING, .len = TOI_MAGIC_STRLEN },
+ { .magic = "S1SUSPEND", .len = 9, .sboff = 0xff6 },
+ { .magic = "S2SUSPEND", .len = 9, .sboff = 0xff6 },
+ { .magic = "ULSUSPEND", .len = 9, .sboff = 0xff6 },
+ { .magic = "LINHIB0001", .len = 10, .sboff = 0xff6 },
+
+ { .magic = "S1SUSPEND", .len = 9, .sboff = 0x1ff6 },
+ { .magic = "S2SUSPEND", .len = 9, .sboff = 0x1ff6 },
+ { .magic = "ULSUSPEND", .len = 9, .sboff = 0x1ff6 },
+ { .magic = "LINHIB0001", .len = 10, .sboff = 0x1ff6 },
+
+ { .magic = "S1SUSPEND", .len = 9, .sboff = 0x3ff6 },
+ { .magic = "S2SUSPEND", .len = 9, .sboff = 0x3ff6 },
+ { .magic = "ULSUSPEND", .len = 9, .sboff = 0x3ff6 },
+ { .magic = "LINHIB0001", .len = 10, .sboff = 0x3ff6 },
+
+ { .magic = "S1SUSPEND", .len = 9, .sboff = 0x7ff6 },
+ { .magic = "S2SUSPEND", .len = 9, .sboff = 0x7ff6 },
+ { .magic = "ULSUSPEND", .len = 9, .sboff = 0x7ff6 },
+ { .magic = "LINHIB0001", .len = 10, .sboff = 0x7ff6 },
+
+ { .magic = "S1SUSPEND", .len = 9, .sboff = 0xfff6 },
+ { .magic = "S2SUSPEND", .len = 9, .sboff = 0xfff6 },
+ { .magic = "ULSUSPEND", .len = 9, .sboff = 0xfff6 },
+ { .magic = "LINHIB0001", .len = 10, .sboff = 0xfff6 },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/sysv.c b/libblkid/src/superblocks/sysv.c
new file mode 100644
index 0000000..421660e
--- /dev/null
+++ b/libblkid/src/superblocks/sysv.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
+ *
+ * This is written from scratch 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 errno ? -errno : 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 errno ? -errno : 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/libblkid/src/superblocks/ubi.c b/libblkid/src/superblocks/ubi.c
new file mode 100644
index 0000000..fbb7c5e
--- /dev/null
+++ b/libblkid/src/superblocks/ubi.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl>
+ *
+ * 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"
+#include "crc32.h"
+
+struct ubi_ec_hdr {
+ uint32_t magic;
+ uint8_t version;
+ uint8_t padding1[3];
+ uint64_t ec;
+ uint32_t vid_hdr_offset;
+ uint32_t data_offset;
+ uint32_t image_seq;
+ uint8_t padding2[32];
+ uint32_t hdr_crc;
+} __attribute__((packed));
+
+static int ubi_verify_csum(blkid_probe pr, const struct ubi_ec_hdr *hdr)
+{
+ return blkid_probe_verify_csum(pr,
+ ul_crc32(~0LL, (unsigned char *) hdr,
+ sizeof(*hdr) - sizeof(hdr->hdr_crc)),
+ be32_to_cpu(hdr->hdr_crc));
+}
+
+static int probe_ubi(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct ubi_ec_hdr *hdr;
+
+ hdr = blkid_probe_get_sb(pr, mag, struct ubi_ec_hdr);
+ if (!hdr)
+ return -1;
+
+ if (!ubi_verify_csum(pr, hdr))
+ return -1;
+
+ blkid_probe_sprintf_version(pr, "%u", hdr->version);
+ blkid_probe_sprintf_uuid(pr, (unsigned char *)&hdr->image_seq, 4, "%u",
+ be32_to_cpu(hdr->image_seq));
+ return 0;
+}
+
+const struct blkid_idinfo ubi_idinfo =
+{
+ .name = "ubi",
+ .usage = BLKID_USAGE_RAID,
+ .probefunc = probe_ubi,
+ .magics =
+ {
+ { .magic = "UBI#", .len = 4 },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/ubifs.c b/libblkid/src/superblocks/ubifs.c
new file mode 100644
index 0000000..8dece28
--- /dev/null
+++ b/libblkid/src/superblocks/ubifs.c
@@ -0,0 +1,137 @@
+/*
+ * 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"
+#include "crc32.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 ubifs_verify_csum(blkid_probe pr, const struct ubifs_sb_node *sb)
+{
+ size_t csummed_offset = offsetof(struct ubifs_ch, sqnum);
+ uint32_t crc = ul_crc32(~0LL,
+ (unsigned char *) sb + csummed_offset,
+ sizeof(*sb) - csummed_offset);
+ return blkid_probe_verify_csum(pr, crc, le32_to_cpu(sb->ch.crc));
+}
+
+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 errno ? -errno : 1;
+
+ if (!ubifs_verify_csum(pr, sb))
+ return 1;
+
+ blkid_probe_set_uuid(pr, sb->uuid);
+ blkid_probe_sprintf_version(pr, "w%dr%d",
+ le32_to_cpu(sb->fmt_version),
+ le32_to_cpu(sb->ro_compat_version));
+ blkid_probe_set_fssize(pr,
+ (uint64_t) le32_to_cpu(sb->leb_size)
+ * le32_to_cpu(sb->leb_cnt));
+ 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/libblkid/src/superblocks/udf.c b/libblkid/src/superblocks/udf.c
new file mode 100644
index 0000000..36adf60
--- /dev/null
+++ b/libblkid/src/superblocks/udf.c
@@ -0,0 +1,596 @@
+/*
+ * 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) 2014-2017 Pali Rohár <pali.rohar@gmail.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"
+
+#define is_charset_udf(charspec) ((charspec).type == 0 && strncmp((charspec).info, "OSTA Compressed Unicode", sizeof((charspec).info)) == 0)
+
+#define udf_cid_to_enc(cid) ((cid) == 8 ? UL_ENCODE_LATIN1 : (cid) == 16 ? UL_ENCODE_UTF16BE : -1)
+
+struct charspec {
+ uint8_t type;
+ char info[63];
+} __attribute__((packed));
+
+struct dstring128 {
+ uint8_t cid;
+ uint8_t c[126];
+ uint8_t clen;
+} __attribute__((packed));
+
+struct dstring32 {
+ uint8_t cid;
+ uint8_t c[30];
+ uint8_t clen;
+} __attribute__((packed));
+
+struct dstring36 {
+ uint8_t cid;
+ uint8_t c[34];
+ uint8_t clen;
+} __attribute__((packed));
+
+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 dstring32 ident;
+ uint16_t vds_num;
+ uint16_t max_vol_seq;
+ uint16_t ichg_lvl;
+ uint16_t max_ichg_lvl;
+ uint32_t charset_list;
+ uint32_t max_charset_list;
+ struct dstring128 volset_id;
+ struct charspec desc_charset;
+ struct charspec exp_charset;
+ uint32_t vol_abstract[2];
+ uint32_t vol_copyright[2];
+ uint8_t app_id_flags;
+ char app_id[23];
+ uint8_t app_id_reserved[8];
+ uint8_t recording_date[12];
+ uint8_t imp_id_flags;
+ char imp_id[23];
+ uint8_t imp_id_os_class;
+ uint8_t imp_id_os_id;
+ uint8_t imp_id_reserved[6];
+ } __attribute__((packed)) primary;
+
+ struct logical_descriptor {
+ uint32_t seq_num;
+ struct charspec desc_charset;
+ struct dstring128 logvol_id;
+ uint32_t logical_blocksize;
+ uint8_t domain_id_flags;
+ char domain_id[23];
+ uint16_t udf_rev;
+ uint8_t domain_suffix_flags;
+ uint8_t reserved[5];
+ uint8_t logical_contents_use[16];
+ uint32_t map_table_length;
+ uint32_t num_partition_maps;
+ uint8_t imp_id[32];
+ uint8_t imp_use[128];
+ uint32_t lvid_length;
+ uint32_t lvid_location;
+ } __attribute__((packed)) logical;
+
+ struct logical_vol_integ_descriptor {
+ uint8_t recording_date[12];
+ uint32_t type;
+ uint32_t next_lvid_length;
+ uint32_t next_lvid_location;
+ uint8_t logical_contents_use[32];
+ uint32_t num_partitions;
+ uint32_t imp_use_length;
+ } __attribute__((packed)) logical_vol_integ;
+
+ struct imp_use_volume_descriptor {
+ uint32_t seq_num;
+ uint8_t lvi_id_flags;
+ char lvi_id[23];
+ uint16_t lvi_id_udf_rev;
+ uint8_t lvi_id_os_class;
+ uint8_t lvi_id_os_id;
+ uint8_t lvi_id_reserved[4];
+ struct charspec lvi_charset;
+ struct dstring128 logvol_id;
+ struct dstring36 lvinfo1;
+ struct dstring36 lvinfo2;
+ struct dstring36 lvinfo3;
+ } __attribute__((packed)) imp_use_volume;
+ } __attribute__((packed)) type;
+
+} __attribute__((packed));
+
+#define TAG_ID_PVD 1
+#define TAG_ID_AVDP 2
+#define TAG_ID_IUVD 4
+#define TAG_ID_LVD 6
+#define TAG_ID_TD 8
+#define TAG_ID_LVID 9
+
+struct volume_structure_descriptor {
+ uint8_t type;
+ uint8_t id[5];
+ uint8_t version;
+} __attribute__((packed));
+
+#define UDF_VSD_OFFSET 0x8000LL
+
+struct logical_vol_integ_descriptor_imp_use
+{
+ uint8_t imp_id[32];
+ uint32_t num_files;
+ uint32_t num_dirs;
+ uint16_t min_udf_read_rev;
+ uint16_t min_udf_write_rev;
+ uint16_t max_udf_write_rev;
+} __attribute__ ((packed));
+
+#define UDF_LVIDIU_OFFSET(vd) (sizeof((vd).tag) + sizeof((vd).type.logical_vol_integ) + 2 * 4 * le32_to_cpu((vd).type.logical_vol_integ.num_partitions))
+#define UDF_LVIDIU_LENGTH(vd) (le32_to_cpu((vd).type.logical_vol_integ.imp_use_length))
+
+static inline int gen_uuid_from_volset_id(unsigned char uuid[17], struct dstring128 *volset_id)
+{
+ int enc;
+ size_t i;
+ size_t len;
+ size_t clen;
+ size_t nonhexpos;
+ unsigned char buf[17];
+
+ memset(buf, 0, sizeof(buf));
+
+ clen = volset_id->clen;
+ if (clen > 0)
+ --clen;
+ if (clen > sizeof(volset_id->c))
+ clen = sizeof(volset_id->c);
+
+ enc = udf_cid_to_enc(volset_id->cid);
+ if (enc == -1)
+ return -1;
+
+ len = ul_encode_to_utf8(enc, buf, sizeof(buf), volset_id->c, clen);
+ if (len < 8)
+ return -1;
+
+ nonhexpos = 16;
+ for (i = 0; i < 16; ++i) {
+ if (!isxdigit(buf[i])) {
+ nonhexpos = i;
+ break;
+ }
+ }
+
+ if (nonhexpos < 8) {
+ snprintf((char *) uuid, 17, "%02x%02x%02x%02x%02x%02x%02x%02x",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+ } else if (nonhexpos < 16) {
+ for (i = 0; i < 8; ++i)
+ uuid[i] = tolower(buf[i]);
+ snprintf((char *) uuid + 8, 9, "%02x%02x%02x%02x",
+ buf[8], buf[9], buf[10], buf[11]);
+ } else {
+ for (i = 0; i < 16; ++i)
+ uuid[i] = tolower(buf[i]);
+ uuid[16] = 0;
+ }
+
+ return 0;
+}
+
+static int probe_udf(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ struct volume_descriptor *vd;
+ struct volume_structure_descriptor *vsd;
+ struct logical_vol_integ_descriptor_imp_use *lvidiu;
+ uint32_t lvid_len = 0;
+ uint32_t lvid_loc = 0;
+ uint64_t s_off;
+ uint32_t bs;
+ uint32_t b;
+ uint16_t type;
+ uint32_t count;
+ uint32_t loc;
+ size_t i;
+ uint32_t vsd_len;
+ uint16_t udf_rev = 0;
+ int vsd_2048_valid = -1;
+ int have_label = 0;
+ int have_uuid = 0;
+ int have_logvolid = 0;
+ int have_volid = 0;
+ int have_volsetid = 0;
+ int have_applicationid = 0;
+ int have_publisherid = 0;
+
+ /* Session offset */
+ if (blkid_probe_get_hint(pr, "session_offset", &s_off) < 0)
+ s_off = 0;
+
+ /* The block size of a UDF filesystem is that of the underlying
+ * storage; we check later on for the special case of image files,
+ * which may have any block size valid for UDF filesystem */
+ uint32_t pbs[] = { 0, 512, 1024, 2048, 4096 };
+ pbs[0] = blkid_probe_get_sectorsize(pr);
+
+ for (i = 0; i < ARRAY_SIZE(pbs); i++) {
+ /* Do not try with block size same as sector size two times */
+ if (i != 0 && pbs[0] == pbs[i])
+ continue;
+
+ /* Do not try with block size which is not divisor of session offset */
+ if (s_off % pbs[i])
+ continue;
+
+ /* ECMA-167 2/8.4, 2/9.1: Each VSD is either 2048 bytes long or
+ * its size is same as blocksize (for blocksize > 2048 bytes)
+ * plus padded with zeros */
+ vsd_len = pbs[i] > 2048 ? pbs[i] : 2048;
+
+ /* Process 2048 bytes long VSD on first session only once
+ * as its location is same for any blocksize */
+ if (s_off == 0 && vsd_len == 2048) {
+ if (vsd_2048_valid == 0)
+ continue;
+ if (vsd_2048_valid == 1)
+ goto anchor;
+ }
+
+ /* Check for a Volume Structure Descriptor (VSD) */
+ for (b = 0; b < 64; b++) {
+ vsd = (struct volume_structure_descriptor *)
+ blkid_probe_get_buffer(pr,
+ s_off + UDF_VSD_OFFSET + b * vsd_len,
+ sizeof(*vsd));
+ if (!vsd)
+ return errno ? -errno : 1;
+ if (vsd->id[0] == '\0')
+ break;
+ if (memcmp(vsd->id, "NSR02", 5) == 0 ||
+ memcmp(vsd->id, "NSR03", 5) == 0)
+ goto anchor;
+ else if (memcmp(vsd->id, "BEA01", 5) != 0 &&
+ memcmp(vsd->id, "BOOT2", 5) != 0 &&
+ memcmp(vsd->id, "CD001", 5) != 0 &&
+ memcmp(vsd->id, "CDW02", 5) != 0 &&
+ memcmp(vsd->id, "TEA01", 5) != 0)
+ /* ECMA-167 2/8.3.1: The volume recognition sequence is
+ * terminated by the first sector which is not a valid
+ * descriptor.
+ * UDF-2.60 2.1.7: UDF 2.00 and lower revisions do not
+ * have requirement that NSR descriptor is in Extended Area
+ * (between BEA01 and TEA01) and that there is only one
+ * Extended Area. So do not stop scanning after TEA01. */
+ break;
+ }
+
+ if (s_off == 0 && vsd_len == 2048)
+ vsd_2048_valid = 0;
+
+ /* NSR was not found, try with next block size */
+ continue;
+
+anchor:
+ if (s_off == 0 && vsd_len == 2048)
+ vsd_2048_valid = 1;
+
+ /* Read Anchor Volume Descriptor (AVDP), detect block size */
+ vd = (struct volume_descriptor *)
+ blkid_probe_get_buffer(pr, s_off + 256 * pbs[i], sizeof(*vd));
+ if (!vd)
+ return errno ? -errno : 1;
+
+ /* Check that we read correct sector and detected correct block size */
+ if (le32_to_cpu(vd->tag.location) == s_off / pbs[i] + 256) {
+ type = le16_to_cpu(vd->tag.id);
+ if (type == TAG_ID_AVDP)
+ goto real_blksz;
+ }
+
+ /* UDF-2.60: 2.2.3: Unclosed sequential Write-Once media may
+ * have a single AVDP present at either sector 256 or 512. */
+ vd = (struct volume_descriptor *)
+ blkid_probe_get_buffer(pr, s_off + 512 * pbs[i], sizeof(*vd));
+ if (!vd)
+ return errno ? -errno : 1;
+
+ if (le32_to_cpu(vd->tag.location) == s_off / pbs[i] + 512) {
+ type = le16_to_cpu(vd->tag.id);
+ if (type == TAG_ID_AVDP)
+ goto real_blksz;
+ }
+
+ }
+ return 1;
+
+real_blksz:
+ /* Use the actual block size from here on out */
+ bs = pbs[i];
+
+ /* get descriptor 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 and read UDF identifiers */
+ for (b = 0; b < count; b++) {
+ vd = (struct volume_descriptor *)
+ blkid_probe_get_buffer(pr,
+ (uint64_t) (loc + b) * bs,
+ sizeof(*vd));
+ if (!vd)
+ return errno ? -errno : 1;
+ type = le16_to_cpu(vd->tag.id);
+ if (type == 0)
+ break;
+ if (le32_to_cpu(vd->tag.location) != loc + b)
+ break;
+ if (type == TAG_ID_TD)
+ break;
+ if (type == TAG_ID_PVD) {
+ if (!have_volid && is_charset_udf(vd->type.primary.desc_charset)) {
+ int enc = udf_cid_to_enc(vd->type.primary.ident.cid);
+ uint8_t clen = vd->type.primary.ident.clen;
+ if (clen > 0)
+ --clen;
+ if (clen > sizeof(vd->type.primary.ident.c))
+ clen = sizeof(vd->type.primary.ident.c);
+ if (enc != -1)
+ have_volid = !blkid_probe_set_utf8_id_label(pr, "VOLUME_ID",
+ vd->type.primary.ident.c, clen, enc);
+ }
+ if (!have_uuid && is_charset_udf(vd->type.primary.desc_charset)) {
+ /* VolumeSetIdentifier in UDF 2.01 specification:
+ * =================================================================================
+ * 2.2.2.5 dstring VolumeSetIdentifier
+ *
+ * Interpreted as specifying the identifier for the volume set.
+ *
+ * The first 16 characters of this field should be set to a unique value. The
+ * remainder of the field may be set to any allowed value. Specifically, software
+ * generating volumes conforming to this specification shall not set this field to a
+ * fixed or trivial value. Duplicate disks which are intended to be identical may
+ * contain the same value in this field.
+ *
+ * NOTE: The intended purpose of this is to guarantee Volume Sets with unique
+ * identifiers. The first 8 characters of the unique part should come from a CS0
+ * hexadecimal representation of a 32-bit time value. The remaining 8 characters
+ * are free for implementation use.
+ * =================================================================================
+ *
+ * Implementation in libblkid:
+ * The first 16 (Unicode) characters of VolumeSetIdentifier are encoded to UTF-8
+ * and then first 16 UTF-8 bytes are used to generate UUID. If all 16 bytes are
+ * hexadecimal digits then their lowercase variants are used as UUID. If one of
+ * the first 8 bytes (time value) is not hexadecimal digit then first 8 bytes are
+ * encoded to their hexadecimal representations, resulting in 16 characters and
+ * set as UUID. If all first 8 bytes (time value) are hexadecimal digits but some
+ * remaining not then lowercase variant of the first 8 bytes are used as first
+ * part of UUID and next 4 bytes encoded in hexadecimal representations (resulting
+ * in 8 characters) are used as second part of UUID string.
+ */
+ unsigned char uuid[17];
+ if (gen_uuid_from_volset_id(uuid, &vd->type.primary.volset_id) == 0)
+ have_uuid = !blkid_probe_strncpy_uuid(pr, uuid, sizeof(uuid));
+ }
+ if (!have_volsetid && is_charset_udf(vd->type.primary.desc_charset)) {
+ int enc = udf_cid_to_enc(vd->type.primary.volset_id.cid);
+ uint8_t clen = vd->type.primary.volset_id.clen;
+ if (clen > 0)
+ --clen;
+ if (clen > sizeof(vd->type.primary.volset_id.c))
+ clen = sizeof(vd->type.primary.volset_id.c);
+ if (enc != -1)
+ have_volsetid = !blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID",
+ vd->type.primary.volset_id.c, clen, enc);
+ }
+ if (!have_applicationid) {
+ /* UDF-2.60: 2.2.2.9: This field specifies a valid Entity Identifier identifying the application that last wrote this field */
+ const unsigned char *app_id = (const unsigned char *)vd->type.primary.app_id;
+ size_t app_id_len = strnlen(vd->type.primary.app_id, sizeof(vd->type.primary.app_id));
+ if (app_id_len > 0 && app_id[0] == '*') {
+ app_id++;
+ app_id_len--;
+ }
+ /* When Application Identifier is not set then use Developer ID from Implementation Identifier */
+ if (app_id_len == 0) {
+ /* UDF-2.60: 2.1.5.2: "*Developer ID" refers to an Entity Identifier that uniquely identifies the current implementation */
+ app_id = (const unsigned char *)vd->type.primary.imp_id;
+ app_id_len = strnlen(vd->type.primary.imp_id, sizeof(vd->type.primary.imp_id));
+ if (app_id_len > 0 && app_id[0] == '*') {
+ app_id++;
+ app_id_len--;
+ }
+ }
+ if (app_id_len > 0) {
+ /* UDF-2.60: 2.1.5.2: Values used by UDF for this field are specified in terms of ASCII character strings */
+ have_applicationid = !blkid_probe_set_id_label(pr, "APPLICATION_ID", app_id, app_id_len);
+ }
+ }
+ } else if (type == TAG_ID_LVD) {
+ if (!lvid_len || !lvid_loc) {
+ uint32_t num_partition_maps = le32_to_cpu(vd->type.logical.num_partition_maps);
+ /* ECMA-167 3/10.6.12: If num_partition_maps is 0, then no LVID is specified */
+ if (num_partition_maps) {
+ lvid_len = le32_to_cpu(vd->type.logical.lvid_length);
+ lvid_loc = le32_to_cpu(vd->type.logical.lvid_location);
+ }
+ }
+ if (!udf_rev) {
+ /* UDF-2.60: 2.1.5.3: UDF revision field shall indicate revision of UDF document
+ * We use maximal value from this field and from LVIDIU fields for ID_FS_VERSION */
+ if (strncmp(vd->type.logical.domain_id, "*OSTA UDF Compliant", sizeof(vd->type.logical.domain_id)) == 0)
+ udf_rev = le16_to_cpu(vd->type.logical.udf_rev);
+ }
+ if ((!have_logvolid || !have_label) && is_charset_udf(vd->type.logical.desc_charset)) {
+ /* LogicalVolumeIdentifier in UDF 2.01 specification:
+ * ===============================================================
+ * 2. Basic Restrictions & Requirements
+ *
+ * Logical Volume Descriptor
+ *
+ * There shall be exactly one prevailing Logical Volume
+ * Descriptor recorded per Volume Set.
+ *
+ * The LogicalVolumeIdentifier field shall not be null and
+ * should contain an identifier that aids in the identification of
+ * the logical volume. Specifically, software generating
+ * volumes conforming to this specification shall not set this
+ * field to a fixed or trivial value. Duplicate disks, which are
+ * intended to be identical, may contain the same value in this
+ * field. This field is extremely important in logical volume
+ * identification when multiple media are present within a
+ * jukebox. This name is typically what is displayed to the user.
+ * ===============================================================
+ *
+ * Implementation in libblkid:
+ * The LogicalVolumeIdentifier field is used for LABEL. MS Windows
+ * read Volume Label also from LogicalVolumeIdentifier. Grub2 read
+ * LABEL also from this field. Program newfs_udf (from UDFclient)
+ * when formatting disk set this field from user option Disc Name.
+ */
+ int enc = udf_cid_to_enc(vd->type.logical.logvol_id.cid);
+ uint8_t clen = vd->type.logical.logvol_id.clen;
+ if (clen > 0)
+ --clen;
+ if (clen > sizeof(vd->type.logical.logvol_id.c))
+ clen = sizeof(vd->type.logical.logvol_id.c);
+ if (enc != -1) {
+ if (!have_label)
+ have_label = !blkid_probe_set_utf8label(pr,
+ vd->type.logical.logvol_id.c, clen, enc);
+ if (!have_logvolid)
+ have_logvolid = !blkid_probe_set_utf8_id_label(pr, "LOGICAL_VOLUME_ID",
+ vd->type.logical.logvol_id.c, clen, enc);
+ }
+ }
+ } else if (type == TAG_ID_IUVD) {
+ if (!have_publisherid && strncmp(vd->type.imp_use_volume.lvi_id, "*UDF LV Info", sizeof(vd->type.imp_use_volume.lvi_id)) == 0 && is_charset_udf(vd->type.imp_use_volume.lvi_charset)) {
+ /* UDF-2.60: 2.2.7.2.3: Field LVInfo1 could contain information such as Owner Name
+ * More UDF generating tools set this field to person who creating the filesystem
+ * therefore its meaning is similar to ISO9660 Publisher Identifier. So for
+ * compatibility with iso9660 superblock code export this field via PUBLISHER_ID.
+ */
+ int enc = udf_cid_to_enc(vd->type.imp_use_volume.lvinfo1.cid);
+ uint8_t clen = vd->type.imp_use_volume.lvinfo1.clen;
+ if (clen > 0)
+ --clen;
+ if (clen > sizeof(vd->type.imp_use_volume.lvinfo1.c))
+ clen = sizeof(vd->type.imp_use_volume.lvinfo1.c);
+ if (enc != -1)
+ have_publisherid = !blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID",
+ vd->type.imp_use_volume.lvinfo1.c, clen, enc);
+ }
+ }
+ if (have_volid && have_uuid && have_volsetid && have_logvolid && have_label && lvid_len && lvid_loc && have_applicationid && have_publisherid)
+ break;
+ }
+
+ /* Pick the first logical volume integrity descriptor and read UDF revision */
+ if (lvid_loc && lvid_len >= sizeof(*vd)) {
+ vd = (struct volume_descriptor *)
+ blkid_probe_get_buffer(pr,
+ (uint64_t) lvid_loc * bs,
+ sizeof(*vd));
+ if (!vd)
+ return errno ? -errno : 1;
+ type = le16_to_cpu(vd->tag.id);
+ if (type == TAG_ID_LVID &&
+ le32_to_cpu(vd->tag.location) == lvid_loc &&
+ UDF_LVIDIU_LENGTH(*vd) >= sizeof(*lvidiu)) {
+ /* ECMA-167 3/8.8.2: There is stored sequence of LVIDs and valid is just last
+ * one. So correctly we should jump to next_lvid_location and read next LVID
+ * until we find last one. This could be time consuming process and could
+ * lead to scanning lot of disk blocks. Because we use LVID only for UDF
+ * version, in the worst case we would report only wrong ID_FS_VERSION. */
+ uint16_t lvidiu_udf_rev;
+ lvidiu = (struct logical_vol_integ_descriptor_imp_use *)
+ blkid_probe_get_buffer(pr,
+ (uint64_t) lvid_loc * bs + UDF_LVIDIU_OFFSET(*vd),
+ sizeof(*lvidiu));
+ if (!lvidiu)
+ return errno ? -errno : 1;
+ /* UDF-2.60: 2. Basic Restrictions & Requirements:
+ * The Minimum UDF Read Revision value shall be at most #0250
+ * for all media with a UDF 2.60 file system.
+ * Because some 2.60 implementations put 2.50 into both LVIDIU
+ * fields and 2.60 into LVD, use maximal value from LVD,
+ * Minimum UDF Read Revision and Minimum UDF Write Revision for
+ * ID_FS_VERSION to distinguish between UDF 2.50 and UDF 2.60 discs. */
+ lvidiu_udf_rev = le16_to_cpu(lvidiu->min_udf_read_rev);
+ if (lvidiu_udf_rev && udf_rev < lvidiu_udf_rev)
+ udf_rev = lvidiu_udf_rev;
+ lvidiu_udf_rev = le16_to_cpu(lvidiu->min_udf_write_rev);
+ if (lvidiu_udf_rev && udf_rev < lvidiu_udf_rev)
+ udf_rev = lvidiu_udf_rev;
+ }
+ }
+
+ if (udf_rev)
+ /* UDF revision is stored as decimal number in hexadecimal format.
+ * E.g. number 0x0150 is revision 1.50, number 0x0201 is revision 2.01. */
+ blkid_probe_sprintf_version(pr, "%x.%02x", (unsigned int)(udf_rev >> 8), (unsigned int)(udf_rev & 0xFF));
+
+ blkid_probe_set_fsblocksize(pr, bs);
+ blkid_probe_set_block_size(pr, bs);
+
+ 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, .hoff = "session_offset" },
+ { .magic = "BOOT2", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
+ { .magic = "CD001", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
+ { .magic = "CDW02", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
+ { .magic = "NSR02", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
+ { .magic = "NSR03", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
+ { .magic = "TEA01", .len = 5, .kboff = 32, .sboff = 1, .hoff = "session_offset" },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/ufs.c b/libblkid/src/superblocks/ufs.c
new file mode 100644
index 0000000..0c8d4ed
--- /dev/null
+++ b/libblkid/src/superblocks/ufs.c
@@ -0,0 +1,269 @@
+/*
+ * 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 errno ? -errno : 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;
+
+ uint32_t bsize = 0;
+ if (!is_be)
+ bsize = le32_to_cpu(ufs->fs_fsize);
+ else
+ bsize = be32_to_cpu(ufs->fs_fsize);
+
+ blkid_probe_set_fsblocksize(pr, bsize);
+ blkid_probe_set_block_size(pr, bsize);
+ blkid_probe_set_fsendianness(pr, is_be ?
+ BLKID_ENDIANNESS_BIG : BLKID_ENDIANNESS_LITTLE);
+
+ 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/libblkid/src/superblocks/vdo.c b/libblkid/src/superblocks/vdo.c
new file mode 100644
index 0000000..bec686f
--- /dev/null
+++ b/libblkid/src/superblocks/vdo.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * 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 vdo_super_block {
+ char magic[8]; /* magic number 'dmvdo001'*/
+ char unused[32]; /* 32 bytes of unimportant space */
+ unsigned char sb_uuid[16]; /* vdo unique id */
+
+ /* this is not all... but enough for libblkid */
+} __attribute__((packed));
+
+static int probe_vdo(blkid_probe pr, const struct blkid_idmag *mag)
+{
+ struct vdo_super_block *vsb;
+
+ vsb = blkid_probe_get_sb(pr, mag, struct vdo_super_block);
+ if (!vsb)
+ return errno ? -errno : 1;
+
+ blkid_probe_set_uuid(pr, vsb->sb_uuid);
+ return 0;
+}
+
+const struct blkid_idinfo vdo_idinfo =
+{
+ .name = "vdo",
+ .usage = BLKID_USAGE_OTHER,
+ .probefunc = probe_vdo,
+ .magics =
+ {
+ { .magic = "dmvdo001", .len = 8 },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/superblocks/vfat.c b/libblkid/src/superblocks/vfat.c
new file mode 100644
index 0000000..6b4a3ad
--- /dev/null
+++ b/libblkid/src/superblocks/vfat.c
@@ -0,0 +1,462 @@
+/*
+ * 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 <inttypes.h>
+
+#include "pt-mbr.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_drive_number;
+/* 41*/ unsigned char vs_boot_flags;
+/* 42*/ unsigned char vs_ext_boot_sign; /* 0x28 - without vs_label/vs_magic; 0x29 - with */
+/* 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 {
+/* DOS 2.0 BPB */
+/* 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 */
+/* DOS 3.0 BPB */
+/* 18*/ uint16_t ms_secs_track;
+/* 1a*/ uint16_t ms_heads;
+/* 1c*/ uint32_t ms_hidden;
+/* DOS 3.31 BPB */
+/* 20*/ uint32_t ms_total_sect; /* iff ms_sectors == 0 */
+/* DOS 3.4 EBPB */
+/* 24*/ unsigned char ms_drive_number;
+/* 25*/ unsigned char ms_boot_flags;
+/* 26*/ unsigned char ms_ext_boot_sign; /* 0x28 - DOS 3.4 EBPB; 0x29 - DOS 4.0 EBPB */
+/* 27*/ unsigned char ms_serno[4];
+/* DOS 4.0 EBPB */
+/* 2b*/ unsigned char ms_label[11];
+/* 36*/ unsigned char ms_magic[8];
+/* padding */
+/* 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 int search_fat_label(blkid_probe pr, uint64_t offset, uint32_t entries, unsigned char out[11])
+{
+ const struct vfat_dir_entry *ent, *dir = NULL;
+ uint32_t i;
+
+ DBG(LOWPROBE, ul_debug("\tlook for label in root-dir "
+ "(entries: %"PRIu32", offset: %"PRIu64")", 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,
+ (uint64_t) entries *
+ sizeof(struct vfat_dir_entry));
+ if (!dir)
+ return 0;
+ }
+
+ 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,
+ (uint64_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(LOWPROBE, ul_debug("\tfound fs LABEL at entry %d", i));
+ memcpy(out, ent->name, 11);
+ if (out[0] == 0x05)
+ out[0] = 0xE5;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int fat_valid_superblock(blkid_probe pr,
+ const struct blkid_idmag *mag,
+ struct msdos_super_block *ms,
+ struct vfat_super_block *vs,
+ uint32_t *cluster_count, uint32_t *fat_size,
+ uint32_t *sect_count)
+{
+ 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)) {
+ DBG(LOWPROBE, ul_debug("\tJFS/HPFS detected"));
+ 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;
+ if (sect_count)
+ *sect_count = __sect_count;
+
+ if (blkid_probe_is_bitlocker(pr))
+ return 0;
+
+ return 1; /* valid */
+}
+
+/* function prototype to avoid warnings (duplicate in partitions/dos.c) */
+extern int blkid_probe_is_vfat(blkid_probe pr);
+
+/*
+ * 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;
+ int rc;
+
+ rc = blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag);
+ if (rc < 0)
+ return rc; /* error */
+ if (rc != BLKID_PROBE_OK || !mag)
+ return 0;
+
+ ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
+ if (!ms)
+ return errno ? -errno : 0;
+ vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
+ if (!vs)
+ return errno ? -errno : 0;
+
+ return fat_valid_superblock(pr, mag, ms, vs, NULL, 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 = NULL;
+ const unsigned char *boot_label = NULL;
+ unsigned char *vol_serno = NULL, vol_label_buf[11];
+ uint16_t sector_size = 0, reserved;
+ uint32_t cluster_count, fat_size, sect_count;
+ const char *version = NULL;
+
+ ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
+ if (!ms)
+ return errno ? -errno : 1;
+
+ vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
+ if (!vs)
+ return errno ? -errno : 1;
+
+ if (!fat_valid_superblock(pr, mag, ms, vs, &cluster_count, &fat_size,
+ &sect_count))
+ 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);
+
+ if (search_fat_label(pr, root_start, root_dir_entries, vol_label_buf))
+ vol_label = vol_label_buf;
+
+ if (ms->ms_ext_boot_sign == 0x29)
+ boot_label = ms->ms_label;
+
+ if (ms->ms_ext_boot_sign == 0x28 || ms->ms_ext_boot_sign == 0x29)
+ 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 = ((uint64_t) 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);
+
+ if (search_fat_label(pr, next_off, count, vol_label_buf)) {
+ 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 (vs->vs_ext_boot_sign == 0x29)
+ boot_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,
+ (uint64_t) fsinfo_sect * sector_size,
+ sizeof(struct fat32_fsinfo));
+ if (buf == NULL)
+ return errno ? -errno : 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 (boot_label && memcmp(boot_label, no_name, 11) != 0)
+ blkid_probe_set_id_label(pr, "LABEL_FATBOOT", boot_label, 11);
+
+ if (vol_label)
+ blkid_probe_set_label(pr, 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);
+
+ blkid_probe_set_fsblocksize(pr, vs->vs_cluster_size * sector_size);
+ blkid_probe_set_block_size(pr, sector_size);
+ blkid_probe_set_fssize(pr, (uint64_t) sector_size * sect_count);
+
+ 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/libblkid/src/superblocks/via_raid.c b/libblkid/src/superblocks/via_raid.c
new file mode 100644
index 0000000..ee3ab65
--- /dev/null
+++ b/libblkid/src/superblocks/via_raid.c
@@ -0,0 +1,91 @@
+/*
+ * 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;
+}
+
+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 errno ? -errno : 1;
+
+ if (le16_to_cpu(v->signature) != VIA_SIGNATURE)
+ return 1;
+ if (v->version_number > 2)
+ return 1;
+ if (!blkid_probe_verify_csum(pr, via_checksum(v), v->checksum))
+ 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/libblkid/src/superblocks/vmfs.c b/libblkid/src/superblocks/vmfs.c
new file mode 100644
index 0000000..fac87ab
--- /dev/null
+++ b/libblkid/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 errno ? -errno : 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 errno ? -errno : 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/libblkid/src/superblocks/vxfs.c b/libblkid/src/superblocks/vxfs.c
new file mode 100644
index 0000000..be66831
--- /dev/null
+++ b/libblkid/src/superblocks/vxfs.c
@@ -0,0 +1,61 @@
+/*
+ * 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;
+ uint32_t vs_ctime;
+ uint32_t vs_cutime;
+ uint32_t __unused1;
+ uint32_t __unused2;
+ uint32_t vs_old_logstart;
+ uint32_t vs_old_logend;
+ uint32_t vs_bsize;
+ uint32_t vs_size;
+ uint32_t vs_dsize;
+};
+
+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 errno ? -errno : 1;
+
+ if (le32_to_cpu(vxs->vs_magic) == 0xa501fcf5) {
+ blkid_probe_sprintf_version(pr, "%u", (unsigned int)le32_to_cpu(vxs->vs_version));
+ blkid_probe_set_fsblocksize(pr, le32_to_cpu(vxs->vs_bsize));
+ blkid_probe_set_block_size(pr, le32_to_cpu(vxs->vs_bsize));
+ blkid_probe_set_fsendianness(pr, BLKID_ENDIANNESS_LITTLE);
+ } else if (be32_to_cpu(vxs->vs_magic) == 0xa501fcf5) {
+ blkid_probe_sprintf_version(pr, "%u", (unsigned int)be32_to_cpu(vxs->vs_version));
+ blkid_probe_set_fsblocksize(pr, be32_to_cpu(vxs->vs_bsize));
+ blkid_probe_set_block_size(pr, be32_to_cpu(vxs->vs_bsize));
+ blkid_probe_set_fsendianness(pr, BLKID_ENDIANNESS_BIG);
+ }
+ 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 },
+ { .magic = "\245\001\374\365", .len = 4, .kboff = 8 },
+ { NULL }
+ }
+};
+
diff --git a/libblkid/src/superblocks/xfs.c b/libblkid/src/superblocks/xfs.c
new file mode 100644
index 0000000..f0e099e
--- /dev/null
+++ b/libblkid/src/superblocks/xfs.c
@@ -0,0 +1,370 @@
+/*
+ * 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) 2013 Eric Sandeen <sandeen@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"
+#include "crc32c.h"
+
+struct xfs_super_block {
+ uint32_t sb_magicnum; /* magic number == XFS_SB_MAGIC */
+ uint32_t sb_blocksize; /* logical block size, bytes */
+ uint64_t sb_dblocks; /* number of data blocks */
+ uint64_t sb_rblocks; /* number of realtime blocks */
+ uint64_t sb_rextents; /* number of realtime extents */
+ unsigned char sb_uuid[16]; /* file system unique id */
+ uint64_t sb_logstart; /* starting block of log if internal */
+ uint64_t sb_rootino; /* root inode number */
+ uint64_t sb_rbmino; /* bitmap inode for realtime extents */
+ uint64_t sb_rsumino; /* summary inode for rt bitmap */
+ uint32_t sb_rextsize; /* realtime extent size, blocks */
+ uint32_t sb_agblocks; /* size of an allocation group */
+ uint32_t sb_agcount; /* number of allocation groups */
+ uint32_t sb_rbmblocks; /* number of rt bitmap blocks */
+ uint32_t sb_logblocks; /* number of log blocks */
+
+ uint16_t sb_versionnum; /* header version == XFS_SB_VERSION */
+ uint16_t sb_sectsize; /* volume sector size, bytes */
+ uint16_t sb_inodesize; /* inode size, bytes */
+ uint16_t sb_inopblock; /* inodes per block */
+ char sb_fname[12]; /* file system name */
+ uint8_t sb_blocklog; /* log2 of sb_blocksize */
+ uint8_t sb_sectlog; /* log2 of sb_sectsize */
+ uint8_t sb_inodelog; /* log2 of sb_inodesize */
+ uint8_t sb_inopblog; /* log2 of sb_inopblock */
+ uint8_t sb_agblklog; /* log2 of sb_agblocks (rounded up) */
+ uint8_t sb_rextslog; /* log2 of sb_rextents */
+ uint8_t sb_inprogress; /* mkfs is in progress, don't mount */
+ uint8_t sb_imax_pct; /* max % of fs for inode space */
+ /* statistics */
+ uint64_t sb_icount; /* allocated inodes */
+ uint64_t sb_ifree; /* free inodes */
+ uint64_t sb_fdblocks; /* free data blocks */
+ uint64_t sb_frextents; /* free realtime extents */
+ uint64_t sb_uquotino; /* inode for user quotas */
+ uint64_t sb_gquotino; /* inode for group or project quotas */
+ uint16_t sb_qflags; /* quota flags */
+ uint8_t sb_flags; /* misc flags */
+ uint8_t sb_shared_vn; /* reserved, zeroed */
+ uint32_t sb_inoalignmt; /* inode alignment */
+ uint32_t sb_unit; /* stripe or raid unit */
+ uint32_t sb_width; /* stripe or raid width */
+ uint8_t sb_dirblklog; /* directory block allocation granularity */
+ uint8_t sb_logsectlog; /* log sector sector size */
+ uint16_t sb_logsectsize; /* log sector size */
+ uint32_t sb_logsunit; /* log device stripe or raid unit */
+ uint32_t sb_features2; /* additional version flags */
+ uint32_t sb_bad_features2; /* mirror of sb_features2 */
+
+ /* version 5 fields */
+ uint32_t sb_features_compat; /* rw compatible flags */
+ uint32_t sb_features_ro_compat; /* ro compatible flags */
+ uint32_t sb_features_incompat; /* rw incompatible flags */
+ uint32_t sb_features_log_incompat; /* rw log incompatible flags */
+ uint32_t sb_crc; /* superblock checksum */
+ uint32_t sb_spino_align; /* sparse inode alignment */
+ uint64_t sb_pquotino; /* project quote inode */
+ uint64_t sb_lsn; /* superblock update sequence number */
+ unsigned char sb_meta_uuid[16]; /* superblock meta uuid */
+ uint64_t sb_rrmapino; /* realtime reversemapping inode */
+} __attribute__((packed));
+
+#define XFS_MIN_BLOCKSIZE_LOG 9 /* i.e. 512 bytes */
+#define XFS_MAX_BLOCKSIZE_LOG 16 /* i.e. 65536 bytes */
+#define XFS_MIN_BLOCKSIZE (1 << XFS_MIN_BLOCKSIZE_LOG)
+#define XFS_MAX_BLOCKSIZE (1 << XFS_MAX_BLOCKSIZE_LOG)
+#define XFS_MIN_SECTORSIZE_LOG 9 /* i.e. 512 bytes */
+#define XFS_MAX_SECTORSIZE_LOG 15 /* i.e. 32768 bytes */
+#define XFS_MIN_SECTORSIZE (1 << XFS_MIN_SECTORSIZE_LOG)
+#define XFS_MAX_SECTORSIZE (1 << XFS_MAX_SECTORSIZE_LOG)
+
+#define XFS_DINODE_MIN_LOG 8
+#define XFS_DINODE_MAX_LOG 11
+#define XFS_DINODE_MIN_SIZE (1 << XFS_DINODE_MIN_LOG)
+#define XFS_DINODE_MAX_SIZE (1 << XFS_DINODE_MAX_LOG)
+
+#define XFS_MAX_RTEXTSIZE (1024 * 1024 * 1024) /* 1GB */
+#define XFS_DFL_RTEXTSIZE (64 * 1024) /* 64kB */
+#define XFS_MIN_RTEXTSIZE (4 * 1024) /* 4kB */
+
+#define XFS_MIN_AG_BLOCKS 64
+#define XFS_MAX_DBLOCKS(s) ((uint64_t)(s)->sb_agcount * (s)->sb_agblocks)
+#define XFS_MIN_DBLOCKS(s) ((uint64_t)((s)->sb_agcount - 1) * \
+ (s)->sb_agblocks + XFS_MIN_AG_BLOCKS)
+
+#define XFS_SB_VERSION_MOREBITSBIT 0x8000
+#define XFS_SB_VERSION2_CRCBIT 0x00000100
+
+
+static void sb_from_disk(struct xfs_super_block *from,
+ struct xfs_super_block *to)
+{
+
+ to->sb_magicnum = be32_to_cpu(from->sb_magicnum);
+ to->sb_blocksize = be32_to_cpu(from->sb_blocksize);
+ to->sb_dblocks = be64_to_cpu(from->sb_dblocks);
+ to->sb_rblocks = be64_to_cpu(from->sb_rblocks);
+ to->sb_rextents = be64_to_cpu(from->sb_rextents);
+ to->sb_logstart = be64_to_cpu(from->sb_logstart);
+ to->sb_rootino = be64_to_cpu(from->sb_rootino);
+ to->sb_rbmino = be64_to_cpu(from->sb_rbmino);
+ to->sb_rsumino = be64_to_cpu(from->sb_rsumino);
+ to->sb_rextsize = be32_to_cpu(from->sb_rextsize);
+ to->sb_agblocks = be32_to_cpu(from->sb_agblocks);
+ to->sb_agcount = be32_to_cpu(from->sb_agcount);
+ to->sb_rbmblocks = be32_to_cpu(from->sb_rbmblocks);
+ to->sb_logblocks = be32_to_cpu(from->sb_logblocks);
+ to->sb_versionnum = be16_to_cpu(from->sb_versionnum);
+ to->sb_sectsize = be16_to_cpu(from->sb_sectsize);
+ to->sb_inodesize = be16_to_cpu(from->sb_inodesize);
+ to->sb_inopblock = be16_to_cpu(from->sb_inopblock);
+ to->sb_blocklog = from->sb_blocklog;
+ to->sb_sectlog = from->sb_sectlog;
+ to->sb_inodelog = from->sb_inodelog;
+ to->sb_inopblog = from->sb_inopblog;
+ to->sb_agblklog = from->sb_agblklog;
+ to->sb_rextslog = from->sb_rextslog;
+ to->sb_inprogress = from->sb_inprogress;
+ to->sb_imax_pct = from->sb_imax_pct;
+ to->sb_icount = be64_to_cpu(from->sb_icount);
+ to->sb_ifree = be64_to_cpu(from->sb_ifree);
+ to->sb_fdblocks = be64_to_cpu(from->sb_fdblocks);
+ to->sb_frextents = be64_to_cpu(from->sb_frextents);
+ to->sb_uquotino = be64_to_cpu(from->sb_uquotino);
+ to->sb_gquotino = be64_to_cpu(from->sb_gquotino);
+ to->sb_qflags = be16_to_cpu(from->sb_qflags);
+ to->sb_flags = from-> sb_flags;
+ to->sb_shared_vn = from-> sb_shared_vn;
+ to->sb_inoalignmt = be32_to_cpu(from->sb_inoalignmt);
+ to->sb_unit = be32_to_cpu(from->sb_unit);
+ to->sb_width = be32_to_cpu(from->sb_width);
+ to->sb_dirblklog = from-> sb_dirblklog;
+ to->sb_logsectlog = from-> sb_logsectlog;
+ to->sb_logsectsize = be16_to_cpu(from->sb_logsectsize);
+ to->sb_logsunit = be32_to_cpu(from->sb_logsunit);
+ to->sb_features2 = be32_to_cpu(from->sb_features2);
+ to->sb_bad_features2 = be32_to_cpu(from->sb_bad_features2);
+ to->sb_features_compat = be32_to_cpu(from->sb_features_compat);
+ to->sb_features_ro_compat = be32_to_cpu(from->sb_features_ro_compat);
+ to->sb_features_incompat = be32_to_cpu(from->sb_features_incompat);
+ to->sb_features_log_incompat = be32_to_cpu(from->sb_features_log_incompat);
+ to->sb_crc = be32_to_cpu(from->sb_crc);
+ to->sb_spino_align = be32_to_cpu(from->sb_spino_align);
+ to->sb_pquotino = be64_to_cpu(from->sb_pquotino);
+ to->sb_lsn = be64_to_cpu(from->sb_lsn);
+ to->sb_rrmapino = be64_to_cpu(from->sb_rrmapino);
+}
+
+static int xfs_verify_sb(struct xfs_super_block *ondisk, blkid_probe pr,
+ const struct blkid_idmag *mag)
+{
+ struct xfs_super_block sb, *sbp = &sb;
+
+ /* beXX_to_cpu(), but don't convert UUID and fsname! */
+ sb_from_disk(ondisk, sbp);
+
+ /* sanity checks, we don't want to rely on magic string only */
+ if (sbp->sb_agcount <= 0 ||
+ sbp->sb_sectsize < XFS_MIN_SECTORSIZE ||
+ sbp->sb_sectsize > XFS_MAX_SECTORSIZE ||
+ sbp->sb_sectlog < XFS_MIN_SECTORSIZE_LOG ||
+ sbp->sb_sectlog > XFS_MAX_SECTORSIZE_LOG ||
+ sbp->sb_sectsize != (1 << sbp->sb_sectlog) ||
+ sbp->sb_blocksize < XFS_MIN_BLOCKSIZE ||
+ sbp->sb_blocksize > XFS_MAX_BLOCKSIZE ||
+ sbp->sb_blocklog < XFS_MIN_BLOCKSIZE_LOG ||
+ sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG ||
+ sbp->sb_blocksize != (1ULL << sbp->sb_blocklog) ||
+ sbp->sb_inodesize < XFS_DINODE_MIN_SIZE ||
+ sbp->sb_inodesize > XFS_DINODE_MAX_SIZE ||
+ sbp->sb_inodelog < XFS_DINODE_MIN_LOG ||
+ sbp->sb_inodelog > XFS_DINODE_MAX_LOG ||
+ sbp->sb_inodesize != (1 << sbp->sb_inodelog) ||
+ (sbp->sb_blocklog - sbp->sb_inodelog != sbp->sb_inopblog) ||
+ (sbp->sb_rextsize * sbp->sb_blocksize > XFS_MAX_RTEXTSIZE) ||
+ (sbp->sb_rextsize * sbp->sb_blocksize < XFS_MIN_RTEXTSIZE) ||
+ (sbp->sb_imax_pct > 100 /* zero sb_imax_pct is valid */) ||
+ sbp->sb_dblocks == 0 ||
+ sbp->sb_dblocks > XFS_MAX_DBLOCKS(sbp) ||
+ sbp->sb_dblocks < XFS_MIN_DBLOCKS(sbp))
+ return 0;
+
+ if ((sbp->sb_versionnum & 0x0f) == 5) {
+ uint32_t expected, crc;
+ unsigned char *csummed;
+
+ if (!(sbp->sb_versionnum & XFS_SB_VERSION_MOREBITSBIT))
+ return 0;
+ if (!(sbp->sb_features2 & XFS_SB_VERSION2_CRCBIT))
+ return 0;
+
+ expected = sbp->sb_crc;
+ csummed = blkid_probe_get_sb_buffer(pr, mag, sbp->sb_sectsize);
+ if (!csummed)
+ return 0;
+
+ crc = ul_crc32c_exclude_offset(~0LL, csummed, sbp->sb_sectsize,
+ offsetof(struct xfs_super_block, sb_crc),
+ sizeof_member(struct xfs_super_block, sb_crc));
+ crc = bswap_32(crc ^ ~0LL);
+
+ if (!blkid_probe_verify_csum(pr, crc, expected))
+ return 0;
+ }
+
+ return 1;
+}
+
+static uint64_t xfs_fssize(struct xfs_super_block *xs)
+{
+ uint32_t lsize = xs->sb_logstart ? xs->sb_logblocks : 0;
+ uint64_t avail_blocks = be64_to_cpu(xs->sb_dblocks) - be32_to_cpu(lsize);
+ uint64_t fssize = avail_blocks*be32_to_cpu(xs->sb_blocksize);
+
+ return fssize;
+}
+
+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 errno ? -errno : 1;
+
+ if (!xfs_verify_sb(xs, pr, mag))
+ return 1;
+
+ if (*xs->sb_fname != '\0')
+ blkid_probe_set_label(pr, (unsigned char *) xs->sb_fname,
+ sizeof(xs->sb_fname));
+ blkid_probe_set_uuid(pr, xs->sb_uuid);
+ blkid_probe_set_fssize(pr, xfs_fssize(xs));
+ blkid_probe_set_fslastblock(pr, be64_to_cpu(xs->sb_dblocks));
+ blkid_probe_set_fsblocksize(pr, be32_to_cpu(xs->sb_blocksize));
+ blkid_probe_set_block_size(pr, be16_to_cpu(xs->sb_sectsize));
+ return 0;
+}
+
+const struct blkid_idinfo xfs_idinfo =
+{
+ .name = "xfs",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_xfs,
+ .magics =
+ {
+ { .magic = "XFSB", .len = 4 },
+ { NULL }
+ }
+};
+
+struct xlog_rec_header {
+ uint32_t h_magicno;
+ uint32_t h_dummy1[1];
+ uint32_t h_version;
+ uint32_t h_len;
+ uint32_t h_dummy2[71];
+ uint32_t h_fmt;
+ unsigned char h_uuid[16];
+} __attribute__((packed));
+
+#define XLOG_HEADER_MAGIC_NUM 0xFEEDbabe
+
+/*
+ * For very small filesystems, the minimum log size
+ * can be smaller, but that seems vanishingly unlikely
+ * when used with an external log (which is used for
+ * performance reasons; tiny conflicts with that goal).
+ */
+#define XFS_MIN_LOG_BYTES (10 * 1024 * 1024)
+
+#define XLOG_FMT_LINUX_LE 1
+#define XLOG_FMT_LINUX_BE 2
+#define XLOG_FMT_IRIX_BE 3
+
+#define XLOG_VERSION_1 1
+#define XLOG_VERSION_2 2 /* Large IClogs, Log sunit */
+#define XLOG_VERSION_OKBITS (XLOG_VERSION_1 | XLOG_VERSION_2)
+
+static int xlog_valid_rec_header(struct xlog_rec_header *rhead)
+{
+ uint32_t hlen;
+
+ if (rhead->h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM))
+ return 0;
+
+ if (!rhead->h_version ||
+ (be32_to_cpu(rhead->h_version) & (~XLOG_VERSION_OKBITS)))
+ return 0;
+
+ /* LR body must have data or it wouldn't have been written */
+ hlen = be32_to_cpu(rhead->h_len);
+ if (hlen <= 0 || hlen > INT_MAX)
+ return 0;
+
+ if (rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_LE) &&
+ rhead->h_fmt != cpu_to_be32(XLOG_FMT_LINUX_BE) &&
+ rhead->h_fmt != cpu_to_be32(XLOG_FMT_IRIX_BE))
+ return 0;
+
+ return 1;
+}
+
+/* xlog record header will be in some sector in the first 256k */
+static int probe_xfs_log(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ int i;
+ struct xlog_rec_header *rhead;
+ unsigned char *buf;
+
+ buf = blkid_probe_get_buffer(pr, 0, 256*1024);
+ if (!buf)
+ return errno ? -errno : 1;
+
+ /* check the first 512 512-byte sectors */
+ for (i = 0; i < 512; i++) {
+ /* this is regular XFS (maybe with some sectors shift), ignore */
+ if (memcmp(&buf[i*512], "XFSB", 4) == 0)
+ return 1;
+
+ rhead = (struct xlog_rec_header *)&buf[i*512];
+
+ if (xlog_valid_rec_header(rhead)) {
+ blkid_probe_set_uuid_as(pr, rhead->h_uuid, "LOGUUID");
+
+ if (blkid_probe_set_magic(pr, i * 512,
+ sizeof(rhead->h_magicno),
+ (unsigned char *) &rhead->h_magicno))
+ return 1;
+
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+const struct blkid_idinfo xfs_log_idinfo =
+{
+ .name = "xfs_external_log",
+ .usage = BLKID_USAGE_OTHER,
+ .probefunc = probe_xfs_log,
+ .magics = BLKID_NONE_MAGIC,
+ .minsz = XFS_MIN_LOG_BYTES,
+};
diff --git a/libblkid/src/superblocks/zfs.c b/libblkid/src/superblocks/zfs.c
new file mode 100644
index 0000000..1fcc384
--- /dev/null
+++ b/libblkid/src/superblocks/zfs.c
@@ -0,0 +1,333 @@
+/*
+ * 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 <limits.h>
+
+#include "superblocks.h"
+
+#define VDEV_LABEL_UBERBLOCK (128 * 1024ULL)
+#define VDEV_LABEL_NVPAIR ( 16 * 1024ULL)
+#define VDEV_LABEL_SIZE (256 * 1024ULL)
+#define UBERBLOCK_SIZE 1024ULL
+#define UBERBLOCKS_COUNT 128
+
+/* #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_WANT 4
+
+#define DATA_TYPE_UINT64 8
+#define DATA_TYPE_STRING 9
+#define DATA_TYPE_DIRECTORY 19
+
+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;
+} __attribute__((packed));
+
+struct nvdirectory {
+ uint32_t nvd_type;
+ uint32_t nvd_unknown[3];
+};
+
+struct nvlist {
+ uint32_t nvl_unknown[3];
+ struct nvpair nvl_nvpair;
+};
+
+static void zfs_process_value(blkid_probe pr, char *name, size_t namelen,
+ void *value, size_t max_value_size, unsigned directory_level)
+{
+ if (strncmp(name, "name", namelen) == 0 &&
+ sizeof(struct nvstring) <= max_value_size &&
+ !directory_level) {
+ struct nvstring *nvs = value;
+ uint32_t nvs_type = be32_to_cpu(nvs->nvs_type);
+ uint32_t nvs_strlen = be32_to_cpu(nvs->nvs_strlen);
+
+ if (nvs_type != DATA_TYPE_STRING ||
+ (uint64_t)nvs_strlen + sizeof(*nvs) > max_value_size)
+ return;
+
+ DBG(LOWPROBE, ul_debug("nvstring: type %u string %*s",
+ nvs_type, nvs_strlen, nvs->nvs_string));
+
+ blkid_probe_set_label(pr, nvs->nvs_string, nvs_strlen);
+ } else if (strncmp(name, "guid", namelen) == 0 &&
+ sizeof(struct nvuint64) <= max_value_size &&
+ !directory_level) {
+ struct nvuint64 *nvu = value;
+ uint32_t nvu_type = be32_to_cpu(nvu->nvu_type);
+ uint64_t nvu_value;
+
+ memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
+ nvu_value = be64_to_cpu(nvu_value);
+
+ if (nvu_type != DATA_TYPE_UINT64)
+ return;
+
+ DBG(LOWPROBE, ul_debug("nvuint64: type %u value %"PRIu64,
+ nvu_type, nvu_value));
+
+ blkid_probe_sprintf_value(pr, "UUID_SUB",
+ "%"PRIu64, nvu_value);
+ } else if (strncmp(name, "pool_guid", namelen) == 0 &&
+ sizeof(struct nvuint64) <= max_value_size &&
+ !directory_level) {
+ struct nvuint64 *nvu = value;
+ uint32_t nvu_type = be32_to_cpu(nvu->nvu_type);
+ uint64_t nvu_value;
+
+ memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
+ nvu_value = be64_to_cpu(nvu_value);
+
+ if (nvu_type != DATA_TYPE_UINT64)
+ return;
+
+ DBG(LOWPROBE, ul_debug("nvuint64: type %u value %"PRIu64,
+ nvu_type, nvu_value));
+
+ blkid_probe_sprintf_uuid(pr, (unsigned char *) &nvu_value,
+ sizeof(nvu_value),
+ "%"PRIu64, nvu_value);
+ } else if (strncmp(name, "ashift", namelen) == 0 &&
+ sizeof(struct nvuint64) <= max_value_size) {
+ struct nvuint64 *nvu = value;
+ uint32_t nvu_type = be32_to_cpu(nvu->nvu_type);
+ uint64_t nvu_value;
+
+ memcpy(&nvu_value, &nvu->nvu_value, sizeof(nvu_value));
+ nvu_value = be64_to_cpu(nvu_value);
+
+ if (nvu_type != DATA_TYPE_UINT64)
+ return;
+
+ if (nvu_value < 32){
+ blkid_probe_set_fsblocksize(pr, 1U << nvu_value);
+ blkid_probe_set_block_size(pr, 1U << nvu_value);
+ }
+ }
+}
+
+static void zfs_extract_guid_name(blkid_probe pr, loff_t offset)
+{
+ unsigned char *p;
+ struct nvlist *nvl;
+ struct nvpair *nvp;
+ size_t left = 4096;
+ unsigned directory_level = 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. */
+ p = blkid_probe_get_buffer(pr, offset, left);
+ if (!p)
+ return;
+
+ DBG(LOWPROBE, ul_debug("zfs_extract: nvlist offset %jd",
+ (intmax_t)offset));
+
+ nvl = (struct nvlist *) p;
+ nvp = &nvl->nvl_nvpair;
+ left -= (unsigned char *)nvp - p; /* Already used up 12 bytes */
+
+ while (left > sizeof(*nvp)) {
+ uint32_t nvp_size = be32_to_cpu(nvp->nvp_size);
+ uint32_t nvp_namelen = be32_to_cpu(nvp->nvp_namelen);
+ uint64_t namesize = ((uint64_t)nvp_namelen + 3) & ~3;
+ size_t max_value_size;
+ void *value;
+
+ if (!nvp->nvp_size) {
+ if (!directory_level)
+ break;
+ directory_level--;
+ nvp_size = 8;
+ goto cont;
+ }
+
+ DBG(LOWPROBE, ul_debug("left %zd nvp_size %u",
+ left, nvp_size));
+
+ /* nvpair fits in buffer and name fits in nvpair? */
+ if (nvp_size > left || namesize + sizeof(*nvp) > nvp_size)
+ break;
+
+ DBG(LOWPROBE,
+ ul_debug("nvlist: size %u, namelen %u, name %*s",
+ nvp_size, nvp_namelen, nvp_namelen,
+ nvp->nvp_name));
+
+ max_value_size = nvp_size - (namesize + sizeof(*nvp));
+ value = nvp->nvp_name + namesize;
+
+ if (sizeof(struct nvdirectory) <= max_value_size) {
+ struct nvdirectory *nvu = value;
+ if (be32_to_cpu(nvu->nvd_type) == DATA_TYPE_DIRECTORY) {
+ nvp_size = sizeof(*nvp) + namesize + sizeof(*nvu);
+ directory_level++;
+ goto cont;
+ }
+ }
+
+ zfs_process_value(pr, nvp->nvp_name, nvp_namelen,
+ value, max_value_size, directory_level);
+
+cont:
+ if (nvp_size > left)
+ break;
+ left -= nvp_size;
+
+ nvp = (struct nvpair *)((char *)nvp + nvp_size);
+ }
+}
+
+static int find_uberblocks(const void *label, loff_t *ub_offset, int *swap_endian)
+{
+ uint64_t swab_magic = swab64((uint64_t)UBERBLOCK_MAGIC);
+ const struct zfs_uberblock *ub;
+ int i, found = 0;
+ loff_t offset = VDEV_LABEL_UBERBLOCK;
+
+ for (i = 0; i < UBERBLOCKS_COUNT; i++, offset += UBERBLOCK_SIZE) {
+ ub = (const struct zfs_uberblock *)((const char *) label + offset);
+
+ if (ub->ub_magic == UBERBLOCK_MAGIC) {
+ *ub_offset = offset;
+ *swap_endian = 0;
+ found++;
+ DBG(LOWPROBE, ul_debug("probe_zfs: found little-endian uberblock at %jd", (intmax_t)offset >> 10));
+ }
+
+ if (ub->ub_magic == swab_magic) {
+ *ub_offset = offset;
+ *swap_endian = 1;
+ found++;
+ DBG(LOWPROBE, ul_debug("probe_zfs: found big-endian uberblock at %jd", (intmax_t)offset >> 10));
+ }
+ }
+
+ return found;
+}
+
+/* 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__)))
+{
+ int swab_endian = 0;
+ struct zfs_uberblock *ub = NULL;
+ loff_t offset = 0, ub_offset = 0;
+ int label_no, found = 0, found_in_label;
+ void *label;
+ loff_t blk_align = (pr->size % (256 * 1024ULL));
+
+ DBG(PROBE, ul_debug("probe_zfs"));
+ /* Look for at least 4 uberblocks to ensure a positive match */
+ for (label_no = 0; label_no < 4; label_no++) {
+ switch(label_no) {
+ case 0: // jump to L0
+ offset = 0;
+ break;
+ case 1: // jump to L1
+ offset = VDEV_LABEL_SIZE;
+ break;
+ case 2: // jump to L2
+ offset = pr->size - 2 * VDEV_LABEL_SIZE - blk_align;
+ break;
+ case 3: // jump to L3
+ offset = pr->size - VDEV_LABEL_SIZE - blk_align;
+ break;
+ }
+
+ if ((S_ISREG(pr->mode) || blkid_probe_is_wholedisk(pr)) &&
+ blkid_probe_is_covered_by_pt(pr, offset, VDEV_LABEL_SIZE))
+ /* ignore this area, it's within any partition and
+ * we are working with whole-disk now */
+ continue;
+
+ label = blkid_probe_get_buffer(pr, offset, VDEV_LABEL_SIZE);
+ if (label == NULL)
+ return errno ? -errno : 1;
+
+ found_in_label = find_uberblocks(label, &ub_offset, &swab_endian);
+
+ if (found_in_label > 0) {
+ found+= found_in_label;
+ ub = (struct zfs_uberblock *)((char *) label + ub_offset);
+ ub_offset += offset;
+
+ if (found >= ZFS_WANT)
+ break;
+ }
+ }
+
+ if (found < ZFS_WANT)
+ 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, ub_offset,
+ sizeof(ub->ub_magic),
+ (unsigned char *) &ub->ub_magic))
+ return 1;
+
+ blkid_probe_set_fsendianness(pr, !swab_endian ?
+ BLKID_ENDIANNESS_NATIVE : BLKID_ENDIANNESS_OTHER);
+
+ return 0;
+}
+
+const struct blkid_idinfo zfs_idinfo =
+{
+ .name = "zfs_member",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_zfs,
+ .minsz = 64 * 1024 * 1024,
+ .magics = BLKID_NONE_MAGIC
+};
diff --git a/libblkid/src/superblocks/zonefs.c b/libblkid/src/superblocks/zonefs.c
new file mode 100644
index 0000000..8aa45b0
--- /dev/null
+++ b/libblkid/src/superblocks/zonefs.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 Western Digital Corporation or its affiliates.
+ *
+ * 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"
+
+#define ZONEFS_MAGIC "SFOZ" /* 0x5a4f4653 'Z' 'O' 'F' 'S' */
+#define ZONEFS_MAGIC_SIZE 4
+#define ZONEFS_MAGIC_OFST 0
+#define ZONEFS_UUID_SIZE 16
+#define ZONEFS_LABEL_SIZE 32
+#define ZONEFS_SB_OFST 0
+
+#define ZONEFS_BLOCK_SIZE 4096U
+
+/* All in little-endian */
+struct zonefs_super {
+
+ /* Magic number */
+ int32_t s_magic;
+
+ /* Checksum */
+ int32_t s_crc;
+
+ /* Volume label */
+ char s_label[ZONEFS_LABEL_SIZE];
+
+ /* 128-bit uuid */
+ uint8_t s_uuid[ZONEFS_UUID_SIZE];
+
+ /* Features */
+ int64_t s_features;
+
+ /* UID/GID to use for files */
+ int32_t s_uid;
+ int32_t s_gid;
+
+ /* File permissions */
+ int32_t s_perm;
+
+ /* Padding to 4096 bytes */
+ uint8_t s_reserved[4020];
+
+} __attribute__ ((packed));
+
+static int zonefs_verify_csum(blkid_probe pr, const struct zonefs_super *sb)
+{
+ uint32_t expected = le32_to_cpu(sb->s_crc);
+ uint32_t crc = ul_crc32_exclude_offset(
+ ~0LL, (unsigned char *) sb, sizeof(*sb),
+ offsetof(typeof(*sb), s_crc), sizeof(sb->s_crc));
+ return blkid_probe_verify_csum(pr, crc, expected);
+}
+
+static int probe_zonefs(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ const struct zonefs_super *sb;
+
+ sb = (struct zonefs_super *)
+ blkid_probe_get_buffer(pr, ZONEFS_SB_OFST,
+ sizeof(struct zonefs_super));
+ if (!sb)
+ return errno ? -errno : 1;
+
+ if (!zonefs_verify_csum(pr, sb))
+ return 1;
+
+ if (sb->s_label[0])
+ blkid_probe_set_label(pr, (unsigned char *) sb->s_label,
+ sizeof(sb->s_label));
+
+ blkid_probe_set_uuid(pr, sb->s_uuid);
+ blkid_probe_set_fsblocksize(pr, ZONEFS_BLOCK_SIZE);
+ blkid_probe_set_block_size(pr, ZONEFS_BLOCK_SIZE);
+
+ return 0;
+}
+
+const struct blkid_idinfo zonefs_idinfo =
+{
+ .name = "zonefs",
+ .usage = BLKID_USAGE_FILESYSTEM,
+ .probefunc = probe_zonefs,
+ .magics =
+ {
+ {
+ .magic = (char *)ZONEFS_MAGIC,
+ .len = ZONEFS_MAGIC_SIZE,
+ .kboff = ZONEFS_SB_OFST,
+ .sboff = ZONEFS_MAGIC_OFST,
+ },
+ { NULL }
+ }
+};
diff --git a/libblkid/src/tag.c b/libblkid/src/tag.c
new file mode 100644
index 0000000..1783365
--- /dev/null
+++ b/libblkid/src/tag.c
@@ -0,0 +1,468 @@
+/*
+ * 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 = calloc(1, sizeof(struct blkid_struct_tag))))
+ return NULL;
+
+ DBG(TAG, ul_debugobj(tag, "alloc"));
+ INIT_LIST_HEAD(&tag->bit_tags);
+ INIT_LIST_HEAD(&tag->bit_names);
+
+ return tag;
+}
+
+void blkid_free_tag(blkid_tag tag)
+{
+ if (!tag)
+ return;
+
+ DBG(TAG, ul_debugobj(tag, "freeing tag %s (%s)", tag->bit_name, tag->bit_val));
+
+ 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;
+
+ 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;
+}
+
+int blkid_dev_has_tag(blkid_dev dev, const char *type,
+ const char *value)
+{
+ blkid_tag tag;
+
+ tag = blkid_find_tag_dev(dev, type);
+ if (!value)
+ return (tag != NULL);
+ if (!tag || strcmp(tag->bit_val, value) != 0)
+ 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(TAG, ul_debug("found cache tag head %s", type));
+ head = tmp;
+ break;
+ }
+ }
+ return head;
+}
+
+/*
+ * Set a tag on an existing device.
+ *
+ * If value is NULL, then delete the tags from the device.
+ */
+int blkid_set_tag(blkid_dev dev, const char *name,
+ const char *value, const int vlength)
+{
+ blkid_tag t = NULL, head = NULL;
+ char *val = NULL;
+ char **dev_var = NULL;
+
+ if (value && !(val = 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;
+ }
+ DBG(TAG, ul_debugobj(t, "update (%s) '%s' -> '%s'", t->bit_name, t->bit_val, val));
+ 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 = strdup(name);
+ t->bit_val = val;
+ t->bit_dev = dev;
+
+ DBG(TAG, ul_debugobj(t, "setting (%s) '%s'", t->bit_name, t->bit_val));
+ 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(TAG, ul_debugobj(head, "creating new cache tag head %s", name));
+ head->bit_name = 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(TAG, ul_debug("trying to parse '%s' as a tag", token));
+
+ if (!token || !(cp = strchr(token, '=')))
+ return -1;
+
+ name = 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';
+ }
+
+ if (ret_val) {
+ value = *value ? strdup(value) : NULL;
+ if (!value)
+ goto errout;
+ *ret_val = value;
+ }
+
+ if (ret_type)
+ *ret_type = name;
+ else
+ free(name);
+
+ return 0;
+
+errout:
+ DBG(TAG, ul_debug("parse error: '%s'", token));
+ 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 implementation. 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 trade-off 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;
+};
+
+blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev)
+{
+ blkid_tag_iterate iter;
+
+ if (!dev) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ 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
+ */
+int blkid_tag_next(blkid_tag_iterate iter,
+ const char **type, const char **value)
+{
+ blkid_tag tag;
+
+ if (!type || !value ||
+ !iter || iter->magic != TAG_ITERATE_MAGIC ||
+ iter->p == &iter->dev->bid_tags)
+ return -1;
+
+ *type = NULL;
+ *value = NULL;
+ 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;
+}
+
+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.
+ */
+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, probe_all = 0;
+
+ if (!cache || !type || !value)
+ return NULL;
+
+ blkid_read_cache(cache);
+
+ DBG(TAG, ul_debug("looking for tag %s=%s in cache", type, value));
+
+try_again:
+ pri = -1;
+ dev = NULL;
+ 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->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 && !probe_all
+ && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) {
+ if (blkid_probe_all(cache) < 0)
+ return NULL;
+ probe_all++;
+ goto try_again;
+ }
+ return dev;
+}
+
+#ifdef TEST_PROGRAM
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+static 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: cannot 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/libblkid/src/topology/dm.c b/libblkid/src/topology/dm.c
new file mode 100644
index 0000000..612b5df
--- /dev/null
+++ b/libblkid/src/topology/dm.c
@@ -0,0 +1,134 @@
+/*
+ * 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 * const paths[] = {
+ "/usr/local/sbin/dmsetup",
+ "/usr/sbin/dmsetup",
+ "/sbin/dmsetup"
+ };
+ int dmpipe[] = { -1, -1 }, stripes = 0, stripesize = 0;
+ const char *cmd = NULL;
+ FILE *stream = NULL;
+ long long offset = 0, size = 0;
+ 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 = paths[i];
+ break;
+ }
+ }
+
+ if (!cmd)
+ goto nothing;
+ if (pipe(dmpipe) < 0) {
+ DBG(LOWPROBE, ul_debug("Failed to open pipe: errno=%d", errno));
+ goto nothing;
+ }
+
+ switch (fork()) {
+ case 0:
+ {
+ const char *dmargv[7];
+ char maj[16], min[16];
+
+ /* Plumbing */
+ close(dmpipe[0]);
+
+ if (dmpipe[1] != STDOUT_FILENO)
+ dup2(dmpipe[1], STDOUT_FILENO);
+
+ if (drop_permissions() != 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], (char * const *) dmargv);
+
+ DBG(LOWPROBE, ul_debug("Failed to execute %s: errno=%d", cmd, errno));
+ exit(1);
+ }
+ case -1:
+ DBG(LOWPROBE, ul_debug("Failed to forking: errno=%d", errno));
+ goto nothing;
+ default:
+ break;
+ }
+
+ stream = fdopen(dmpipe[0], "r" UL_CLOEXECSTR);
+ if (!stream)
+ goto nothing;
+
+ if (dmpipe[1] != -1) {
+ close(dmpipe[1]);
+ }
+
+ 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);
+ return 0;
+
+nothing:
+ if (stream)
+ fclose(stream);
+ else if (dmpipe[0] != -1)
+ close(dmpipe[0]);
+ return 1;
+}
+
+const struct blkid_idinfo dm_tp_idinfo =
+{
+ .name = "dm",
+ .probefunc = probe_dm_tp,
+ .magics = BLKID_NONE_MAGIC
+};
+
diff --git a/libblkid/src/topology/evms.c b/libblkid/src/topology/evms.c
new file mode 100644
index 0000000..1aa32f9
--- /dev/null
+++ b/libblkid/src/topology/evms.c
@@ -0,0 +1,77 @@
+/*
+ * Evms 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"
+
+#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 */
+};
+
+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/libblkid/src/topology/ioctl.c b/libblkid/src/topology/ioctl.c
new file mode 100644
index 0000000..3560a2f
--- /dev/null
+++ b/libblkid/src/topology/ioctl.c
@@ -0,0 +1,83 @@
+/*
+ * 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 const struct topology_val {
+
+ long ioc;
+
+ /* functions to set probing result */
+ int (*set_ulong)(blkid_probe, unsigned long);
+ int (*set_int)(blkid_probe, int);
+ int (*set_u64)(blkid_probe, uint64_t);
+
+} 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 },
+ { BLKGETDISKSEQ, .set_u64 = blkid_topology_set_diskseq },
+ /* 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++) {
+ const struct topology_val *val = &topology_vals[i];
+ int rc = 1;
+ union {
+ unsigned long ul;
+ int i;
+ uint64_t u64;
+ } data;
+
+ if (ioctl(pr->fd, val->ioc, &data) == -1)
+ goto nothing;
+
+ if (val->set_int)
+ rc = val->set_int(pr, data.i);
+ else if (val->set_ulong)
+ rc = val->set_ulong(pr, data.ul);
+ else
+ rc = val->set_u64(pr, data.u64);
+
+ 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/libblkid/src/topology/lvm.c b/libblkid/src/topology/lvm.c
new file mode 100644
index 0000000..af1a612
--- /dev/null
+++ b/libblkid/src/topology/lvm.c
@@ -0,0 +1,144 @@
+/*
+ * 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 * const 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(LOWPROBE, ul_debug("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);
+
+ if (drop_permissions() != 0)
+ exit(1);
+
+ lvargv[0] = cmd;
+ lvargv[1] = devname;
+ lvargv[2] = NULL;
+
+ execv(lvargv[0], lvargv);
+
+ DBG(LOWPROBE, ul_debug("Failed to execute %s: errno=%d", cmd, errno));
+ exit(1);
+ }
+ case -1:
+ DBG(LOWPROBE, ul_debug("Failed to forking: errno=%d", errno));
+ goto nothing;
+ default:
+ break;
+ }
+
+ stream = fdopen(lvpipe[0], "r" UL_CLOEXECSTR);
+ if (!stream)
+ goto nothing;
+
+ while (fgets(buf, sizeof(buf), stream) != NULL) {
+ if (!strncmp(buf, "Stripes", 7))
+ ignore_result( sscanf(buf, "Stripes %d", &stripes) );
+
+ if (!strncmp(buf, "Stripe size", 11))
+ ignore_result( 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/libblkid/src/topology/md.c b/libblkid/src/topology/md.c
new file mode 100644
index 0000000..02f27a8
--- /dev/null
+++ b/libblkid/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|O_CLOEXEC);
+ 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, (unsigned long) 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/libblkid/src/topology/sysfs.c b/libblkid/src/topology/sysfs.c
new file mode 100644
index 0000000..25debd0
--- /dev/null
+++ b/libblkid/src/topology/sysfs.c
@@ -0,0 +1,132 @@
+/*
+ * 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 const struct topology_val {
+
+ /* /sys/dev/block/<maj>:<min>/<ATTR> */
+ const char *attr;
+
+ /* functions to set probing result */
+ int (*set_ulong)(blkid_probe, unsigned long);
+ int (*set_int)(blkid_probe, int);
+ int (*set_u64)(blkid_probe, uint64_t);
+
+} 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 },
+ { "queue/dax", blkid_topology_set_dax },
+ { "diskseq", .set_u64 = blkid_topology_set_diskseq },
+};
+
+static int probe_sysfs_tp(blkid_probe pr,
+ const struct blkid_idmag *mag __attribute__((__unused__)))
+{
+ dev_t dev;
+ int rc, set_parent = 1;
+ struct path_cxt *pc;
+ size_t i, count = 0;
+
+ dev = blkid_probe_get_devno(pr);
+ if (!dev)
+ return 1;
+ pc = ul_new_sysfs_path(dev, NULL, NULL);
+ if (!pc)
+ return 1;
+
+ rc = 1; /* nothing (default) */
+
+ for (i = 0; i < ARRAY_SIZE(topology_vals); i++) {
+ const struct topology_val *val = &topology_vals[i];
+ int ok = ul_path_access(pc, F_OK, val->attr) == 0;
+
+ rc = 1; /* nothing */
+
+ if (!ok && set_parent) {
+ dev_t disk = blkid_probe_get_wholedisk_devno(pr);
+ set_parent = 0;
+
+ /*
+ * Read attributes from "disk" if the current device is
+ * a partition. Note that sysfs ul_path_* API is able
+ * to redirect requests to attributes if parent is set.
+ */
+ if (disk && disk != dev) {
+ struct path_cxt *parent = ul_new_sysfs_path(disk, NULL, NULL);
+ if (!parent)
+ goto done;
+
+ sysfs_blkdev_set_parent(pc, parent);
+ ul_unref_path(parent);
+
+ /* try it again */
+ ok = ul_path_access(pc, F_OK, val->attr) == 0;
+ }
+ }
+ if (!ok)
+ continue; /* attribute does not exist */
+
+ if (val->set_ulong) {
+ uint64_t data;
+
+ if (ul_path_read_u64(pc, &data, val->attr) != 0)
+ continue;
+ rc = val->set_ulong(pr, (unsigned long) data);
+
+ } else if (val->set_int) {
+ int64_t data;
+
+ if (ul_path_read_s64(pc, &data, val->attr) != 0)
+ continue;
+ rc = val->set_int(pr, (int) data);
+ } else if (val->set_u64) {
+ uint64_t data;
+
+ if (ul_path_read_u64(pc, &data, val->attr) != 0)
+ continue;
+ rc = val->set_u64(pr, data);
+ }
+
+ if (rc < 0)
+ goto done; /* error */
+ if (rc == 0)
+ count++;
+ }
+
+done:
+ ul_unref_path(pc); /* unref pc and 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/libblkid/src/topology/topology.c b/libblkid/src/topology/topology.c
new file mode 100644
index 0000000..67df3e3
--- /dev/null
+++ b/libblkid/src/topology/topology.c
@@ -0,0 +1,423 @@
+/*
+ * 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 <inttypes.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;
+ unsigned long dax;
+ uint64_t diskseq;
+};
+
+/*
+ * Topology chain probing functions
+ */
+static const struct blkid_idinfo *idinfos[] =
+{
+#ifdef __linux__
+ &sysfs_tp_idinfo,
+ &ioctl_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)
+{
+ 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_topology objects in the same time you have to create
+ * more blkid_probe handlers (see blkid_new_probe()).
+ *
+ * Returns: blkid_topology, 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 (chn->idx < -1)
+ return -1;
+
+ if (!S_ISBLK(pr->mode))
+ return -EINVAL; /* nothing, works with block devices only */
+
+ if (chn->binary) {
+ DBG(LOWPROBE, ul_debug("initialize topology binary data"));
+
+ 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 -ENOMEM;
+ }
+ }
+
+ blkid_probe_chain_reset_values(pr, chn);
+
+ DBG(LOWPROBE, ul_debug("--> starting probing loop [TOPOLOGY idx=%d]",
+ 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(LOWPROBE, ul_debug("%s: call probefunc()", id->name));
+
+ errno = 0;
+ 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(LOWPROBE, ul_debug("<-- leaving probing loop (type=%s) [TOPOLOGY idx=%d]",
+ id->name, chn->idx));
+ return BLKID_PROBE_OK;
+ }
+
+ DBG(LOWPROBE, ul_debug("<-- leaving probing loop (failed) [TOPOLOGY idx=%d]",
+ chn->idx));
+ return BLKID_PROBE_NONE;
+}
+
+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((char *) chn->data + structoff, &data, sizeof(data));
+ return 0;
+ }
+ return blkid_probe_sprintf_value(pr, name, "%lu", data);
+}
+
+static int topology_set_value64(blkid_probe pr, const char *name,
+ size_t structoff, uint64_t data)
+{
+ struct blkid_chain *chn = blkid_probe_get_chain(pr);
+
+ if (!chn)
+ return -1;
+ if (!data)
+ return 0; /* ignore zeros */
+
+ if (chn->binary) {
+ memcpy((char *) chn->data + structoff, &data, sizeof(data));
+ return 0;
+ }
+ return blkid_probe_sprintf_value(pr, name, "%"PRIu64, 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);
+}
+
+int blkid_topology_set_dax(blkid_probe pr, unsigned long val)
+{
+ return topology_set_value(pr,
+ "DAX",
+ offsetof(struct blkid_struct_topology, dax),
+ val);
+}
+
+int blkid_topology_set_diskseq(blkid_probe pr, uint64_t val)
+{
+ return topology_set_value64(pr,
+ "DISKSEQ",
+ offsetof(struct blkid_struct_topology, diskseq),
+ 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->alignment_offset;
+}
+
+/**
+ * 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->minimum_io_size;
+}
+
+/**
+ * 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->optimal_io_size;
+}
+
+/**
+ * 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->logical_sector_size;
+}
+
+/**
+ * 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->physical_sector_size;
+}
+
+/**
+ * blkid_topology_get_dax
+ * @tp: topology
+ *
+ * Returns: 1 if dax is supported, 0 otherwise.
+ *
+ * Since: 2.36
+ */
+unsigned long blkid_topology_get_dax(blkid_topology tp)
+{
+ return tp->dax;
+}
+
+/**
+ * blkid_topology_get_diskseq
+ * @tp: topology
+ *
+ * Returns: disk sequence number
+ *
+ * Since: 2.39
+ */
+uint64_t blkid_topology_get_diskseq(blkid_topology tp)
+{
+ return tp->diskseq;
+}
diff --git a/libblkid/src/topology/topology.h b/libblkid/src/topology/topology.h
new file mode 100644
index 0000000..9810fe4
--- /dev/null
+++ b/libblkid/src/topology/topology.h
@@ -0,0 +1,26 @@
+#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);
+extern int blkid_topology_set_dax(blkid_probe pr, unsigned long val);
+extern int blkid_topology_set_diskseq(blkid_probe pr, uint64_t 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/libblkid/src/verify.c b/libblkid/src/verify.c
new file mode 100644
index 0000000..3b9754f
--- /dev/null
+++ b/libblkid/src/verify.c
@@ -0,0 +1,226 @@
+/*
+ * 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"
+#include "sysfs.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 if (!strstr(name, "_ID")) {
+ /* superblock UUID, LABEL, ...
+ * but not {SYSTEM,APPLICATION,..._ID} */
+ blkid_set_tag(dev, name, data, len);
+ }
+ }
+}
+
+/*
+ * 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)
+{
+ blkid_tag_iterate iter;
+ const char *type, *value;
+ struct stat st;
+ time_t diff, now;
+ int fd;
+
+ if (!dev || !cache)
+ return NULL;
+
+ now = time(NULL);
+ diff = (uintmax_t)now - dev->bid_time;
+
+ if (stat(dev->bid_name, &st) < 0) {
+ DBG(PROBE, ul_debug("blkid_verify: error %s (%d) while "
+ "trying to stat %s", strerror(errno), errno,
+ dev->bid_name));
+ open_err:
+ if ((errno == EPERM) || (errno == EACCES) || (errno == ENOENT)) {
+ /* We don't have read permission, just return cache data. */
+ DBG(PROBE, ul_debug("returning unverified data for %s",
+ 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 >= 0 && diff < BLKID_PROBE_MIN) {
+ dev->bid_flags |= BLKID_BID_FL_VERIFIED;
+ return dev;
+ }
+
+#ifndef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+ DBG(PROBE, ul_debug("need to revalidate %s (cache time %lld, stat time %lld,\t"
+ "time since last check %lld)",
+ dev->bid_name, (long long)dev->bid_time,
+ (long long)st.st_mtime, (long long)diff));
+#else
+ DBG(PROBE, ul_debug("need to revalidate %s (cache time %lld.%lld, stat time %lld.%lld,\t"
+ "time since last check %lld)",
+ dev->bid_name,
+ (long long)dev->bid_time, (long long)dev->bid_utime,
+ (long long)st.st_mtime, (long long)st.st_mtim.tv_nsec / 1000,
+ (long long)diff));
+#endif
+
+ if (sysfs_devno_is_dm_private(st.st_rdev, NULL)) {
+ blkid_free_dev(dev);
+ return NULL;
+ }
+ if (!cache->probe) {
+ cache->probe = blkid_new_probe();
+ if (!cache->probe) {
+ blkid_free_dev(dev);
+ return NULL;
+ }
+ }
+
+ fd = open(dev->bid_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ if (fd < 0) {
+ DBG(PROBE, ul_debug("blkid_verify: error %s (%d) while "
+ "opening %s", strerror(errno), 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;
+ }
+
+ /* remove old cache info */
+ iter = blkid_tag_iterate_begin(dev);
+ while (blkid_tag_next(iter, &type, &value) == 0)
+ blkid_set_tag(dev, type, NULL, 0);
+ blkid_tag_iterate_end(iter);
+
+ /* enable superblocks probing */
+ 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);
+
+ /* enable partitions probing */
+ blkid_probe_enable_partitions(cache->probe, TRUE);
+ blkid_probe_set_partitions_flags(cache->probe, BLKID_PARTS_ENTRY_DETAILS);
+
+ /* probe */
+ if (blkid_do_safeprobe(cache->probe)) {
+ /* found nothing or error */
+ blkid_free_dev(dev);
+ dev = NULL;
+ }
+
+ 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(NULL);
+
+ 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(PROBE, ul_debug("%s: devno 0x%04llx, type %s",
+ dev->bid_name, (long long)st.st_rdev, dev->bid_type));
+ }
+
+ /* reset prober */
+ blkid_probe_reset_superblocks_filter(cache->probe);
+ blkid_probe_set_device(cache->probe, -1, 0, 0);
+ 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/libblkid/src/version.c b/libblkid/src/version.c
new file mode 100644
index 0000000..ed92db9
--- /dev/null
+++ b/libblkid/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 release 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);
+}