Whamcloud - gitweb
Integrate new blkid library.
authorTheodore Ts'o <tytso@mit.edu>
Thu, 23 Jan 2003 21:45:16 +0000 (16:45 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 23 Jan 2003 21:45:16 +0000 (16:45 -0500)
27 files changed:
ChangeLog
MCONFIG.in
Makefile.in
configure
configure.in
lib/blkid/ChangeLog [new file with mode: 0644]
lib/blkid/Makefile.in [new file with mode: 0644]
lib/blkid/blkid.h [new file with mode: 0644]
lib/blkid/blkid_types.h.in [new file with mode: 0644]
lib/blkid/cache.c [new file with mode: 0644]
lib/blkid/dev.c [new file with mode: 0644]
lib/blkid/devname.c [new file with mode: 0644]
lib/blkid/devno.c [new file with mode: 0644]
lib/blkid/getsize.c [new file with mode: 0644]
lib/blkid/libblkid.3.in [new file with mode: 0644]
lib/blkid/list.h [new file with mode: 0644]
lib/blkid/llseek.c [new file with mode: 0644]
lib/blkid/probe.c [new file with mode: 0644]
lib/blkid/probe.h [new file with mode: 0644]
lib/blkid/read.c [new file with mode: 0644]
lib/blkid/resolve.c [new file with mode: 0644]
lib/blkid/save.c [new file with mode: 0644]
lib/blkid/tag.c [new file with mode: 0644]
misc/ChangeLog
misc/Makefile.in
misc/blkid.8.in [new file with mode: 0644]
misc/blkid.c [new file with mode: 0644]

index e13f445..c3769e2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2003-01-23  Theodore Ts'o  <tytso@mit.edu>
+
+       * Makefile.in, configure, configure.in: Integrate new blkid library.
+
 2002-11-09  Theodore Ts'o  <tytso@mit.edu>
 
        * Release of E2fsprogs 1.32
index 4847cc4..0bd6f99 100644 (file)
@@ -65,6 +65,7 @@ LIBCOM_ERR = $(LIB)/libcom_err@LIB_EXT@
 LIBE2P = $(LIB)/libe2p@LIB_EXT@
 LIBEXT2FS = $(LIB)/libext2fs@LIB_EXT@
 LIBUUID = $(LIB)/libuuid@LIB_EXT@ @SOCKET_LIB@ 
+LIBBLKID = $(LIB)/libblkid@LIB_EXT@
 DEPLIBUUID = $(LIB)/libuuid@LIB_EXT@
 
 STATIC_LIBSS = $(LIB)/libss@STATIC_LIB_EXT@
@@ -72,6 +73,7 @@ STATIC_LIBCOM_ERR = $(LIB)/libcom_err@STATIC_LIB_EXT@
 STATIC_LIBE2P = $(LIB)/libe2p@STATIC_LIB_EXT@
 STATIC_LIBEXT2FS = $(LIB)/libext2fs@STATIC_LIB_EXT@
 STATIC_LIBUUID = $(LIB)/libuuid@STATIC_LIB_EXT@ @SOCKET_LIB@ 
+STATIC_LIBBLKID = $(LIB)/libblkid@STATIC_LIB_EXT@
 DEPSTATIC_LIBUUID = $(LIB)/libuuid@STATIC_LIB_EXT@
 
 PROFILED_LIBSS = $(LIB)/libss@PROFILED_LIB_EXT@
@@ -79,6 +81,7 @@ PROFILED_LIBCOM_ERR = $(LIB)/libcom_err@PROFILED_LIB_EXT@
 PROFILED_LIBE2P = $(LIB)/libe2p@PROFILED_LIB_EXT@
 PROFILED_LIBEXT2FS = $(LIB)/libext2fs@PROFILED_LIB_EXT@
 PROFILED_LIBUUID = $(LIB)/libuuid@PROFILED_LIB_EXT@ @SOCKET_LIB@ 
+PROFILED_LIBBLKID = $(LIB)/libblkid@PROFILED_LIB_EXT@
 DEPPROFILED_LIBUUID = $(LIB)/libuuid@PROFILED_LIB_EXT@
 
 #
index 42468f0..76de6c2 100644 (file)
@@ -11,7 +11,7 @@ INSTALL = @INSTALL@
 @DEBUGFS_CMT@DEBUGFS_DIR= debugfs
 @LINUX_CMT@EVMS_DIR= lib/evms
 
-LIB_SUBDIRS=lib/et lib/ss lib/e2p lib/ext2fs lib/uuid # lib/finddev
+LIB_SUBDIRS=lib/et lib/ss lib/e2p lib/ext2fs lib/uuid lib/blkid
 PROG_SUBDIRS=e2fsck $(DEBUGFS_DIR) misc $(RESIZE_DIR) tests/progs $(EVMS_DIR)
 SUBDIRS=util $(LIB_SUBDIRS) $(PROG_SUBDIRS) tests
 
index 30867f0..ff6fd9e 100644 (file)
--- a/configure
+++ b/configure
@@ -4676,7 +4676,7 @@ EOF
 
   fi
 fi
-for ac_func in chflags getrusage llseek lseek64 open64 getmntinfo strcasecmp srandom fchown mallinfo fdatasync strnlen sysconf pathconf
+for ac_func in chflags getrusage llseek lseek64 open64 getmntinfo strtoull strcasecmp srandom fchown mallinfo fdatasync strnlen sysconf pathconf
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
 echo "configure:4683: checking for $ac_func" >&5
@@ -5028,7 +5028,7 @@ ac_given_INSTALL="$INSTALL"
 
 trap 'rm -fr `echo "MCONFIG Makefile util/Makefile util/subst.conf lib/et/Makefile 
        lib/ss/Makefile lib/ext2fs/Makefile lib/e2p/Makefile lib/uuid/Makefile
-       lib/evms/Makefile
+       lib/blkid/Makefile lib/blkid/blkid_types.h lib/evms/Makefile
        misc/Makefile ext2ed/Makefile e2fsck/Makefile debugfs/Makefile \
        tests/Makefile tests/progs/Makefile $rmakefile doc/Makefile 
        intl/Makefile po/Makefile.in" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
@@ -5213,7 +5213,7 @@ cat >> $CONFIG_STATUS <<EOF
 
 CONFIG_FILES=\${CONFIG_FILES-"MCONFIG Makefile util/Makefile util/subst.conf lib/et/Makefile 
        lib/ss/Makefile lib/ext2fs/Makefile lib/e2p/Makefile lib/uuid/Makefile
-       lib/evms/Makefile
+       lib/blkid/Makefile lib/blkid/blkid_types.h lib/evms/Makefile
        misc/Makefile ext2ed/Makefile e2fsck/Makefile debugfs/Makefile \
        tests/Makefile tests/progs/Makefile $rmakefile doc/Makefile 
        intl/Makefile po/Makefile.in"}
index 6c1e8ae..1ed81cc 100644 (file)
@@ -595,7 +595,7 @@ if test "$e2fsprogs_cv_struct_st_flags" = yes; then
          AC_DEFINE(HAVE_STAT_FLAGS)
   fi
 fi
-AC_CHECK_FUNCS(chflags getrusage llseek lseek64 open64 getmntinfo strcasecmp srandom fchown mallinfo fdatasync strnlen sysconf pathconf)
+AC_CHECK_FUNCS(chflags getrusage llseek lseek64 open64 getmntinfo strtoull strcasecmp srandom fchown mallinfo fdatasync strnlen sysconf pathconf)
 dnl
 dnl Check to see if -lsocket is required (solaris) to make something
 dnl that uses socket() to compile; this is needed for the UUID library
@@ -736,7 +736,7 @@ if test -d ${srcdir}/resize ; then
 fi
 AC_OUTPUT(MCONFIG Makefile util/Makefile util/subst.conf lib/et/Makefile 
        lib/ss/Makefile lib/ext2fs/Makefile lib/e2p/Makefile lib/uuid/Makefile
-       lib/evms/Makefile
+       lib/blkid/Makefile lib/blkid/blkid_types.h lib/evms/Makefile
        misc/Makefile ext2ed/Makefile e2fsck/Makefile debugfs/Makefile \
        tests/Makefile tests/progs/Makefile $rmakefile doc/Makefile 
        intl/Makefile po/Makefile.in)
diff --git a/lib/blkid/ChangeLog b/lib/blkid/ChangeLog
new file mode 100644 (file)
index 0000000..8a00f90
--- /dev/null
@@ -0,0 +1,7 @@
+2002-10-04  Jordan Breeding  <jordan.breeding@attbi.com>
+
+       * Forward port to e2fsprogs 1.30
+
+2001-09-20  Andreas Dilger  <adilger@turbolinux.com>
+
+       * Initial release of libblkid.
diff --git a/lib/blkid/Makefile.in b/lib/blkid/Makefile.in
new file mode 100644 (file)
index 0000000..9420a8e
--- /dev/null
@@ -0,0 +1,156 @@
+# Makefile for libblkid
+#
+# Copyright (C) 2001 Theodore Ts'o (tytso@mit.edu)
+#
+# This file can be redistributed under the terms of the
+# GNU Lesser General Public License
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+top_builddir = ../..
+my_dir = lib/blkid
+INSTALL = @INSTALL@
+
+@MCONFIG@
+
+all:: 
+
+SMANPAGES=     libblkid.3
+
+OBJS= cache.o dev.o devname.o devno.o getsize.o llseek.o probe.o \
+ read.o resolve.o save.o tag.o
+
+SRCS= $(srcdir)/cache.c $(srcdir)/dev.c $(srcdir)/devname.c $(srcdir)/devno.c \
+ $(srcdir)/getsize.c $(srcdir)/llseek.c $(srcdir)/probe.c \
+ $(srcdir)/read.c $(srcdir)/resolve.c $(srcdir)/save.c $(srcdir)/tag.c
+
+HFILES=                blkid.h list.h
+HFILES_IN=     blkid_types.h
+
+LIBRARY= libblkid
+LIBDIR= blkid
+
+ELF_VERSION = 2.0
+ELF_SO_VERSION = 2
+ELF_IMAGE = libblkid
+ELF_MYDIR = blkid
+ELF_INSTALL_DIR = $(root_libdir)
+ELF_OTHER_LIBS =
+
+BSDLIB_VERSION = 2.0
+BSDLIB_IMAGE = libblkid
+BSDLIB_MYDIR = blkid
+BSDLIB_INSTALL_DIR = $(root_libdir)
+
+@MAKEFILE_LIBRARY@
+@MAKEFILE_DLL@
+@MAKEFILE_ELF@
+@MAKEFILE_BSDLIB@
+@MAKEFILE_PROFILE@
+@MAKEFILE_CHECKER@
+
+LIBS_BLKID=    $(STATIC_LIBBLKID) $(STATIC_LIBUUID)
+DEPLIBS_BLKID= $(DEPSTATIC_LIBBLKID) $(DEPSTATIC_LIBUUID)
+
+.c.o:
+       $(CC) -Wall $(ALL_CFLAGS) -c $< -o $@
+@PROFILE_CMT@  $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $<
+@CHECKER_CMT@  $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $<
+@DLL_CMT@      (export JUMP_DIR=`pwd`/jump; $(CC) -B$(JUMP_PREFIX) $(ALL_CFLAGS) \
+@DLL_CMT@              -o jump/$*.o -c $<)
+@ELF_CMT@      $(CC) $(ALL_CFLAGS) -fPIC -o elfshared/$*.o -c $<
+@BSDLIB_CMT@   $(CC) $(ALL_CFLAGS) -fpic -o pic/$*.o -c $<
+
+all:: $(SMANPAGES)
+
+libblkid.3: $(DEP_SUBSTITUTE) $(srcdir)/libblkid.3.in
+       $(SUBSTITUTE) $(srcdir)/libblkid.3.in libblkid.3
+
+tst_cache: $(srcdir)/cache.c $(DEPLIBS_BLKID)
+       $(CC) -Wall -o tst_cache -DTEST_PROGRAM $(srcdir)/cache.c $(LIBS_BLKID) $(LIBUUID) $(ALL_CFLAGS)
+
+tst_dev: $(srcdir)/dev.c $(DEPLIBS_BLKID)
+       $(CC) -Wall -o tst_dev -DTEST_PROGRAM $(srcdir)/dev.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_devname: $(srcdir)/devname.c $(DEPLIBS_BLKID)
+       $(CC) -Wall -o tst_devname -DTEST_PROGRAM $(srcdir)/devname.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_devno: $(srcdir)/devno.c $(DEPLIBS_BLKID)
+       $(CC) -Wall -o tst_devno -DTEST_PROGRAM $(srcdir)/devno.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_getsize: $(srcdir)/getsize.c $(DEPLIBS_BLKID)
+       $(CC) -Wall -o tst_getsize -DTEST_PROGRAM $(srcdir)/getsize.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_probe: $(srcdir)/probe.c $(DEPLIBS_BLKID)
+       $(CC) -Wall -o tst_probe -DTEST_PROGRAM $(srcdir)/probe.c $(LIBS_BLKID) $(LIBUUID) $(ALL_CFLAGS)
+
+tst_read: $(srcdir)/read.c $(DEPLIBS_BLKID)
+       $(CC) -Wall -o tst_read -DTEST_PROGRAM $(srcdir)/read.c $(LIBS_BLKID) $(ALL_CFLAGS)
+
+tst_resolve: $(srcdir)/resolve.c $(DEPLIBS_BLKID)
+       $(CC) -Wall -o tst_resolve -DTEST_PROGRAM $(srcdir)/resolve.c $(LIBS_BLKID) $(LIBUUID) $(ALL_CFLAGS)
+
+tst_save: $(srcdir)/save.c $(DEPLIBS_BLKID)
+       $(CC) -Wall -o tst_save -DTEST_PROGRAM $(srcdir)/save.c $(LIBS_BLKID) $(LIBUUID) $(ALL_CFLAGS)
+
+check:: tst_cache tst_dev tst_devname tst_devno tst_getsize tst_probe \
+ tst_read tst_resolve tst_save
+
+installdirs::
+       $(top_srcdir)/mkinstalldirs $(DESTDIR)$(libdir) \
+               $(DESTDIR)$(includedir)/blkid
+
+install:: all installdirs 
+       $(INSTALL_DATA) libblkid.a $(DESTDIR)$(libdir)/libblkid.a
+       $(CHMOD) 644 $(DESTDIR)$(libdir)/libblkid.a
+       -$(RANLIB) $(DESTDIR)$(libdir)/libblkid.a
+       $(CHMOD) $(LIBMODE) $(DESTDIR)$(libdir)/libblkid.a
+       set -e; for i in $(HFILES); do \
+         $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir)/blkid/$$i; \
+       done
+       set -e; for i in $(HFILES_IN); do \
+               $(INSTALL_DATA) $$i $(DESTDIR)$(includedir)/blkid/$$i; \
+       done
+       for i in $(SMANPAGES); do \
+               $(INSTALL_DATA) $$i $(DESTDIR)$(man3dir)/$$i; \
+       done
+
+uninstall::
+       $(RM) -f $(DESTDIR)$(libdir)/libblkid.a
+       $(RM) -rf $(DESTDIR)$(includedir)/blkid
+       for i in $(SMANPAGES); do \
+               $(RM) -f $(DESTDIR)$(man3dir)/$$i; \
+       done
+
+clean::
+       $(RM) -f \#* *.s *.o *.orig *.a *~ *.bak tst_* core profiled/* checker/*
+       $(RM) -f ../libblkid.a ../libblkid_p.a $(SMANPAGES)
+
+mostlyclean:: clean
+distclean:: clean
+       $(RM) -f .depend Makefile $(srcdir)/TAGS $(srcdir)/Makefile.in.old
+
+# +++ Dependency line eater +++
+# 
+# Makefile dependencies follow.  This must be the last section in
+# the Makefile.in file
+#
+cache.o: $(srcdir)/cache.c $(srcdir)/blkid.h $(srcdir)/list.h \
+ $(top_builddir)/lib/blkid/blkid_types.h
+dev.o: $(srcdir)/dev.c $(srcdir)/blkid.h $(srcdir)/list.h \
+ $(top_builddir)/lib/blkid/blkid_types.h
+devname.o: $(srcdir)/devname.c $(srcdir)/blkid.h $(srcdir)/list.h \
+ $(top_builddir)/lib/blkid/blkid_types.h
+devno.o: $(srcdir)/devno.c $(srcdir)/blkid.h $(srcdir)/list.h \
+ $(top_builddir)/lib/blkid/blkid_types.h
+getsize.o: $(srcdir)/getsize.c $(srcdir)/blkid.h $(srcdir)/list.h \
+ $(top_builddir)/lib/blkid/blkid_types.h
+llseek.o: $(srcdir)/llseek.c $(srcdir)/blkid.h $(srcdir)/list.h \
+ $(top_builddir)/lib/blkid/blkid_types.h
+read.o: $(srcdir)/read.c $(srcdir)/blkid.h $(srcdir)/list.h \
+ $(top_builddir)/lib/blkid/blkid_types.h $(top_srcdir)/lib/uuid/uuid.h
+save.o: $(srcdir)/save.c $(srcdir)/blkid.h $(srcdir)/list.h \
+ $(top_builddir)/lib/blkid/blkid_types.h
+tag.o: $(srcdir)/tag.c $(srcdir)/blkid.h $(srcdir)/list.h \
+ $(top_builddir)/lib/blkid/blkid_types.h
diff --git a/lib/blkid/blkid.h b/lib/blkid/blkid.h
new file mode 100644 (file)
index 0000000..4d7ec25
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * blkid.h - Interface for libblkid, a library to identify block devices
+ *
+ * Copyright (C) 2001 Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifndef _BLKID_BLKID_H
+#define _BLKID_BLKID_H
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BLKID_VERSION  "1.2.0"
+#define BLKID_DATE     "22-Nov-2001"
+
+#include "blkid/list.h"
+#include "blkid/blkid_types.h"
+
+typedef __s64 blkid_loff_t;
+
+/*
+ * 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.
+ */
+typedef struct blkid_dev
+{
+       struct list_head        bid_devs;       /* All devices in the cache */
+       struct list_head        bid_tags;       /* All tags for this device */
+       char                    *bid_name;      /* Device inode pathname */
+       char                    *bid_type;      /* Preferred device TYPE */
+       blkid_loff_t            bid_size;       /* Filesystem size in bytes */
+       blkid_loff_t            bid_free;       /* Filesystem free in bytes */
+       blkid_loff_t            bid_devsize;    /* Device size in bytes */
+       dev_t                   bid_devno;      /* Device major/minor number */
+       time_t                  bid_time;       /* Last update time of device */
+       unsigned int            bid_id;         /* Unique cache id for device */
+       unsigned int            bid_flags;      /* Device status bitflags */
+       char                    *bid_label;     /* Shortcut to device LABEL */
+       char                    *bid_uuid;      /* Shortcut to binary UUID */
+       unsigned long           bid_unused[13]; /* Fields for future use */
+} blkid_dev;
+
+#define BLKID_BID_FL_VERIFIED  0x0001  /* Device data validated from disk */
+#define BLKID_BID_FL_MTYPE     0x0002  /* Device has multiple type matches */
+
+/*
+ * 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.
+ */
+typedef struct blkid_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 */
+       struct blkid_dev        *bit_dev;       /* pointer to device */
+       unsigned long           bit_unused[9];  /* Fields for future use */
+} blkid_tag;
+
+/*
+ * 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).
+ */
+typedef struct blkid_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 */
+       unsigned int            bic_idmax;      /* Highest ID assigned */
+       unsigned int            bic_flags;      /* Status flags of the cache */
+       unsigned long           bic_unused[9];  /* Fields for future use */
+} blkid_cache;
+
+#define BLKID_BIC_FL_PARSED    0x0001  /* We parsed a cache file */
+#define BLKID_BIC_FL_PROBED    0x0002  /* We probed /proc/partition devices */
+#define BLKID_BIC_FL_CHANGED   0x0004  /* Cache has changed from disk */
+
+extern char *string_copy(const char *s);
+extern char *stringn_copy(const char *s, const int length);
+extern void string_free(char *s);
+extern blkid_cache *blkid_new_cache(void);
+extern void blkid_free_cache(blkid_cache *cache);
+
+#define BLKID_CACHE_FILE "/etc/blkid.tab"
+extern const char *devdirs[];
+
+#define BLKID_ERR_IO    5
+#define BLKID_ERR_PROC  9
+#define BLKID_ERR_MEM  12
+#define BLKID_ERR_CACHE        14
+#define BLKID_ERR_DEV  19
+#define BLKID_ERR_PARAM        22
+#define BLKID_ERR_BIG  27
+
+#ifdef DEBUG
+#define DEBUG_CACHE
+#define DEBUG_DUMP
+#define DEBUG_DEV
+#define DEBUG_DEVNAME
+#define DEBUG_DEVNO
+#define DEBUG_PROBE
+#define DEBUG_READ
+#define DEBUG_RESOLVE
+#define DEBUG_SAVE
+#define DEBUG_TAG
+#define CHECK_TAG
+#endif
+
+#if defined(TEST_PROGRAM) && !defined(DEBUG_DUMP)
+#define DEBUG_DUMP
+#endif
+
+#ifdef DEBUG_DUMP
+static inline void DEB_DUMP_TAG(blkid_tag *tag)
+{
+       if (!tag) {
+               printf("    tag: NULL\n");
+               return;
+       }
+
+       printf("    tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val);
+}
+
+static inline void DEB_DUMP_DEV(blkid_dev *dev)
+{
+       struct list_head *p;
+
+       if (!dev) {
+               printf("  dev: NULL\n");
+               return;
+       }
+
+       printf("  dev: name = %s\n", dev->bid_name);
+       printf("  dev: DEVNO=\"0x%0Lx\"\n", dev->bid_devno);
+       printf("  dev: ID=\"%u\"\n", dev->bid_id);
+       printf("  dev: TIME=\"%lu\"\n", dev->bid_time);
+       printf("  dev: size = %Lu\n", dev->bid_size);
+       printf("  dev: flags = 0x%08X\n", dev->bid_flags);
+
+       list_for_each(p, &dev->bid_tags) {
+               blkid_tag *tag = list_entry(p, blkid_tag, bit_tags);
+               DEB_DUMP_TAG(tag);
+       }
+       printf("\n");
+}
+
+static inline void DEB_DUMP_CACHE(blkid_cache *cache)
+{
+       struct list_head *p;
+
+       if (!cache) {
+               printf("cache: NULL\n");
+               return;
+       }
+
+       printf("cache: time = %lu\n", cache->bic_time);
+       printf("cache: idmax = %u\n", cache->bic_idmax);
+       printf("cache: flags = 0x%08X\n", cache->bic_flags);
+
+       list_for_each(p, &cache->bic_devs) {
+               blkid_dev *dev = list_entry(p, blkid_dev, bid_devs);
+               DEB_DUMP_DEV(dev);
+       }
+}
+#else
+#define DEB_DUMP_TAG(tag) do {} while (0)
+#define DEB_DUMP_DEV(dev) do {} while (0)
+#define DEB_DUMP_CACHE(cache) do {} while (0)
+#endif
+
+/*
+ * Primitive disk functions: llseek.c, getsize.c
+ */
+extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence);
+extern blkid_loff_t blkid_get_dev_size(int fd);
+
+/*
+ * Getting data from the cache file: read.c
+ */
+int blkid_read_cache_line(blkid_cache *cache, blkid_dev **dev_p, char *cp);
+int blkid_read_cache_file(blkid_cache **cache, FILE *file);
+int blkid_read_cache(blkid_cache **cache, const char *filename);
+
+/*
+ * Save data to the cache file: save.c
+ */
+int blkid_save_cache_file(blkid_cache *cache, FILE *file);
+int blkid_save_cache(blkid_cache *cache, char *filename);
+
+/*
+ * Identify a device by inode name: probe.c
+ */
+extern blkid_dev *blkid_devname_to_dev(const char *devname,
+                                      blkid_loff_t size);
+
+/*
+ * Locate a device by inode name: devname.c
+ */
+extern blkid_dev *blkid_find_devname(blkid_cache *cache, const char *devname);
+extern blkid_dev *blkid_verify_devname(blkid_cache *cache, blkid_dev *dev);
+extern blkid_dev *blkid_get_devname(blkid_cache *cache, const char *devname);
+extern int blkid_probe_all(blkid_cache **cache);
+
+/*
+ * Locate a device by device major/minor number: devno.c
+ */
+extern char *blkid_devno_to_devname(dev_t devno);
+extern blkid_dev *blkid_find_devno(blkid_cache *cache, dev_t devno);
+extern blkid_dev *blkid_get_devno(blkid_cache *cache, dev_t devno);
+
+/*
+ * Functions to create and find a specific tag type: tag.c
+ */
+extern blkid_tag *blkid_new_tag(void);
+extern void blkid_free_tag(blkid_tag *tag);
+extern int blkid_create_tag(blkid_dev *dev, blkid_tag **tag,
+                           const char *name, const char *value,
+                           const int vlength);
+extern blkid_tag *blkid_token_to_tag(const char *token);
+extern blkid_tag *blkid_find_tv_tags(blkid_tag *head, const char *value);
+extern blkid_tag *blkid_find_tag_dev(blkid_dev *dev, blkid_tag *tag);
+extern blkid_tag *blkid_find_head_cache(blkid_cache *cache, blkid_tag *tag);
+extern blkid_tag *blkid_find_tag_cache(blkid_cache *cache, blkid_tag *tag);
+extern blkid_tag *blkid_get_tag_cache(blkid_cache *cache, blkid_tag *tag);
+
+/*
+ * Functions to create and find a specific tag type: dev.c
+ */
+extern blkid_dev *blkid_new_dev(void);
+extern void blkid_free_dev(blkid_dev *dev);
+extern blkid_dev *blkid_add_dev_to_cache(blkid_cache *cache, blkid_dev *dev);
+
+/*
+ * Helper functions for primarily single use: resolve.c
+ */
+extern char *blkid_get_tagname_devname(blkid_cache *cache, const char *tagname,
+                                      const char *devname);
+extern char *blkid_get_token(blkid_cache *cache, const char *token,
+                            const char *value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BLKID_BLKID_H */
diff --git a/lib/blkid/blkid_types.h.in b/lib/blkid/blkid_types.h.in
new file mode 100644 (file)
index 0000000..38f588f
--- /dev/null
@@ -0,0 +1,53 @@
+#ifndef _EXT2_TYPES_H
+#define _EXT2_TYPES_H
+
+/* 
+ * If linux/types.h is already been included, assume it has defined
+ * everything we need.  (cross fingers)
+ */
+#ifndef _LINUX_TYPES_H
+
+typedef unsigned char __u8;
+typedef signed char __s8;
+
+#if (@SIZEOF_INT@ == 8)
+typedef int            __s64;
+typedef unsigned int   __u64;
+#elif (@SIZEOF_LONG@ == 8)
+typedef long           __s64;
+typedef unsigned long  __u64;
+#elif (@SIZEOF_LONG_LONG@ == 8)
+#if defined(__GNUC__)
+typedef __signed__ long long   __s64;
+#else
+typedef signed long long       __s64;
+#endif
+typedef unsigned long long     __u64;
+#endif
+
+#if (@SIZEOF_INT@ == 2)
+typedef        int             __s16;
+typedef        unsigned int    __u16;
+#elif (@SIZEOF_SHORT@ == 2)
+typedef        short           __s16;
+typedef        unsigned short  __u16;
+#else
+  ?==error: undefined 16 bit type
+#endif
+
+#if (@SIZEOF_INT@ == 4)
+typedef        int             __s32;
+typedef        unsigned int    __u32;
+#elif (@SIZEOF_LONG@ == 4)
+typedef        long            __s32;
+typedef        unsigned long   __u32;
+#elif (@SIZEOF_SHORT@ == 4)
+typedef        short           __s32;
+typedef        unsigned short  __u32;
+#else
+ ?== error: undefined 32 bit type
+#endif
+
+#endif /* LINUX_TYPES_H */
+
+#endif /* EXT2_TYPES_H */
diff --git a/lib/blkid/cache.c b/lib/blkid/cache.c
new file mode 100644 (file)
index 0000000..83d28d4
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * cache.c - allocation/initialization/free routines for cache
+ *
+ * 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 <stdlib.h>
+#include "blkid/blkid.h"
+
+#ifdef DEBUG_CACHE
+#include <stdio.h>
+#define DEB_CACHE(fmt, arg...) printf("cache: " fmt, ## arg)
+#else
+#define DEB_CACHE(fmt, arg...) do {} while (0)
+#endif
+
+blkid_cache *blkid_new_cache(void)
+{
+       blkid_cache *cache;
+
+       if (!(cache = (blkid_cache *)calloc(1, sizeof(blkid_cache))))
+               return NULL;
+
+       INIT_LIST_HEAD(&cache->bic_devs);
+       INIT_LIST_HEAD(&cache->bic_tags);
+
+       return cache;
+}
+
+void blkid_free_cache(blkid_cache *cache)
+{
+       if (!cache)
+               return;
+
+       DEB_CACHE("freeing cache struct\n");
+       /* DEB_DUMP_CACHE(cache); */
+
+       while (!list_empty(&cache->bic_devs)) {
+               blkid_dev *dev = list_entry(cache->bic_devs.next, blkid_dev,
+                                           bid_devs);
+               blkid_free_dev(dev);
+       }
+
+       while (!list_empty(&cache->bic_tags)) {
+               blkid_tag *tag = list_entry(cache->bic_tags.next, blkid_tag,
+                                           bit_tags);
+
+               while (!list_empty(&tag->bit_names)) {
+                       blkid_tag *bad = list_entry(tag->bit_names.next,
+                                                   blkid_tag, bit_names);
+
+                       DEB_CACHE("warning: unfreed tag %s=%s\n",
+                                bad->bit_name, bad->bit_val);
+                       blkid_free_tag(bad);
+               }
+               blkid_free_tag(tag);
+       }
+       free(cache);
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+       blkid_cache *cache = NULL;
+       int ret;
+
+       if ((argc > 2)) {
+               fprintf(stderr, "Usage: %s [filename] \n", argv[0]);
+               exit(1);
+       }
+
+       if ((ret = blkid_read_cache(&cache, argv[1])) < 0)
+               fprintf(stderr, "error %d parsing cache file %s\n", ret,
+                       argv[1] ? argv[1] : BLKID_CACHE_FILE);
+       else if ((ret = blkid_probe_all(&cache) < 0))
+               fprintf(stderr, "error probing devices\n");
+       else if ((ret = blkid_save_cache(cache, argv[1])) < 0)
+               fprintf(stderr, "error %d saving cache to %s\n", ret,
+                       argv[1] ? argv[1] : BLKID_CACHE_FILE);
+
+       blkid_free_cache(cache);
+
+       return ret;
+}
+#endif
diff --git a/lib/blkid/dev.c b/lib/blkid/dev.c
new file mode 100644 (file)
index 0000000..1904c31
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * dev.c - allocation/initialization/free routines for dev
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include "blkid/blkid.h"
+
+#ifdef DEBUG_DEV
+#include <stdio.h>
+#define DEB_DEV(fmt, arg...) printf("dev: " fmt, ## arg)
+#else
+#define DEB_DEV(fmt, arg...) do {} while (0)
+#endif
+
+blkid_dev *blkid_new_dev(void)
+{
+       blkid_dev *dev;
+
+       if (!(dev = (blkid_dev *)calloc(1, sizeof(blkid_dev))))
+               return NULL;
+
+       INIT_LIST_HEAD(&dev->bid_devs);
+       INIT_LIST_HEAD(&dev->bid_tags);
+
+       return dev;
+}
+
+void blkid_free_dev(blkid_dev *dev)
+{
+       if (!dev)
+               return;
+
+       DEB_DEV("  freeing dev %s (%s)\n", dev->bid_name, dev->bid_type);
+       DEB_DUMP_DEV(dev);
+
+       list_del(&dev->bid_devs);
+       while (!list_empty(&dev->bid_tags)) {
+               blkid_tag *tag = list_entry(dev->bid_tags.next, blkid_tag,
+                                           bit_tags);
+               blkid_free_tag(tag);
+       }
+       if (dev->bid_name)
+               string_free(dev->bid_name);
+       free(dev);
+}
+
+/*
+ * This is kind of ugly, but I want to be able to compare two strings in
+ * several different ways.  For example, in some cases, if both strings
+ * are NULL, those would be considered different, but in other cases
+ * they would be considered the same.  Hence the ugliness.
+ *
+ * Use as:     "ret == SC_SAME" if both strings exist and are equal
+ *                             this is equivalent to "!(ret & SC_DIFF)"
+ *             "ret & SC_SAME" if both strings being NULL is also equal
+ *                             this is equivalent to "!(ret == SC_DIFF)"
+ *             "ret == SC_DIFF" if both strings exist and are different
+ *                             this is equivalent to "!(ret & SC_SAME)"
+ *             "ret & SC_DIFF" if both strings being NULL is also different
+ *                             this is equivalent to "!(ret == SC_SAME)"
+ *             "ret == SC_NONE" to see if both strings do not exist
+ */
+#define SC_DIFF        0x0001
+#define SC_NONE        0x0003
+#define SC_SAME        0x0002
+
+int string_compare(char *s1, char *s2)
+{
+       if (!s1 && !s2)
+               return SC_NONE;
+
+       if (!s1 || !s2)
+               return SC_DIFF;
+
+       if (strcmp(s1, s2))
+               return SC_DIFF;
+
+       return SC_SAME;
+}
+
+/*
+ * Add a tag to the global cache tag list.
+ */
+static int add_tag_to_cache(blkid_cache *cache, blkid_tag *tag)
+{
+       blkid_tag *head = NULL;
+
+       if (!cache || !tag)
+               return 0;
+
+       DEB_DEV("    adding tag %s=%s to cache\n", tag->bit_name, tag->bit_val);
+
+       if (!(head = blkid_find_head_cache(cache, tag))) {
+               head = blkid_new_tag();
+               if (!head)
+                       return -BLKID_ERR_MEM;
+
+               DEB_DEV("    creating new cache tag head %s\n",tag->bit_name);
+               head->bit_name = string_copy(tag->bit_name);
+               if (!head->bit_name) {
+                       blkid_free_tag(head);
+                       return -BLKID_ERR_MEM;
+               }
+
+               list_add_tail(&head->bit_tags, &cache->bic_tags);
+       }
+
+       /* Add this tag to global list */
+       list_add_tail(&tag->bit_names, &head->bit_names);
+
+       return 0;
+}
+
+/*
+ * Add a device to the global cache list, along with all its tags.
+ */
+blkid_dev *blkid_add_dev_to_cache(blkid_cache *cache, blkid_dev *dev)
+{
+       struct list_head *p;
+
+       if (!cache || !dev)
+               return dev;
+
+       if (!dev->bid_id)
+               dev->bid_id = ++(cache->bic_idmax);
+
+       list_for_each(p, &cache->bic_devs) {
+               blkid_dev *odev = list_entry(p, blkid_dev, bid_devs);
+               int dup_uuid, dup_label, dup_name, dup_type;
+
+               dup_name = string_compare(odev->bid_name, dev->bid_name);
+               dup_label = string_compare(odev->bid_label, dev->bid_label);
+               dup_uuid = string_compare(odev->bid_uuid, dev->bid_uuid);
+
+               if (odev->bid_id == dev->bid_id)
+                       dev->bid_id = ++(cache->bic_idmax);
+
+               /* Fields different, do nothing (check more fields?) */
+               if ((dup_name & SC_DIFF) && (dup_uuid & SC_DIFF) &&
+                   (dup_label & SC_DIFF))
+                       continue;
+
+               /* We can't simply allow duplicate fields if the bid_type is
+                * different, given that a filesystem may change from ext2
+                * to ext3 but it will have the same UUID and LABEL fields.
+                * We need to discard the old cache entry in this case.
+                */
+
+               /* If the UUIDs are the same but one is unverified discard it */
+               if (dup_uuid == SC_SAME) {
+                       DEB_DEV("  duplicate uuid %s\n", dev->bid_uuid);
+                       if (!(odev->bid_flags & BLKID_BID_FL_VERIFIED)) {
+                               dev->bid_id = odev->bid_id; /* keep old id */
+                               blkid_free_dev(odev);
+                               goto exit_new;
+                       } else if (!(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
+                               blkid_free_dev(dev);
+                               dev = odev;
+                               goto exit_old;
+                       }
+
+                       /* This shouldn't happen */
+                       fprintf(stderr, "blkid: same UUID for %s and %s\n",
+                               dev->bid_name, odev->bid_name);
+               }
+
+               /* If the device name is the same, discard one of them
+                * (prefer one that has been validated, or the first one).
+                */
+               if (dup_name == SC_SAME) {
+                       DEB_DEV("  duplicate devname %s\n", dev->bid_name);
+                       if (odev->bid_flags & BLKID_BID_FL_VERIFIED ||
+                           !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
+                               if ((dup_uuid & SC_SAME) &&
+                                   (dup_label & SC_SAME))      /* use old id */
+                                       dev->bid_id = odev->bid_id;
+                               blkid_free_dev(dev);
+                               dev = odev;
+                               goto exit_old;
+                       } else {
+                               blkid_free_dev(odev);
+                               goto exit_new;
+                       }
+               }
+
+               dup_type = string_compare(odev->bid_type, dev->bid_type);
+
+               if (dup_label == SC_SAME && dup_type == SC_SAME) {
+                       DEB_DEV("  duplicate label %s\n", dev->bid_label);
+                       if (!(odev->bid_flags & BLKID_BID_FL_VERIFIED)) {
+                               blkid_free_dev(odev);
+                               goto exit_new;
+                       } else if (!(dev->bid_flags & BLKID_BID_FL_VERIFIED)) {
+                               blkid_free_dev(dev);
+                               dev = odev;
+                               goto exit_old;
+                       }
+                       fprintf(stderr, "blkid: same LABEL for %s and %s\n",
+                               dev->bid_name, odev->bid_name);
+               }
+       }
+
+exit_new:
+       DEB_DEV("  adding new devname %s to cache\n", dev->bid_name);
+
+       cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+
+       list_add_tail(&dev->bid_devs, &cache->bic_devs);
+       list_for_each(p, &dev->bid_tags) {
+               blkid_tag *tag = list_entry(p, blkid_tag, bit_tags);
+               add_tag_to_cache(cache, tag);
+       }
+       return dev;
+
+exit_old:
+       DEB_DEV("  using old devname %s from cache\n", dev->bid_name);
+       return dev;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+       blkid_cache *cache;
+       blkid_dev *dev, *newdev;
+
+       if ((argc != 3)) {
+               fprintf(stderr, "Usage:\t%s dev1 dev2\n"
+                       "Test that adding the same device to the cache fails\n",
+                       argv[0]);
+               exit(1);
+       }
+
+       cache = blkid_new_cache();
+       if (!cache) {
+               perror(argv[0]);
+               return 1;
+       }
+       dev = blkid_devname_to_dev(argv[1], 0);
+       newdev = blkid_add_dev_to_cache(cache, dev);
+       if (newdev != dev)
+               printf("devices changed for %s (unexpected)\n", argv[1]);
+       dev = blkid_devname_to_dev(argv[2], 0);
+       newdev = blkid_add_dev_to_cache(cache, dev);
+       if (newdev != dev)
+               printf("devices changed for %s (unexpected)\n", argv[2]);
+       dev = blkid_devname_to_dev(argv[2], 0);
+       newdev = blkid_add_dev_to_cache(cache, dev);
+       if (newdev != dev)
+               printf("devices changed for %s (expected)\n", argv[2]);
+
+       blkid_free_cache(cache);
+
+       return 0;
+}
+#endif
diff --git a/lib/blkid/devname.c b/lib/blkid/devname.c
new file mode 100644 (file)
index 0000000..43c34ef
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * devname.c - get a dev by its device inode name
+ *
+ * Copyright (C) Andries Brouwer
+ * Copyright (C) 1999, 2000, 2001 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>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#include <time.h>
+
+#include "blkid/blkid.h"
+
+/* #define DEBUG_DEVNAME */
+#ifdef DEBUG_DEVNAME
+#define DEB_DEV(fmt, arg...) printf("devname: " fmt, ## arg)
+#else
+#define DEB_DEV(fmt, arg...) do {} while (0)
+#endif
+
+/*
+ * Find a dev struct in the cache by device name, if available.
+ */
+blkid_dev *blkid_find_devname(blkid_cache *cache, const char *devname)
+{
+       blkid_dev *dev = NULL;
+       struct list_head *p;
+
+       if (!cache || !devname)
+               return NULL;
+
+       list_for_each(p, &cache->bic_devs) {
+               blkid_dev *tmp = list_entry(p, blkid_dev, bid_devs);
+
+               if (strcmp(tmp->bid_name, devname))
+                       continue;
+
+               DEB_DEV("found devname %s in cache\n", tmp->bid_name);
+               dev = blkid_verify_devname(cache, tmp);
+               break;
+       }
+
+       return dev;
+}
+
+/*
+ * Return a pointer to an dev struct, either from cache or by probing.
+ */
+blkid_dev *blkid_get_devname(blkid_cache *cache, const char *devname)
+{
+       blkid_dev *dev;
+
+       if ((dev = blkid_find_devname(cache, devname)))
+               return dev;
+
+       dev = blkid_devname_to_dev(devname, 0);
+       return blkid_add_dev_to_cache(cache, dev);
+}
+
+/*
+ * Probe a single block device to add to the device cache.
+ * If the size is not specified, it will be found in blkid_devname_to_dev().
+ */
+static blkid_dev *probe_one(blkid_cache *cache, const char *ptname,
+                           int major, int minor, unsigned long long size)
+{
+       dev_t devno = makedev(major, minor);
+       blkid_dev *dev;
+       const char **dir;
+       char *devname = NULL;
+
+       /* See if we already have this device number in the cache. */
+       if ((dev = blkid_find_devno(cache, devno)))
+               return 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 = devdirs; *dir; dir++) {
+               struct stat st;
+               char device[256];
+
+               sprintf(device, "%s/%s", *dir, ptname);
+               if ((dev = blkid_find_devname(cache, device)) &&
+                   dev->bid_devno == devno)
+                       return dev;
+
+               if (stat(device, &st) == 0 && st.st_rdev == devno) {
+                       devname = string_copy(device);
+                       break;
+               }
+       }
+       if (!devname) {
+               devname = blkid_devno_to_devname(devno);
+               if (!devname)
+                       return NULL;
+       }
+       dev = blkid_devname_to_dev(devname, size);
+       string_free(devname);
+
+       return blkid_add_dev_to_cache(cache, dev);
+}
+
+#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
+#include <dirent.h>
+static int lvm_get_devno(const char *lvm_device, int *major, int *minor,
+                        blkid_loff_t *size)
+{
+       FILE *lvf;
+       char buf[1024];
+       int ret = 1;
+
+       *major = *minor = 0;
+       *size = 0;
+
+       DEB_DEV("opening %s\n", lvm_device);
+       if ((lvf = fopen(lvm_device, "r")) == NULL) {
+               ret = errno;
+               DEB_DEV("%s: (%d) %s\n", lvm_device, ret, strerror(ret));
+               return -ret;
+       }
+
+       while (fgets(buf, sizeof(buf), lvf)) {
+               if (sscanf(buf, "size: %Ld", size) == 1) { /* sectors */
+                       *size <<= 9;
+               }
+               if (sscanf(buf, "device: %d:%d", major, minor) == 2) {
+                       ret = 0;
+                       break;
+               }
+       }
+       fclose(lvf);
+
+       return ret;
+}
+
+static void lvm_probe_all(blkid_cache **cache)
+{
+       DIR             *vg_list;
+       struct dirent   *vg_iter;
+       int             vg_len = strlen(VG_DIR);
+
+       if ((vg_list = opendir(VG_DIR)) == NULL)
+               return;
+
+       DEB_DEV("probing LVM devices under %s\n", VG_DIR);
+
+       while ((vg_iter = readdir(vg_list)) != NULL) {
+               DIR             *lv_list;
+               char            *vdirname;
+               char            *vg_name;
+               struct dirent   *lv_iter;
+
+               vg_name = vg_iter->d_name;
+               if (!strcmp(vg_name, ".") || !strcmp(vg_name, ".."))
+                       continue;
+               vdirname = malloc(vg_len + strlen(vg_name) + 8);
+               if (!vdirname)
+                       goto exit;
+               sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name);
+
+               lv_list = opendir(vdirname);
+               free(vdirname);
+               if (lv_list == NULL)
+                       continue;
+
+               while ((lv_iter = readdir(lv_list)) != NULL) {
+                       char            *lv_name, *lvm_device;
+                       int             major, minor;
+                       blkid_loff_t    size;
+
+                       lv_name = lv_iter->d_name;
+                       if (!strcmp(lv_name, ".") || !strcmp(lv_name, ".."))
+                               continue;
+
+                       lvm_device = malloc(vg_len + strlen(vg_name) +
+                                           strlen(lv_name) + 8);
+                       if (!lvm_device) {
+                               closedir(lv_list);
+                               goto exit;
+                       }
+                       sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name,
+                               lv_name);
+                       if (lvm_get_devno(lvm_device, &major, &minor, &size)) {
+                               free(lvm_device);
+                               continue;
+                       }
+                       sprintf(lvm_device, "%s/%s", vg_name, lv_name);
+                       DEB_DEV("LVM dev %s: devno 0x%02X%02X, size %Ld\n",
+                               lvm_device, major, minor, size);
+                       probe_one(*cache, lvm_device, major, minor, size);
+                       free(lvm_device);
+               }
+               closedir(lv_list);
+       }
+exit:
+       closedir(vg_list);
+}
+#endif
+
+/*
+ * Read the device data for all available block devices in the system.
+ */
+int blkid_probe_all(blkid_cache **cache)
+{
+       FILE *proc;
+       int firstPass;
+
+       if (!cache)
+               return -BLKID_ERR_PARAM;
+
+       if (!*cache)
+               *cache = blkid_new_cache();
+
+       if (!*cache)
+               return -BLKID_ERR_MEM;
+
+       if ((*cache)->bic_flags & BLKID_BIC_FL_PROBED &&
+           time(0) - (*cache)->bic_time < BLKID_PROBE_INTERVAL)
+               return 0;
+
+#ifdef VG_DIR
+       lvm_probe_all(cache);
+#endif
+
+       proc = fopen(PROC_PARTITIONS, "r");
+       if (!proc)
+               return -BLKID_ERR_PROC;
+
+       for (firstPass = 1; firstPass >= 0; firstPass--) {
+               char line[1024];
+               char ptname0[128], ptname1[128];
+               char *ptnames[2] = { ptname0, ptname1 };
+               int majors[2], minors[2];
+               unsigned long long sizes[2];
+               int lens[2] = { 0, 0 };
+               int handleOnFirst;
+               int which = 0, last = 0;
+
+               fseek(proc, 0, SEEK_SET);
+
+               while (fgets(line, sizeof(line), proc)) {
+                       last = which;
+                       which ^= 1;
+
+                       if (sscanf(line, " %d %d %Ld %128[^\n ]",
+                                  &majors[which], &minors[which],
+                                  &sizes[which], ptnames[which]) != 4)
+                               continue;
+
+                       DEB_DEV("read partition name %s\n", ptnames[which]);
+
+                       /* look only at md devices on first pass */
+                       handleOnFirst = !strncmp(ptnames[which], "md", 2);
+                       if (firstPass != handleOnFirst)
+                               continue;
+
+                       /* Skip whole disk devs unless they have no partitions
+                        * If we don't have a partition on this dev, also
+                        * check previous dev to see if it didn't have a partn.
+                        * heuristic: partition name ends in a digit.
+                        *
+                        * Skip extended partitions.
+                        * heuristic: size is 1
+                        *
+                        * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs
+                        */
+
+                       lens[which] = strlen(ptnames[which]);
+                       if (isdigit(ptnames[which][lens[which] - 1])) {
+                               DEB_DEV("partition dev %s, devno 0x%02X%02X\n",
+                                       ptnames[which], majors[which],
+                                       minors[which]);
+
+                               if (sizes[which] > 1)
+                                       probe_one(*cache, ptnames[which],
+                                                 majors[which], minors[which],
+                                                 sizes[which] << 10);
+                               lens[which] = 0;
+                               lens[last] = 0;
+                       } else if (lens[last] &&
+                                  strncmp(ptnames[last], ptnames[which],
+                                          lens[last])) {
+                               DEB_DEV("whole dev %s, devno 0x%02X%02X\n",
+                                       ptnames[last], majors[last],
+                                       minors[last]);
+                               probe_one(*cache, ptnames[last], majors[last],
+                                         minors[last], sizes[last] << 10);
+                               lens[last] = 0;
+                       }
+               }
+
+               /* Handle the last device if it wasn't partitioned */
+               if (lens[which])
+                       probe_one(*cache, ptnames[which], majors[which],
+                                 minors[which], sizes[which] << 10);
+       }
+       fclose(proc);
+
+       (*cache)->bic_time = time(0);
+       (*cache)->bic_flags |= BLKID_BIC_FL_PROBED;
+
+       return 0;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+       blkid_cache *cache = NULL;
+
+       if (argc != 1) {
+               fprintf(stderr, "Usage: %s\n"
+                       "Probe all devices and exit\n", argv[0]);
+               exit(1);
+       }
+       if (blkid_probe_all(&cache) < 0)
+               printf("%s: error probing devices\n", argv[0]);
+
+       blkid_free_cache(cache);
+       return (0);
+}
+#endif
diff --git a/lib/blkid/devno.c b/lib/blkid/devno.c
new file mode 100644 (file)
index 0000000..56f754d
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * devno.c - find a particular device by its device number (major/minor)
+ *
+ * Copyright (C) 2000, 2001 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>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <dirent.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+
+#include "blkid/blkid.h"
+
+#ifdef DEBUG_DEVNO
+#define DEB_DEVNO(fmt, arg...) printf("devno: " fmt, ## arg)
+#else
+#define DEB_DEVNO(fmt, arg...) do {} while (0)
+#endif
+
+struct dir_list {
+       char    *name;
+       struct dir_list *next;
+};
+
+char *stringn_copy(const char *s, const int length)
+{
+       char *ret;
+
+       if (!s)
+               return NULL;
+
+       ret = malloc(length + 1);
+       if (ret) {
+               strncpy(ret, s, length);
+               ret[length] = '\0';
+       }
+       return ret;
+}
+
+char *string_copy(const char *s)
+{
+       if (!s)
+               return NULL;
+
+       return stringn_copy(s, strlen(s));
+}
+
+void string_free(char *s)
+{
+       if (s)
+               free(s);
+}
+
+/*
+ * This function adds an entry to the directory list
+ */
+static void add_to_dirlist(const char *name, struct dir_list **list)
+{
+       struct dir_list *dp;
+
+       dp = malloc(sizeof(struct dir_list));
+       if (!dp)
+               return;
+       dp->name = string_copy(name);
+       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;
+               string_free(dp->name);
+               free(dp);
+       }
+       *list = NULL;
+}
+
+static int scan_dir(char *dirname, dev_t devno, struct dir_list **list,
+                   char **devname)
+{
+       DIR     *dir;
+       struct dirent *dp;
+       char    path[1024];
+       int     dirlen;
+       struct stat st;
+       int     ret = 0;
+
+       dirlen = strlen(dirname);
+       if ((dir = opendir(dirname)) == NULL)
+               return errno;
+       dp = readdir(dir);
+       while (dp) {
+               if (dirlen + strlen(dp->d_name) + 2 >= sizeof(path))
+                       goto skip_to_next;
+
+               if (dp->d_name[0] == '.' &&
+                   ((dp->d_name[1] == 0) ||
+                    ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
+                       goto skip_to_next;
+
+               sprintf(path, "%s/%s", dirname, dp->d_name);
+               if (stat(path, &st) < 0)
+                       goto skip_to_next;
+
+               if (S_ISDIR(st.st_mode))
+                       add_to_dirlist(path, list);
+               else if (S_ISBLK(st.st_mode) && st.st_rdev == devno) {
+                       *devname = string_copy(path);
+                       DEB_DEVNO("found 0x%Lx at %s (%p)\n", devno,
+                                *devname, *devname);
+                       if (!*devname)
+                               ret = -BLKID_ERR_MEM;
+                       break;
+               }
+       skip_to_next:
+               dp = readdir(dir);
+       }
+       closedir(dir);
+       return ret;
+}
+
+/* Directories where we will try to search for device numbers */
+const char *devdirs[] = { "/dev", "/devfs", "/devices", NULL };
+
+/*
+ * This function finds the pathname to a block device with a given
+ * device number.  It returns a pointer to allocated memory to the
+ * pathname on success, and NULL on failure.
+ */
+char *blkid_devno_to_devname(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++)
+               /* go to end of list */;
+
+       do {
+               --dir;
+               add_to_dirlist(*dir, &list);
+       } while (dir != devdirs);
+
+       while (list) {
+               struct dir_list *current = list;
+
+               list = list->next;
+               DEB_DEVNO("directory %s\n", current->name);
+               scan_dir(current->name, devno, &new_list, &devname);
+               string_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);
+
+       if (!devname)
+               fprintf(stderr, "blkid: couldn't find devno 0x%04Lx\n", devno);
+       else
+               DEB_DEVNO("found devno 0x%04Lx as %s\n", devno, devname);
+
+       return devname;
+}
+
+blkid_dev *blkid_find_devno(blkid_cache *cache, dev_t devno)
+{
+       blkid_dev *dev = NULL;
+       struct list_head *p, *n;
+
+       if (!cache)
+               return NULL;
+
+       /* This cannot be a standard list_for_each() because we may be
+        * deleting the referenced struct in blkid_verify_devname() and
+        * pointing to another one that was probed from disk, and "p"
+        * would point to freed memory.
+        */
+       list_for_each_safe(p, n, &cache->bic_devs) {
+               blkid_dev *tmp = list_entry(p, blkid_dev, bid_devs);
+               if (tmp->bid_devno != devno)
+                       continue;
+
+               tmp = blkid_verify_devname(cache, tmp);
+               if (!tmp || tmp->bid_devno != devno)
+                       continue;
+
+               dev = tmp;
+               break;
+       }
+
+       if (dev)
+               DEB_DEVNO("found devno 0x%04LX in cache as %s\n",
+                         devno, dev->bid_name);
+
+       return dev;
+}
+
+blkid_dev *blkid_get_devno(blkid_cache *cache, dev_t devno)
+{
+       char *devname;
+       blkid_dev *dev;
+
+       if (!(dev = blkid_find_devno(cache, devno)) &&
+           (devname = blkid_devno_to_devname(devno))) {
+               dev = blkid_get_devname(cache, devname);
+               string_free(devname);
+       }
+
+       return dev;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char** argv)
+{
+       char    *devname, *tmp;
+       int     major, minor;
+       dev_t   devno;
+       const char *errmsg = "Couldn't parse %s: %s\n";
+
+       if ((argc != 2) && (argc != 3)) {
+               fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n"
+                       "Resolve a device number to a device name\n",
+                       argv[0], argv[0]);
+               exit(1);
+       }
+       if (argc == 2) {
+               devno = strtoul(argv[1], &tmp, 0);
+               if (*tmp) {
+                       fprintf(stderr, errmsg, "device number", argv[1]);
+                       exit(1);
+               }
+       } else {
+               major = strtoul(argv[1], &tmp, 0);
+               if (*tmp) {
+                       fprintf(stderr, errmsg, "major number", argv[1]);
+                       exit(1);
+               }
+               minor = strtoul(argv[2], &tmp, 0);
+               if (*tmp) {
+                       fprintf(stderr, errmsg, "minor number", argv[2]);
+                       exit(1);
+               }
+               devno = makedev(major, minor);
+       }
+       printf("Looking for device 0x%04Lx\n", devno);
+       devname = blkid_devno_to_devname(devno);
+       if (devname)
+               string_free(devname);
+       return 0;
+}
+#endif
diff --git a/lib/blkid/getsize.c b/lib/blkid/getsize.c
new file mode 100644 (file)
index 0000000..4489e05
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * getsize.c --- get the size of a partition.
+ *
+ * Copyright (C) 1995, 1995 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+
+#include <stdio.h>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_LINUX_FD_H
+#include <sys/ioctl.h>
+#include <linux/fd.h>
+#endif /* HAVE_LINUX_FD_H */
+#ifdef HAVE_SYS_DISKLABEL_H
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+#include "blkid/blkid.h"
+
+#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
+#define BLKGETSIZE _IO(0x12,96)        /* return device size */
+#endif
+
+static int valid_offset(int fd, blkid_loff_t offset)
+{
+       char ch;
+
+       if (blkid_llseek(fd, offset, 0) < 0)
+               return 0;
+       if (read(fd, &ch, 1) < 1)
+               return 0;
+       return 1;
+}
+
+/*
+ * Returns the number of blocks in a partition
+ */
+blkid_loff_t blkid_get_dev_size(int fd)
+{
+#ifdef BLKGETSIZE
+       unsigned long size;
+#endif
+       blkid_loff_t high, low;
+#ifdef FDGETPRM
+       struct floppy_struct this_floppy;
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+       int part;
+       struct disklabel lab;
+       struct partition *pp;
+       char ch;
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+#ifdef BLKGETSIZE
+       if (ioctl(fd, BLKGETSIZE, &size) >= 0)
+               return (blkid_loff_t)size << 9;
+#endif
+#ifdef FDGETPRM
+       if (ioctl(fd, FDGETPRM, &this_floppy) >= 0)
+               return (blkid_loff_t)this_floppy.size << 9;
+#endif
+#ifdef HAVE_SYS_DISKLABEL_H
+       part = strlen(file) - 1;
+       if (part >= 0) {
+               ch = file[part];
+               if (isdigit(ch))
+                       part = 0;
+               else if (ch >= 'a' && ch <= 'h')
+                       part = ch - 'a';
+               else
+                       part = -1;
+       }
+       if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+               pp = &lab.d_partitions[part];
+               if (pp->p_size)
+                       return pp->p_size << 9;
+       }
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+       /*
+        * OK, we couldn't figure it out by using a specialized ioctl,
+        * which is generally the best way.  So do binary search to
+        * find the size of the partition.
+        */
+       low = 0;
+       for (high = 1024; valid_offset(fd, high); high *= 2)
+               low = high;
+       while (low < high - 1)
+       {
+               const blkid_loff_t mid = (low + high) / 2;
+
+               if (valid_offset(fd, mid))
+                       low = mid;
+               else
+                       high = mid;
+       }
+       return low + 1;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+       blkid_loff_t bytes;
+       int     fd;
+
+       if (argc < 2) {
+               fprintf(stderr, "Usage: %s device\n"
+                       "Determine the size of a device\n", argv[0]);
+               return 1;
+       }
+
+       if ((fd = open(argv[1], O_RDONLY)) < 0)
+               perror(argv[0]);
+
+       bytes = blkid_get_dev_size(fd);
+       printf("Device %s has %Ld 1k blocks.\n", argv[1], bytes >> 10);
+
+       return 0;
+}
+#endif
diff --git a/lib/blkid/libblkid.3.in b/lib/blkid/libblkid.3.in
new file mode 100644 (file)
index 0000000..23938d1
--- /dev/null
@@ -0,0 +1,96 @@
+.\" Copyright 2001 Andreas Dilger (adilger@turbolinux.com)
+.\"
+.\" This man page was created for libblkid.so.1.0 from e2fsprogs-1.24.
+.\" 
+.\" This file may be copied under the terms of the GNU Public License.
+.\" 
+.\" Created  Wed Sep 14 12:02:12 2001, Andreas Dilger
+.TH LIBBLKID 3 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+libblkid \- block device identification library
+.SH SYNOPSIS
+.B #include <blkid/blkid.h>
+.sp
+.B cc
+.I file.c
+.B \-lblkid
+.SH DESCRIPTION
+The
+.B libblkid
+library is used to identify block devices (disks) as to their content (e.g.
+filesystem type) as well as extracting additional information such as
+filesystem labels/volume names, unique identifiers/serial numbers, etc.
+A common use is to allow use of LABEL= and UUID= tags instead of hard-coding
+specific block device names into configuration files.
+.P
+Block device information is normally kept in a cache file
+.I /etc/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.
+.P
+Functions with "get" in their name will first search the cache (if given)
+for the specified data, and failing that will search visible block devices
+for this information.  Functions with "find" in their name will only search
+the existing cache data for the specified device.
+.P
+In situations where one is getting information about a single known device,
+it does not impact performance whether the cache is used or not (unless you
+are not able to read the block device directly).  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.
+.P
+In some cases (modular kernels), block devices are not even visible until
+after they are accessed the first time, so it is critical that there is
+some way to locate these devices without enumerating only visible devices,
+so the use of the cache file is
+.B required
+in this situation.
+.SH AUTHOR
+.B libblkid
+was written by Andreas Dilger for the ext2 filesystem utilties, with input
+from Ted Ts'o.
+.SH FILES
+.TP
+.I /etc/blkid.tab 
+Caches data extracted from each recognized block device.
+.SH AVAILABILITY
+.B libblkid
+is part of the e2fsprogs package since version 1.25 and is available from
+http://e2fsprogs.sourceforge.net.
+.SH COPYING
+.B libblkid
+is available under the terms of the GNU Library General Public License (LGPL),
+version 2 (or at your discretion any later version).  A copy of the LGPL
+should be included with this library in the file COPYING.  If not, write to
+.RS
+Free Software Foundation, Inc.
+.br
+59 Temple Place
+.br
+Suite 330
+.br
+Boston, MA  02111-1307  USA
+.RE
+.PP
+or visit
+.UR http://www.gnu.org/licenses/licenses.html#LGPL
+http://www.gnu.org/licenses/licenses.html#LGPL
+.UE
+.SH "SEE ALSO"
+.BR blkid_read_cache (3),
+.BR blkid_save_cache (3),
+.BR blkid_free_cache (3),
+.BR blkid_get_devname (3),
+.BR blkid_get_devno (3),
+.BR blkid_probe_all (3),
+.BR blkid_token_to_tag (3),
+.BR blkid_free_dev (3),
+.BR blkid_find_tag_dev (3),
+.BR blkid_find_tag_cache (3),
+.BR blkid_get_tag_cache (3),
+.BR blkid_get_tagname_devname (3),
+.BR blkid_get_token (3),
+.BR blkid.tab (7)
diff --git a/lib/blkid/list.h b/lib/blkid/list.h
new file mode 100644 (file)
index 0000000..7800a46
--- /dev/null
@@ -0,0 +1,171 @@
+#if !defined(_BLKID_LIST_H) && !defined(LIST_HEAD)
+#define _BLKID_LIST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+       (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * add,
+       struct list_head * prev,
+       struct list_head * next)
+{
+       next->prev = add;
+       add->next = next;
+       add->prev = prev;
+       prev->next = add;
+}
+
+/**
+ * list_add - add a new entry
+ * @add:       new entry to be added
+ * @head:      list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static __inline__ void list_add(struct list_head *add, struct list_head *head)
+{
+       __list_add(add, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @add:       new entry to be added
+ * @head:      list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static __inline__ void list_add_tail(struct list_head *add, struct list_head *head)
+{
+       __list_add(add, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+                                 struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry:     the element to delete from the list.
+ *
+ * list_empty() on @entry does not return true after this, @entry is
+ * in an undefined state.
+ */
+static __inline__ void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry:     the element to delete from the list.
+ */
+static __inline__ void list_del_init(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head:      the list to test.
+ */
+static __inline__ int list_empty(struct list_head *head)
+{
+       return head->next == head;
+}
+
+/**
+ * list_splice - join two lists
+ * @list:      the new list to add.
+ * @head:      the place to add it in the first list.
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+       struct list_head *first = list->next;
+
+       if (first != list) {
+               struct list_head *last = list->prev;
+               struct list_head *at = head->next;
+
+               first->prev = head;
+               head->next = first;
+
+               last->next = at;
+               at->prev = last;
+       }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+       ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each - iterate over elements in a list
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @head:      the head for your list.
+ */
+#define list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe - iterate over elements in a list, but don't dereference
+ *                      pos after the body is done (in case it is freed)
+ * @pos:       the &struct list_head to use as a loop counter.
+ * @pnext:     the &struct list_head to use as a pointer to the next item.
+ * @head:      the head for your list (not included in iteration).
+ */
+#define list_for_each_safe(pos, pnext, head) \
+       for (pos = (head)->next, pnext = pos->next; pos != (head); \
+            pos = pnext, pnext = pos->next)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BLKID_LIST_H */
diff --git a/lib/blkid/llseek.c b/lib/blkid/llseek.c
new file mode 100644 (file)
index 0000000..67d9df7
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * llseek.c -- stub calling the llseek system call
+ *
+ * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef __MSDOS__
+#include <io.h>
+#endif
+
+#include "blkid/blkid.h"
+
+#ifdef __linux__
+
+#if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE)
+
+#define my_llseek lseek64
+
+#elif defined(HAVE_LLSEEK)
+#include <syscall.h>
+
+#ifndef HAVE_LLSEEK_PROTOTYPE
+extern long long llseek(int fd, long long offset, int origin);
+#endif
+
+#define my_llseek llseek
+
+#else  /* ! HAVE_LLSEEK */
+
+#if defined(__alpha__) || defined(__ia64__)
+
+#define llseek lseek
+
+#else /* !__alpha__ && !__ia64__*/
+
+#include <linux/unistd.h>
+
+#ifndef __NR__llseek
+#define __NR__llseek            140
+#endif
+
+#ifndef __i386__
+static int _llseek(unsigned int, unsigned long, unsigned long,
+                  blkid_loff_t *, unsigned int);
+
+static _syscall5(int, _llseek, unsigned int, fd, unsigned long, offset_high,
+                unsigned long, offset_low, blkid_loff_t *, result,
+                unsigned int, origin)
+#endif
+
+static blkid_loff_t my_llseek(int fd, blkid_loff_t offset, int origin)
+{
+       blkid_loff_t result;
+       int retval;
+
+#ifndef __i386__
+       retval = _llseek(fd, ((unsigned long long) offset) >> 32,
+                        ((unsigned long long)offset) & 0xffffffff,
+                        &result, origin);
+#else
+       retval = syscall(__NR__llseek, fd, ((unsigned long long) offset) >> 32,
+                        ((unsigned long long)offset) & 0xffffffff,
+                        &result, origin);
+#endif
+       return (retval == -1 ? (blkid_loff_t) retval : result);
+}
+
+#endif /* __alpha__ || __ia64__ */
+
+#endif /* HAVE_LLSEEK */
+
+blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence)
+{
+       blkid_loff_t result;
+       static int do_compat = 0;
+
+       if ((sizeof(off_t) >= sizeof(blkid_loff_t)) ||
+           (offset < ((blkid_loff_t) 1 << ((sizeof(off_t)*8) -1))))
+               return lseek(fd, (off_t) offset, whence);
+
+       if (do_compat) {
+               errno = EOVERFLOW;
+               return -1;
+       }
+
+       result = my_llseek(fd, offset, whence);
+       if (result == -1 && errno == ENOSYS) {
+               /*
+                * Just in case this code runs on top of an old kernel
+                * which does not support the llseek system call
+                */
+               do_compat++;
+               errno = EOVERFLOW;
+       }
+       return result;
+}
+
+#else /* !linux */
+
+#ifndef EOVERFLOW
+#ifdef EXT2_ET_INVALID_ARGUMENT
+#define EOVERFLOW EXT2_ET_INVALID_ARGUMENT
+#else
+#define EOVERFLOW 112
+#endif
+#endif
+
+blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int origin)
+{
+       if ((sizeof(off_t) < sizeof(blkid_loff_t)) &&
+           (offset >= ((blkid_loff_t) 1 << ((sizeof(off_t)*8) - 1)))) {
+               errno = EOVERFLOW;
+               return -1;
+       }
+       return lseek(fd, (off_t) offset, origin);
+}
+
+#endif /* linux */
+
+
diff --git a/lib/blkid/probe.c b/lib/blkid/probe.c
new file mode 100644 (file)
index 0000000..d58e050
--- /dev/null
@@ -0,0 +1,838 @@
+/*
+ * probe.c - identify a block device by its contents, and return a dev
+ *           struct with the details
+ *
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000 by Theodore Ts'o
+ * Copyright (C) 2001 by 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>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "blkid/blkid.h"
+#include "uuid/uuid.h"
+#include "probe.h"
+
+/* #define DEBUG_PROBE */
+#ifdef DEBUG_PROBE
+#define DEB_PROBE(fmt, arg...) printf("probe: " fmt, ## arg)
+#else
+#define DEB_PROBE(fmt, arg...) do {} while (0)
+#endif
+
+/*
+ * Do the required things for instantiating a new device.  This is called if
+ * there is nor a probe handler for a filesystem type, and is also called by
+ * the filesystem-specific types to do common initialization tasks.
+ *
+ * The devname, dev_p, and id fields are required.  The buf is
+ * a buffer to return superblock data in.
+ */
+static int probe_default(int fd, blkid_dev **dev_p, const char *devname,
+                        struct blkid_magic *id, unsigned char *buf,
+                        blkid_loff_t size)
+{
+       blkid_loff_t offset;
+       blkid_dev *dev;
+       struct stat st;
+       int ret;
+
+       if (!devname || !dev_p || !id || !buf || fd < 0)
+               return -BLKID_ERR_PARAM;
+
+       if (fstat(fd, &st) < 0 || !S_ISBLK(st.st_mode))
+               return -BLKID_ERR_DEV;
+
+       offset = (blkid_loff_t)id->bim_kboff << 10;
+       if (id->bim_kboff < 0)
+               offset = (size & ~((blkid_loff_t)id->bim_align - 1)) + offset;
+
+       if (blkid_llseek(fd, offset, 0) < 0 ||
+           read(fd, buf, id->bim_kbsize << 10) != id->bim_kbsize << 10)
+               return -BLKID_ERR_IO;
+
+       /* Revalidate magic for blkid_validate_devname */
+       if (memcmp(id->bim_magic, buf + id->bim_sboff, id->bim_len))
+               return -BLKID_ERR_PARAM;
+
+       dev = blkid_new_dev();
+       if (!dev)
+               return -BLKID_ERR_MEM;
+
+       dev->bid_name = string_copy(devname);
+       if (!dev->bid_name) {
+               ret = -BLKID_ERR_MEM;
+               goto exit_dev;
+       }
+
+       /* Don't set this until there is no chance of error */
+       *dev_p = dev;
+       dev->bid_devno = st.st_rdev;
+       dev->bid_devsize = size;
+       dev->bid_time = time(0);
+       dev->bid_flags |= BLKID_BID_FL_VERIFIED;
+
+       if (id->bim_type)
+               blkid_create_tag(dev, NULL, "TYPE", id->bim_type,
+                                strlen(id->bim_type));
+
+       DEB_PROBE("%s: devno 0x%04Lx, type %s\n", devname,
+                 st.st_rdev, id->bim_type);
+
+       return 0;
+exit_dev:
+       blkid_free_dev(dev);
+       return ret;
+}
+
+static int probe_ext2(int fd, blkid_dev **dev_p, const char *devname,
+                     struct blkid_magic *id, unsigned char *buf,
+                     blkid_loff_t size)
+{
+       blkid_dev *dev;
+       struct ext2_super_block *es;
+       int ret;
+
+       if ((ret = probe_default(fd, &dev, devname, id, buf, size)) < 0)
+               return ret;
+
+       es = (struct ext2_super_block *)buf;
+
+       DEB_PROBE("size = %Ld, ext2_sb.compat = %08X:%08X:%08X\n", size,
+                 le32_to_cpu(es->s_feature_compat),
+                 le32_to_cpu(es->s_feature_incompat),
+                 le32_to_cpu(es->s_feature_ro_compat));
+
+       /* Make sure we don't keep re-probing as ext2 for a journaled fs */
+       if (!strcmp(id->bim_type, "ext2") &&
+           (le32_to_cpu(es->s_feature_compat) &
+            EXT3_FEATURE_COMPAT_HAS_JOURNAL ||
+            le32_to_cpu(es->s_feature_incompat) &
+            EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
+               blkid_free_dev(dev);
+               return -BLKID_ERR_PARAM;
+       }
+
+       /* Don't set this until there is no chance of error */
+       *dev_p = dev;
+
+       dev->bid_size = (blkid_loff_t)le32_to_cpu(es->s_blocks_count) <<
+               (le32_to_cpu(es->s_log_block_size) + 10);
+
+       /* This is a safe (minimum) number, as it ignores metadata usage. */
+       dev->bid_free = (blkid_loff_t)le32_to_cpu(es->s_free_blocks_count) <<
+               (le32_to_cpu(es->s_log_block_size) + 10);
+
+       if (strlen(es->s_volume_name)) {
+               blkid_create_tag(dev, NULL, "LABEL", es->s_volume_name,
+                                sizeof(es->s_volume_name));
+       }
+
+       if (!uuid_is_null(es->s_uuid)) {
+               unsigned char uuid[37];
+               uuid_unparse(es->s_uuid, uuid);
+               blkid_create_tag(dev, NULL, "UUID", uuid, sizeof(uuid));
+       }
+
+       return 0;
+}
+
+static int probe_jbd(int fd, blkid_dev **dev_p, const char *devname,
+                    struct blkid_magic *id, unsigned char *buf,
+                    blkid_loff_t size)
+{
+       blkid_dev *dev;
+       struct ext2_super_block *es;
+       int ret;
+
+       if ((ret = probe_ext2(fd, &dev, devname, id, buf, size)) < 0)
+               return ret;
+
+       es = (struct ext2_super_block *)buf;
+
+       if (!(le32_to_cpu(es->s_feature_incompat) &
+             EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
+               blkid_free_dev(dev);
+               return -BLKID_ERR_PARAM;
+       }
+
+       /* Don't set this until there is no chance of error */
+       *dev_p = dev;
+       return 0;
+}
+
+static int probe_ext3(int fd, blkid_dev **dev_p, const char *devname,
+                    struct blkid_magic *id, unsigned char *buf,
+                    blkid_loff_t size)
+{
+       blkid_dev *dev;
+       struct ext2_super_block *es;
+       int ret;
+
+       if ((ret = probe_ext2(fd, &dev, devname, id, buf, size)) < 0)
+               return ret;
+
+       es = (struct ext2_super_block *)buf;
+
+       if (!(le32_to_cpu(es->s_feature_compat) &
+             EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
+               blkid_free_dev(dev);
+               *dev_p = NULL;
+               return -BLKID_ERR_PARAM;
+       }
+       /* Don't set this until there is no chance of error */
+       *dev_p = dev;
+
+       if (!(le32_to_cpu(es->s_feature_incompat) &
+             EXT3_FEATURE_INCOMPAT_RECOVER)) {
+               blkid_create_tag(dev, NULL, "TYPE", "ext2", 4);
+               dev->bid_flags |= BLKID_BID_FL_MTYPE;
+       }
+
+       return 0;
+}
+
+static int probe_vfat(int fd, blkid_dev **dev_p, const char *devname,
+                     struct blkid_magic *id, unsigned char *buf,
+                     blkid_loff_t size)
+{
+       blkid_dev *dev;
+       struct vfat_super_block *vs;
+       char serno[10];
+       blkid_loff_t sectors;
+       int cluster_size;
+       int ret;
+
+       if ((ret = probe_default(fd, &dev, devname, id, buf, size)) < 0)
+               return ret;
+
+       vs = (struct vfat_super_block *)buf;
+
+       /* Don't set this until there is no chance of error */
+       *dev_p = dev;
+
+       sectors = ((vs->vs_sectors[1] << 8) | vs->vs_sectors[0]);
+       if (sectors == 0)
+               sectors = vs->vs_total_sect;
+       cluster_size = ((vs->vs_sector_size[1] << 8) | vs->vs_sector_size[0]);
+       dev->bid_size = sectors * cluster_size;
+       DEB_PROBE("%Ld %d byte sectors\n", sectors, cluster_size);
+
+       if (strncmp(vs->vs_label, "NO NAME", 7)) {
+               unsigned char *end = vs->vs_label + sizeof(vs->vs_label) - 1;
+
+               while (*end == ' ' && end >= vs->vs_label)
+                       --end;
+               if (end >= vs->vs_label)
+                       blkid_create_tag(dev, NULL, "LABEL", vs->vs_label,
+                                        end - vs->vs_label + 1);
+       }
+
+       /* We can't just print them as %04X, because they are unaligned */
+       sprintf(serno, "%02X%02X-%02X%02X", vs->vs_serno[3], vs->vs_serno[2],
+               vs->vs_serno[1], vs->vs_serno[0]);
+       blkid_create_tag(dev, NULL, "UUID", serno, sizeof(serno));
+
+       return 0;
+}
+
+static int probe_msdos(int fd, blkid_dev **dev_p, const char *devname,
+                      struct blkid_magic *id, unsigned char *buf,
+                      blkid_loff_t size)
+{
+       blkid_dev *dev;
+       struct msdos_super_block *ms;
+       char serno[10];
+       int cluster_size;
+       blkid_loff_t sectors;
+       int ret;
+
+       if ((ret = probe_default(fd, &dev, devname, id, buf, size)) < 0)
+               return ret;
+
+       ms = (struct msdos_super_block *)buf;
+
+       /* Don't set this until there is no chance of error */
+       *dev_p = dev;
+
+       sectors = ((ms->ms_sectors[1] << 8) | ms->ms_sectors[0]);
+       if (sectors == 0)
+               sectors = ms->ms_total_sect;
+       cluster_size = ((ms->ms_sector_size[1] << 8) | ms->ms_sector_size[0]);
+       dev->bid_size = sectors * cluster_size;
+       DEB_PROBE("%Ld %d byte sectors\n", sectors, cluster_size);
+
+       if (strncmp(ms->ms_label, "NO NAME", 7)) {
+               unsigned char *end = ms->ms_label + sizeof(ms->ms_label) - 1;
+
+               while (*end == ' ' && end >= ms->ms_label)
+                       --end;
+               if (end >= ms->ms_label)
+                       blkid_create_tag(dev, NULL, "LABEL", ms->ms_label,
+                                        end - ms->ms_label + 1);
+       }
+
+       /* We can't just print them as %04X, because they are unaligned */
+       sprintf(serno, "%02X%02X-%02X%02X", ms->ms_serno[3], ms->ms_serno[2],
+               ms->ms_serno[1], ms->ms_serno[0]);
+       blkid_create_tag(dev, NULL, "UUID", serno, sizeof(serno));
+
+       return 0;
+}
+
+static int probe_xfs(int fd, blkid_dev **dev_p, const char *devname,
+                    struct blkid_magic *id, unsigned char *buf,
+                    blkid_loff_t size)
+{
+       blkid_dev *dev;
+       struct xfs_super_block *xs;
+       int ret;
+
+       if ((ret = probe_default(fd, &dev, devname, id, buf, size)) < 0)
+               return ret;
+
+       xs = (struct xfs_super_block *)buf;
+
+       /* Don't set this until there is no chance of error */
+       *dev_p = dev;
+       /* If the filesystem size is larger than the device, this is bad */
+       dev->bid_size = be64_to_cpu(xs->xs_dblocks) *
+               be32_to_cpu(xs->xs_blocksize);
+       dev->bid_free = be64_to_cpu(xs->xs_fdblocks) *
+               be32_to_cpu(xs->xs_blocksize);
+
+       if (strlen(xs->xs_fname))
+               blkid_create_tag(dev, NULL, "LABEL", xs->xs_fname,
+                                sizeof(xs->xs_fname));
+
+       if (!uuid_is_null(xs->xs_uuid)) {
+               char uuid[37];
+               uuid_unparse(xs->xs_uuid, uuid);
+               blkid_create_tag(dev, NULL, "UUID", uuid, sizeof(uuid));
+       }
+       return 0;
+}
+
+static int probe_reiserfs(int fd, blkid_dev **dev_p, const char *devname,
+                         struct blkid_magic *id, unsigned char *buf,
+                         blkid_loff_t size)
+{
+       blkid_dev *dev;
+       struct reiserfs_super_block *rs;
+       unsigned int blocksize;
+       int ret;
+
+       if ((ret = probe_default(fd, &dev, devname, id, buf, size)) < 0)
+               return ret;
+
+       rs = (struct reiserfs_super_block *)buf;
+
+       blocksize = le16_to_cpu(rs->rs_blocksize);
+
+       /* If the superblock is inside the journal, we have the wrong one */
+       if (id->bim_kboff/(blocksize>>10) > le32_to_cpu(rs->rs_journal_block)) {
+               blkid_free_dev(dev);
+               return -BLKID_ERR_BIG;
+       }
+
+       /* Don't set this until there is no chance of error */
+       *dev_p = dev;
+
+       /* If the filesystem size is larger than the device, this is bad */
+       dev->bid_size = le32_to_cpu(rs->rs_blocks_count) * blocksize;
+       dev->bid_free = le32_to_cpu(rs->rs_free_blocks) * blocksize;
+
+       /* LABEL/UUID are only valid for later versions of Reiserfs v3.6. */
+       if (!strcmp(id->bim_magic, "ReIsEr2Fs") ||
+           !strcmp(id->bim_magic, "ReIsEr3Fs")) {
+               if (strlen(rs->rs_label)) {
+                       blkid_create_tag(dev, NULL, "LABEL", rs->rs_label,
+                                        sizeof(rs->rs_label));
+               }
+
+               if (!uuid_is_null(rs->rs_uuid)) {
+                       unsigned char uuid[37];
+                       uuid_unparse(rs->rs_uuid, uuid);
+                       blkid_create_tag(dev, NULL, "UUID", uuid, sizeof(uuid));
+               }
+       }
+
+       return 0;
+}
+
+static int probe_minix(int fd, blkid_dev **dev_p, const char *devname,
+                      struct blkid_magic *id, unsigned char *buf,
+                      blkid_loff_t size)
+{
+       blkid_dev *dev;
+       struct minix_super_block *ms;
+       int ret;
+
+       if ((ret = probe_default(fd, &dev, devname, id, buf, size)) < 0)
+               return ret;
+
+       ms = (struct minix_super_block *)buf;
+
+       /* Don't set this until there is no chance of error */
+       *dev_p = dev;
+       dev->bid_size = ms->ms_nzones << ms->ms_log_zone_size;
+       return 0;
+}
+
+static int probe_swap(int fd, blkid_dev **dev_p, const char *devname,
+                     struct blkid_magic *id, unsigned char *buf,
+                     blkid_loff_t size)
+{
+       blkid_dev *dev;
+       struct swap_header *sh;
+       int psize;
+       int ret;
+
+       if ((ret = probe_default(fd, &dev, devname, id, buf, size)) < 0)
+               return ret;
+
+       /* PAGE_SIZE can be found by where the magic is located */
+       psize = (id->bim_kboff << 10) + (id->bim_sboff + 10);
+
+       /* Don't set this until there is no chance of error */
+       *dev_p = dev;
+
+       sh = (struct swap_header *)buf;
+       /* Is swap data in local endian format? */
+       dev->bid_size = (blkid_loff_t)(sh->sh_last_page + 1) * psize;
+
+       /* A label can not exist on the old (128MB max) swap format */
+       if (!strcmp(id->bim_magic, "SWAPSPACE2") && sh->sh_label[0]) {
+               blkid_create_tag(dev, NULL, "LABEL", sh->sh_label,
+                                sizeof(sh->sh_label));
+       }
+
+       return 0;
+}
+
+static int probe_mdraid(int fd, blkid_dev **dev_p, const char *devname,
+                       struct blkid_magic *id, unsigned char *buf,
+                       blkid_loff_t size)
+{
+       blkid_dev *dev;
+       struct mdp_superblock_s *md;
+       int ret;
+
+       if ((ret = probe_default(fd, &dev, devname, id, buf, size)) < 0)
+               return ret;
+
+       /* Don't set this until there is no chance of error */
+       *dev_p = dev;
+
+       md = (struct mdp_superblock_s *)buf;
+       /* What units is md->size in?  Assume 512-byte sectors? */
+       dev->bid_size = md->size * 512;
+
+       /* The MD UUID is not contiguous in the superblock, make it so */
+       if (md->set_uuid0 || md->set_uuid1 || md->set_uuid2 || md->set_uuid3) {
+               unsigned char md_uuid[16];
+               unsigned char uuid[37];
+
+               memcpy(md_uuid, &md->set_uuid0, 4);
+               memcpy(md_uuid + 4, &md->set_uuid1, 12);
+
+               uuid_unparse(md_uuid, uuid);
+               blkid_create_tag(dev, NULL, "UUID", uuid, sizeof(uuid));
+       }
+       return 0;
+}
+
+static int probe_hfs(int fd, blkid_dev **dev_p, const char *devname,
+                    struct blkid_magic *id, unsigned char *buf,
+                    blkid_loff_t size)
+{
+       blkid_dev *dev;
+       struct hfs_super_block *hfs;
+       int ret;
+
+       if ((ret = probe_default(fd, &dev, devname, id, buf, size)) < 0)
+               return ret;
+
+       hfs = (struct hfs_super_block *)buf;
+
+       if (be32_to_cpu(hfs->h_blksize) != 512)
+               return -BLKID_ERR_PARAM;
+
+       /* Don't set this until there is no chance of error */
+       *dev_p = dev;
+
+       return 0;
+}
+
+
+/*
+ * BLKID_BLK_OFFS is at least as large as the highest bim_kboff defined
+ * in the type_array table below + bim_kbalign.  If we ever start looking for magics
+ * relative to the end of a device, we can start using negative offsets
+ * in type_array.
+ */
+#define BLKID_BLK_BITS (10)
+#define BLKID_BLK_KBITS        (BLKID_BLK_BITS - 10)
+#define BLKID_BLK_SIZE (1024 << BLKID_BLK_KBITS)
+#define BLKID_BLK_MASK  (BLKID_BLK_SIZE - 1)
+#define BLKID_BLK_OFFS 128     /* currently MDRAID kboff + align */
+
+/*
+ * Various filesystem magics that we can check for.  Note that kboff and
+ * sboff are in kilobytes and bytes respectively.  All magics are in
+ * byte strings so we don't worry about endian issues.
+ */
+struct blkid_magic type_array[] = {
+/*  type     kboff   sboff len  magic           align kbsize probe */
+  { "MDRAID",  -64,      0,  4, "\251+N\374",   65536,  4, probe_mdraid },
+/*{ "LVM",       0,      0,  4, "HM\001\000",       1,  4, probe_lvm },*/
+  { "jbd",       1,   0x38,  2, "\123\357",         1,  1, probe_jbd },
+  { "ext3",      1,   0x38,  2, "\123\357",         1,  1, probe_ext3 },
+  { "ext2",      1,   0x38,  2, "\123\357",         1,  1, probe_ext2 },
+  { "reiserfs",  8,   0x34,  8, "ReIsErFs",         1,  1, probe_reiserfs },
+  { "reiserfs", 64,   0x34,  9, "ReIsEr2Fs",        1,  1, probe_reiserfs },
+  { "reiserfs", 64,   0x34,  9, "ReIsEr3Fs",        1,  1, probe_reiserfs },
+  { "reiserfs", 64,   0x34,  8, "ReIsErFs",         1,  1, probe_reiserfs },
+  { "reiserfs",  8,     20,  8, "ReIsErFs",         1,  1, probe_reiserfs },
+  { "ntfs",      0,      3,  8, "NTFS    ",         1,  1, probe_default },
+  { "vfat",      0,   0x52,  5, "MSWIN",            1,  1, probe_vfat },
+  { "vfat",      0,   0x52,  8, "FAT32   ",         1,  1, probe_vfat },
+  { "msdos",     0,   0x36,  5, "MSDOS",            1,  1, probe_msdos },
+  { "msdos",     0,   0x36,  8, "FAT16   ",         1,  1, probe_msdos },
+  { "msdos",     0,   0x36,  8, "FAT12   ",         1,  1, probe_msdos },
+  { "minix",     1,   0x10,  2, "\177\023",         1,  1, probe_minix },
+  { "minix",     1,   0x10,  2, "\217\023",         1,  1, probe_minix },
+  { "minix",     1,   0x10,  2, "\150\044",         1,  1, probe_minix },
+  { "minix",     1,   0x10,  2, "\170\044",         1,  1, probe_minix },
+  { "vxfs",      1,      0,  4, "\365\374\001\245", 1,  1, probe_default },
+  { "xfs",       0,      0,  4, "XFSB",             1,  1, probe_xfs },
+  { "romfs",     0,      0,  8, "-rom1fs-",         1,  1, probe_default },
+  { "bfs",       0,      0,  4, "\316\372\173\033", 1,  1, probe_default },
+  { "cramfs",    0,      0,  4, "E=\315\034",       1,  1, probe_default },
+  { "qnx4",      0,      4,  6, "QNX4FS",           1,  1, probe_default },
+  { "iso9660",  32,      1,  5, "CD001",            1,  1, probe_default },
+  { "iso9660",  32,      9,  5, "CDROM",            1,  1, probe_default },
+  { "udf",      32,      1,  5, "BEA01",            1,  1, probe_default },
+  { "udf",      32,      1,  5, "BOOT2",            1,  1, probe_default },
+  { "udf",      32,      1,  5, "CD001",            1,  1, probe_default },
+  { "udf",      32,      1,  5, "CDW02",            1,  1, probe_default },
+  { "udf",      32,      1,  5, "NSR02",            1,  1, probe_default },
+  { "udf",      32,      1,  5, "NSR03",            1,  1, probe_default },
+  { "udf",      32,      1,  5, "TEA01",            1,  1, probe_default },
+  { "jfs",      32,      0,  4, "JFS1",             1,  1, probe_default },
+  { "hfs",       1,      0,  2, "BD",               1,  1, probe_hfs },
+  { "ufs",       8,  0x55c,  4, "T\031\001\000",    1,  1, probe_default },
+  { "hpfs",      8,      0,  4, "I\350\225\371",    1,  1, probe_default },
+  { "sysv",      0,  0x3f8,  4, "\020~\030\375",    1,  1, probe_default },
+  { "swap",      0,  0xff6, 10, "SWAP-SPACE",       1,  4, probe_swap },
+  { "swap",      0,  0xff6, 10, "SWAPSPACE2",       1,  4, probe_swap },
+  { "swap",      0, 0x1ff6, 10, "SWAP-SPACE",       1,  8, probe_swap },
+  { "swap",      0, 0x1ff6, 10, "SWAPSPACE2",       1,  8, probe_swap },
+  { "swap",      0, 0x3ff6, 10, "SWAP-SPACE",       1, 16, probe_swap },
+  { "swap",      0, 0x3ff6, 10, "SWAPSPACE2",       1, 16, probe_swap },
+  {   NULL,      0,      0,  0, NULL,               1,  0, NULL }
+};
+
+
+/*
+ * When probing for a lot of magics, we handle everything in 1kB buffers so
+ * that we don't have to worry about reading each combination of block sizes.
+ */
+static unsigned char *read_one_buf(int fd, blkid_loff_t offset)
+{
+       char *buf;
+
+       if (lseek(fd, offset, SEEK_SET) < 0)
+               return NULL;
+
+       if (!(buf = (unsigned char *)malloc(BLKID_BLK_SIZE)))
+               return NULL;
+
+       if (read(fd, buf, BLKID_BLK_SIZE) != BLKID_BLK_SIZE) {
+               free(buf);
+               return NULL;
+       }
+
+       return buf;
+}
+
+static unsigned char *read_sb_buf(int fd, unsigned char **bufs, int kboff,
+                                 blkid_loff_t start)
+{
+       int index = kboff >> BLKID_BLK_KBITS;
+       unsigned char **buf;
+
+       if (index > BLKID_BLK_OFFS || index < -BLKID_BLK_OFFS) {
+               fprintf(stderr, "reading from invalid offset %d (%d)!\n",
+                       kboff, index);
+               return NULL;
+       }
+
+       buf = bufs + index;
+       if (!*buf)
+               *buf = read_one_buf(fd, start);
+
+       return *buf;
+}
+
+static struct blkid_magic *devname_to_magic(const char *devname, int fd,
+                                           unsigned char **bufs,
+                                           struct blkid_magic *id,
+                                           blkid_loff_t size)
+{
+       struct blkid_magic *ret = NULL;
+
+       if (!bufs || fd < 0)
+               return NULL;
+
+       if (id >= type_array + sizeof(type_array) / sizeof(*id))
+               return NULL;
+
+       for (id = id < type_array ? type_array : id + 1; id->bim_type; ++id) {
+               unsigned char *buf;
+               blkid_loff_t start = 0LL;
+               blkid_loff_t offset = 0LL;
+               int kboff;
+
+               offset = ((blkid_loff_t)id->bim_kboff << 10) +
+                       (id->bim_sboff & ~0x3ffULL);
+               /*
+                * We index negative buffers by their actual offset (including
+                * superblock offsets > 1kB, not the aligned offset, so that
+                * we correctly access negative buffers with different
+                * alignment requirements.
+                */
+               if (id->bim_kboff < 0) {
+                       start = (size & ~((blkid_loff_t)id->bim_align - 1)) +
+                               offset;
+                       if (start < 0) /* Device too small for alignment */
+                               continue;
+                       kboff = (start - size) >> 10;
+               } else {
+                       start = offset;
+                       kboff = offset >> 10;
+               }
+
+               if ((buf =
+                    read_sb_buf(fd, bufs, kboff, start)) &&
+                   !memcmp(id->bim_magic, buf + (id->bim_sboff&0x3ffULL),
+                           id->bim_len)) {
+                       ret = id;
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+/*
+ * Get data from a single block special device.
+ *
+ * Return a blkid_dev with at least the device type and size set.
+ * If the passed-in size is zero, then we get the device size here.
+ */
+blkid_dev *blkid_devname_to_dev(const char *devname, blkid_loff_t size)
+{
+       unsigned char *buf_array[BLKID_BLK_OFFS * 2 + 1];
+       unsigned char **bufs = buf_array + BLKID_BLK_OFFS;
+       blkid_dev *dev = NULL, *last = NULL;
+       unsigned char *sb_buf = NULL;
+       int sb_size = 0;
+       struct blkid_magic *id = NULL;
+       blkid_loff_t diff_last = 0xf000000000000000ULL;
+       int fd;
+
+       if (!devname)
+               return NULL;
+
+       fd = open(devname, O_RDONLY);
+       if (fd < 0)
+               return NULL;
+
+       if (!size)
+               size = blkid_get_dev_size(fd);
+       if (size < 1024)
+               goto exit_fd;
+
+       memset(buf_array, 0, sizeof(buf_array));
+
+       while ((id = devname_to_magic(devname, fd, bufs, id, size)) &&
+              diff_last) {
+               int new_sb;
+               blkid_loff_t diff_dev;
+
+               DEB_PROBE("found type %s (#%d) on %s, probing\n",
+                         id->bim_type, id - type_array, devname);
+
+               new_sb = id->bim_kbsize << 10;
+               if (sb_size < new_sb) {
+                       unsigned char *sav = sb_buf;
+                       if (!(sb_buf = realloc(sb_buf, new_sb))) {
+                               sb_buf = sav;
+                               continue;
+                       }
+                       sb_size = new_sb;
+               }
+
+               if (id->bim_probe(fd, &dev, devname, id, sb_buf, size) < 0)
+                       continue;
+
+               diff_dev = size - dev->bid_size;
+               DEB_PROBE("size = %Lu, fs size = %Lu\n", size, dev->bid_size);
+               DEB_PROBE("checking best match: old %Ld, new %Ld\n",
+                         diff_last, diff_dev);
+               /* See which type is a better match by checking size */
+               if ((diff_last < 0 && diff_dev > diff_last) ||
+                   (diff_last > 0 && diff_dev >= 0 && diff_dev < diff_last)) {
+                       if (last)
+                               blkid_free_dev(last);
+                       last = dev;
+                       diff_last = diff_dev;
+               } else
+                       blkid_free_dev(dev);
+       }
+
+       if (!last)
+               DEB_PROBE("unknown device type on %s\n", devname);
+       else
+               DEB_DUMP_DEV(last);
+
+       /* Free up any buffers we allocated */
+       for (bufs = buf_array; bufs - buf_array < sizeof(buf_array) /
+                                               sizeof(buf_array[0]); bufs++) {
+               if (*bufs)
+                       free(*bufs);
+       }
+
+       if (sb_buf)
+               free(sb_buf);
+exit_fd:
+       close(fd);
+       return last;
+}
+
+/*
+ * 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_devname(blkid_cache *cache, blkid_dev *dev)
+{
+       blkid_loff_t size;
+       struct blkid_magic *id;
+       blkid_dev *new = NULL;
+       char *sb_buf = NULL;
+       int sb_size = 0;
+       time_t diff;
+       int fd;
+
+       if (!dev)
+               return NULL;
+
+       diff = time(0) - dev->bid_time;
+
+       if (diff < BLKID_PROBE_MIN || (dev->bid_flags & BLKID_BID_FL_VERIFIED &&
+                                      diff < BLKID_PROBE_INTERVAL))
+               return dev;
+
+       DEB_PROBE("need to revalidate %s\n", dev->bid_name);
+
+       if ((fd = open(dev->bid_name, O_RDONLY)) < 0) {
+               if (errno == ENXIO || errno == ENODEV) {
+                       fprintf(stderr, "unable to open %s for revalidation\n",
+                               dev->bid_name);
+                       blkid_free_dev(dev);
+                       return NULL;
+               }
+               /* We don't have read permission, just return cache data. */
+               DEB_PROBE("returning unverified data for %s\n", dev->bid_name);
+               return dev;
+       }
+
+       size = blkid_get_dev_size(fd);
+
+       /* See if we can probe this device by its existing type directly */
+       for (id = type_array; id->bim_type; id++) {
+               if (!strcmp(id->bim_type, dev->bid_type)) {
+                       int new_sb = id->bim_kbsize << 10;
+                       /* See if we need to allocate a larger sb buffer */
+                       if (sb_size < new_sb) {
+                               char *sav = sb_buf;
+
+                               /* We can't revalidate, return old dev */
+                               if (!(sb_buf = realloc(sb_buf, new_sb))) {
+                                       fprintf(stderr, "not enough memory for "
+                                               "%s revalidation\n",
+                                               dev->bid_name);
+                                       free(sav);
+                                       goto exit_fd;
+                               }
+                               sb_size = new_sb;
+                       }
+
+                       if (id->bim_probe(fd, &new, dev->bid_name, id, sb_buf,
+                                         size) == 0)
+                               break;
+               }
+       }
+
+       if (sb_buf)
+               free(sb_buf);
+
+       /* Otherwise we need to determine the device type first */
+       if (new || (new = blkid_devname_to_dev(dev->bid_name, size))) {
+               new->bid_id = dev->bid_id; /* save old id for cache */
+               blkid_free_dev(dev);
+               dev = blkid_add_dev_to_cache(cache, new);
+       }
+
+exit_fd:
+       close(fd);
+
+       /* In case the cache is missing the device size */
+       if (dev->bid_devsize == 0)
+               dev->bid_devsize = size;
+       return dev;
+
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+       blkid_dev *dev;
+
+       if (argc != 2) {
+               fprintf(stderr, "Usage: %s device\n"
+                       "Probe a single device to determine type\n", argv[0]);
+               exit(1);
+       }
+       dev = blkid_devname_to_dev(argv[1], 0);
+       if (dev)
+               blkid_free_dev(dev);
+       else
+               printf("%s: %s has an unsupported type\n", argv[0], argv[1]);
+       return (0);
+}
+#endif
diff --git a/lib/blkid/probe.h b/lib/blkid/probe.h
new file mode 100644 (file)
index 0000000..9312a5f
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * probe.h - constants and on-disk structures for extracting device data
+ *
+ * Copyright (C) 1999 by Andries Brouwer
+ * Copyright (C) 1999, 2000 by Theodore Ts'o
+ * Copyright (C) 2001 by Andreas Dilger
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ * %End-Header%
+ */
+
+#ifndef _BLKID_PROBE_H
+#define _BLKID_PROBE_H
+
+#include <blkid/blkid_types.h>
+
+struct blkid_magic;
+
+typedef int (*blkid_probe_t)(int fd, blkid_dev **dev_p, const char *devname,
+                            struct blkid_magic *id, unsigned char *buf,
+                            blkid_loff_t size);
+
+struct blkid_magic {
+       const char      *bim_type;      /* type name for this magic */
+       long            bim_kboff;      /* kilobyte offset of superblock */
+       unsigned        bim_sboff;      /* byte offset within superblock */
+       unsigned        bim_len;        /* length of magic */
+       unsigned char   *bim_magic;     /* magic string */
+       unsigned        bim_align;      /* byte alignment of superblock */
+       unsigned        bim_kbsize;     /* size of superblock in kilobytes */
+       blkid_probe_t   bim_probe;      /* probe function */
+};
+
+/*
+ * Structures for each of the content types we want to extract information
+ * from.  We do not necessarily need the magic field here, because we have
+ * already identified the content type before we get this far.  It may still
+ * be useful if there are probe functions which handle multiple content types.
+ */
+struct ext2_super_block {
+       __u32           s_inodes_count;
+       __u32           s_blocks_count;
+       __u32           s_r_blocks_count;
+       __u32           s_free_blocks_count;
+       __u32           s_free_inodes_count;
+       __u32           s_first_data_block;
+       __u32           s_log_block_size;
+       __u32           s_dummy3[7];
+       unsigned char   s_magic[2];
+       __u16           s_state;
+       __u32           s_dummy5[8];
+       __u32           s_feature_compat;
+       __u32           s_feature_incompat;
+       __u32           s_feature_ro_compat;
+       unsigned char   s_uuid[16];
+       unsigned char   s_volume_name[16];
+};
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL                0x00000004
+#define EXT3_FEATURE_INCOMPAT_RECOVER          0x00000004
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV      0x00000008
+
+struct xfs_super_block {
+       unsigned char   xs_magic[4];
+       __u32           xs_blocksize;
+       __u64           xs_dblocks;
+       __u64           xs_rblocks;
+       __u32           xs_dummy1[8];
+       unsigned char   xs_uuid[16];
+       __u32           xs_dummy2[15];
+       unsigned char   xs_fname[12];
+       __u32           xs_dummy3[2];
+       __u64           xs_icount;
+       __u64           xs_ifree;
+       __u64           xs_fdblocks;
+};
+
+struct reiserfs_super_block {
+       __u32           rs_blocks_count;
+       __u32           rs_free_blocks;
+       __u32           rs_root_block;
+       __u32           rs_journal_block;
+       __u32           rs_journal_dev;
+       __u32           rs_orig_journal_size;
+       __u32           rs_dummy2[5];
+       __u16           rs_blocksize;
+       __u16           rs_dummy3[3];
+       unsigned char   rs_magic[12];
+       __u32           rs_dummy4[5];
+       unsigned char   rs_uuid[16];
+       unsigned char   rs_label[16];
+};
+
+/* 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*/        __u8            vs_cluster_size;
+/* 0e*/        __u16           vs_reserved;
+/* 10*/        __u8            vs_fats;
+/* 11*/        unsigned char   vs_dir_entries[2];
+/* 13*/        unsigned char   vs_sectors[2];
+/* 15*/        unsigned char   vs_media;
+/* 16*/        __u16           vs_fat_length;
+/* 18*/        __u16           vs_secs_track;
+/* 1a*/        __u16           vs_heads;
+/* 1c*/        __u32           vs_hidden;
+/* 20*/        __u32           vs_total_sect;
+/* 24*/        __u32           vs_fat32_length;
+/* 28*/        __u16           vs_flags;
+/* 2a*/        __u8            vs_version[2];
+/* 2c*/        __u32           vs_root_cluster;
+/* 30*/        __u16           vs_insfo_sector;
+/* 32*/        __u16           vs_backup_boot;
+/* 34*/        __u16           vs_reserved2[6];
+/* 40*/        unsigned char   vs_unknown[3];
+/* 43*/        unsigned char   vs_serno[4];
+/* 47*/        unsigned char   vs_label[11];
+/* 52*/        unsigned char   vs_magic[8];
+/* 5a*/        unsigned char   vs_dummy2[164];
+/*1fe*/        unsigned char   vs_pmagic[2];
+};
+
+/* Yucky misaligned values */
+struct msdos_super_block {
+/* 00*/        unsigned char   ms_ignored[3];
+/* 03*/        unsigned char   ms_sysid[8];
+/* 0b*/        unsigned char   ms_sector_size[2];
+/* 0d*/        __u8            ms_cluster_size;
+/* 0e*/        __u16           ms_reserved;
+/* 10*/        __u8            ms_fats;
+/* 11*/        unsigned char   ms_dir_entries[2];
+/* 13*/        unsigned char   ms_sectors[2];
+/* 15*/        unsigned char   ms_media;
+/* 16*/        __u16           ms_fat_length;
+/* 18*/        __u16           ms_secs_track;
+/* 1a*/        __u16           ms_heads;
+/* 1c*/        __u32           ms_hidden;
+/* 20*/        __u32           ms_total_sect;
+/* 24*/        unsigned char   ms_unknown[3];
+/* 27*/        unsigned char   ms_serno[4];
+/* 2b*/        unsigned char   ms_label[11];
+/* 36*/        unsigned char   ms_magic[8];
+/* 3d*/        unsigned char   ms_dummy2[192];
+/*1fe*/        unsigned char   ms_pmagic[2];
+};
+
+struct minix_super_block {
+       __u16           ms_ninodes;
+       __u16           ms_nzones;
+       __u16           ms_imap_blocks;
+       __u16           ms_zmap_blocks;
+       __u16           ms_firstdatazone;
+       __u16           ms_log_zone_size;
+       __u32           ms_max_size;
+       unsigned char   ms_magic[2];
+       __u16           ms_state;
+       __u32           ms_zones;
+};
+
+struct swap_header {
+       char            sh_bootbits[1024];
+       unsigned int    sh_version;
+       unsigned int    sh_last_page;
+       unsigned int    sh_nr_badpages;
+       char            sh_label[16];
+};
+
+struct mdp_superblock_s {
+       __u32 md_magic;
+       __u32 major_version;
+       __u32 minor_version;
+       __u32 patch_version;
+       __u32 gvalid_words;
+       __u32 set_uuid0;
+       __u32 ctime;
+       __u32 level;
+       __u32 size;
+       __u32 nr_disks;
+       __u32 raid_disks;
+       __u32 md_minor;
+       __u32 not_persistent;
+       __u32 set_uuid1;
+       __u32 set_uuid2;
+       __u32 set_uuid3;
+};
+
+struct hfs_super_block {
+       char    h_magic[2];
+       char    h_dummy[18];
+       __u32   h_blksize;
+};
+
+#ifndef le32_to_cpu    /* Assume if one is defined, all are defined */
+#define X16_to_cpu(x) ((__u16)((((__u16)(x) & 0x00ffU) << 8) | \
+                              (((__u16)(x) & 0xff00U) >> 8)))
+#define X32_to_cpu(x) ((__u32)((((__u32)(x) & 0x000000ffU) << 24) | \
+                              (((__u32)(x) & 0x0000ff00U) << 8) | \
+                              (((__u32)(x) & 0x00ff0000U) >> 8) | \
+                              (((__u32)(x) & 0xff000000U) >> 24)))
+#define X64_to_cpu(x) ((__u64)((((__u64)(x) & 0x00000000000000ffULL) << 56) | \
+                              (((__u64)(x) & 0x000000000000ff00ULL) << 40) | \
+                              (((__u64)(x) & 0x0000000000ff0000ULL) << 24) | \
+                              (((__u64)(x) & 0x00000000ff000000ULL) <<  8) | \
+                              (((__u64)(x) & 0x000000ff00000000ULL) >>  8) | \
+                              (((__u64)(x) & 0x0000ff0000000000ULL) >> 24) | \
+                              (((__u64)(x) & 0x00ff000000000000ULL) >> 40) | \
+                              (((__u64)(x) & 0xff00000000000000ULL) >> 56)))
+#if  __BYTE_ORDER == __BIG_ENDIAN
+#define le16_to_cpu(x) X16_to_cpu(x)
+#define le32_to_cpu(x) X32_to_cpu(x)
+#define le64_to_cpu(x) X64_to_cpu(x)
+#define be16_to_cpu(x) (x)
+#define be32_to_cpu(x) (x)
+#define be64_to_cpu(x) (x)
+#else
+#define le16_to_cpu(x) (x)
+#define le32_to_cpu(x) (x)
+#define le64_to_cpu(x) (x)
+#define be16_to_cpu(x) X16_to_cpu(x)
+#define be32_to_cpu(x) X32_to_cpu(x)
+#define be64_to_cpu(x) X64_to_cpu(x)
+#endif
+#endif
+
+#endif /* _BLKID_PROBE_H */
diff --git a/lib/blkid/read.c b/lib/blkid/read.c
new file mode 100644 (file)
index 0000000..222e233
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ * read.c - read the blkid cache from disk, to avoid scanning all devices
+ *
+ * Copyright (C) 2001 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/stat.h>
+#include <unistd.h>
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#include "blkid/blkid.h"
+#include "uuid/uuid.h"
+
+#ifdef DEBUG_CACHE
+#define DEB_CACHE(fmt, arg...) printf("cache:" fmt, ##arg)
+#else
+#define DEB_CACHE(fmt, arg...) do {} while (0)
+#endif
+
+#ifdef HAVE_STRTOULL
+#define __USE_ISOC9X
+#define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */
+#else
+/* FIXME: need to support real strtoull here */
+#define STRTOULL strtoul
+#endif
+
+#if HAVE_STDLIB_H
+#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="time">   (ascii time_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)) {
+               DEB_CACHE("found device header: %8s\n", p);
+               p += 7;
+
+               *cp = p;
+               return 1;
+       }
+
+       if (*p == '<')
+               return 0;
+
+       return -BLKID_ERR_CACHE;
+}
+
+/* Consume the remaining XML on the line (cosmetic only) */
+static int parse_end(char **cp)
+{
+       *cp = skip_over_blank(*cp);
+
+       if (!strncmp(*cp, "</device>", 9)) {
+               DEB_CACHE("found device trailer %9s\n", *cp);
+               *cp += 9;
+               return 0;
+       }
+
+       return -BLKID_ERR_CACHE;
+}
+
+/*
+ * Allocate a new device struct with device name filled in.  Will handle
+ * finding the device on lines of the form:
+ * <device foo=bar>devname</device>
+ * <device>devname<foo>bar</foo></device>
+ */
+static int parse_dev(blkid_dev **dev, char **cp)
+{
+       char **name;
+       char *start, *tmp, *end;
+       int ret;
+
+       if ((ret = parse_start(cp)) <= 0)
+               return ret;
+
+       start = tmp = strchr(*cp, '>');
+       if (!start) {
+               fprintf(stderr, "blkid: short line parsing dev: %s\n", *cp);
+               return -BLKID_ERR_CACHE;
+       }
+       start = skip_over_blank(start + 1);
+       end = skip_over_word(start);
+
+       DEB_CACHE("device should be %*s\n", end - start, start);
+
+       if (**cp == '>')
+               *cp = end;
+       else
+               (*cp)++;
+
+       *tmp = '\0';
+
+       if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0)
+               fprintf(stderr, "blkid: missing </device> ending: %s\n", end);
+       else if (tmp)
+               *tmp = '\0';
+
+       if (end - start <= 1) {
+               fprintf(stderr, "blkid: empty device name: %s\n", *cp);
+               return -BLKID_ERR_CACHE;
+       }
+
+       if (!(*dev = blkid_new_dev()))
+               return -BLKID_ERR_MEM;
+
+       name = &(*dev)->bid_name;
+       *name = (char *)malloc(end - start + 1);
+       if (*name == NULL) {
+               blkid_free_dev(*dev);
+               return -BLKID_ERR_MEM;
+       }
+
+       memcpy(*name, start, end - start);
+       (*name)[end - start] = '\0';
+
+       DEB_CACHE("found dev %s\n", *name);
+
+       return 1;
+}
+
+/*
+ * Extract a tag of the form NAME="value" from the line.
+ */
+static int parse_token(char **name, char **value, char **cp)
+{
+       char *end;
+
+       if (!name || !value || !cp)
+               return -BLKID_ERR_PARAM;
+
+       if (!(*value = strchr(*cp, '=')))
+               return 0;
+
+       **value = '\0';
+       *name = strip_line(*cp);
+       *value = skip_over_blank(*value + 1);
+
+       if (**value == '"') {
+               end = strchr(*value + 1, '"');
+               if (!end) {
+                       fprintf(stderr, "unbalanced quotes at: %s\n", *value);
+                       *cp = *value;
+                       return -BLKID_ERR_CACHE;
+               }
+               (*value)++;
+               *end = '\0';
+               end++;
+       } else {
+               end = skip_over_word(*value);
+               if (*end) {
+                       *end = '\0';
+                       end++;
+               }
+       }
+       *cp = end;
+
+       return 1;
+}
+
+/*
+ * Extract a tag of the form <NAME>value</NAME> from the line.
+ */
+/*
+static int parse_xml(char **name, char **value, char **cp)
+{
+       char *end;
+
+       if (!name || !value || !cp)
+               return -BLKID_ERR_PARAM;
+
+       *name = strip_line(*cp);
+
+       if ((*name)[0] != '<' || (*name)[1] == '/')
+               return 0;
+
+       FIXME: finish this.
+}
+*/
+
+/*
+ * Extract a tag from the line.
+ *
+ * Return 1 if a valid tag was found.
+ * Return 0 if no tag found.
+ * Return -ve error code.
+ */
+static int parse_tag(blkid_cache *cache, blkid_dev *dev, blkid_tag **tag,
+                    char **cp)
+{
+       char *name;
+       char *value;
+       int ret;
+
+       if (!cache || !dev)
+               return -BLKID_ERR_PARAM;
+
+       *tag = NULL;
+
+       if ((ret = parse_token(&name, &value, cp)) <= 0 /* &&
+           (ret = parse_xml(&name, &value, cp)) <= 0 */)
+               return ret;
+
+       /* Some tags are stored directly in the device struct */
+       if (!strcmp(name, "ID")) {
+               dev->bid_id = (unsigned int)strtoul(value, 0, 0);
+               if (dev->bid_id > cache->bic_idmax)
+                       cache->bic_idmax = dev->bid_id;
+       } else if (!strcmp(name, "DEVNO"))
+               dev->bid_devno = STRTOULL(value, 0, 0);
+       else if (!strcmp(name, "DEVSIZE"))
+               dev->bid_devno = STRTOULL(value, 0, 0);
+       else if (!strcmp(name, "TIME"))
+               /* FIXME: need to parse a long long eventually */
+               dev->bid_time = strtol(value, 0, 0);
+       else
+               ret = blkid_create_tag(dev, tag, 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).
+ */
+int blkid_parse_line(blkid_cache *cache, blkid_dev **dev_p, char *cp)
+{
+       blkid_dev *dev;
+       blkid_tag *tag;
+       int ret;
+
+       if (!cache || !dev_p)
+               return -BLKID_ERR_PARAM;
+
+       *dev_p = NULL;
+
+       DEB_CACHE("line: %s\n", cp);
+
+       if ((ret = parse_dev(dev_p, &cp)) <= 0)
+               return ret;
+
+       dev = *dev_p;
+
+       while ((ret = parse_tag(cache, dev, &tag, &cp)) > 0) {
+               /* Added to tags for this device struct already */
+               DEB_DUMP_TAG(tag);
+       }
+
+       if (dev->bid_type == NULL) {
+               fprintf(stderr, "blkid: device %s has no TYPE\n",dev->bid_name);
+               blkid_free_dev(dev);
+       }
+
+       DEB_DUMP_DEV(dev);
+
+       *dev_p = blkid_add_dev_to_cache(cache, dev);
+
+       return ret;
+}
+
+/*
+ * Read the given file stream for cached device data, and return it
+ * in a newly allocated cache struct.
+ *
+ * Returns 0 on success, or -ve error value.
+ */
+int blkid_read_cache_file(blkid_cache **cache, FILE *file)
+{
+       char buf[4096];
+       int lineno = 0;
+
+       if (!file || !cache)
+               return -BLKID_ERR_PARAM;
+
+       if (!*cache)
+               *cache = blkid_new_cache();
+
+       if (!*cache)
+               return -BLKID_ERR_MEM;
+
+       while (fgets(buf, sizeof(buf), file)) {
+               blkid_dev *dev;
+
+               int end = strlen(buf) - 1;
+
+               lineno++;
+               /* Continue reading next line if it ends with a backslash */
+               while (buf[end] == '\\' && end < sizeof(buf) - 2 &&
+                      fgets(buf + end, sizeof(buf) - end, stdin)) {
+                       end = strlen(buf) - 1;
+                       lineno++;
+               }
+
+               if (blkid_parse_line(*cache, &dev, buf) < 0) {
+                       fprintf(stderr, "blkid: bad format on line %d\n",
+                               lineno);
+                       continue;
+               }
+       }
+
+       /*
+        * Initially assume that we do not need to write out the cache file.
+        * This would be incorrect if we probed first, and parsed the cache
+        * afterwards, or parsed two caches and wanted to write it out, but
+        * the alternative is to force manually marking the cache dirty when
+        * any device is added, and that is also prone to error.
+        */
+       (*cache)->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+
+       return 0;
+}
+
+/*
+ * 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.
+ */
+int blkid_read_cache(blkid_cache **cache, const char *filename)
+{
+       FILE *file;
+       int ret;
+
+       if (!cache)
+               return -BLKID_ERR_PARAM;
+
+       if (!filename || !strlen(filename))
+               filename = BLKID_CACHE_FILE;
+
+       DEB_CACHE("cache file %s\n", filename);
+
+       /* If we read the standard cache file, do not do so again */
+       if (!strcmp(filename, BLKID_CACHE_FILE) && (*cache) &&
+           ((*cache)->bic_flags & BLKID_BIC_FL_PARSED))
+               return 0;
+
+       if (!strcmp(filename, "-") || !strcmp(filename, "stdin"))
+               file = stdin;
+       else {
+               /*
+                * If the file doesn't exist, then we just return an empty
+                * struct so that the cache can be populated.
+                */
+               if (access(filename, R_OK) < 0) {
+                       *cache = blkid_new_cache();
+
+                       return *cache ? 0 : -BLKID_ERR_MEM;
+               }
+
+               file = fopen(filename, "r");
+               if (!file) {
+                       perror(filename);
+                       return errno;
+               }
+       }
+
+       ret = blkid_read_cache_file(cache, file);
+
+       if (file != stdin)
+               fclose(file);
+
+       /* Mark us as having read the standard cache file */
+       if (!strcmp(filename, BLKID_CACHE_FILE))
+               (*cache)->bic_flags |= BLKID_BIC_FL_PARSED;
+
+       return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char**argv)
+{
+       blkid_cache *cache = NULL;
+       int ret;
+
+       if (argc > 2) {
+               fprintf(stderr, "Usage: %s [filename]\n"
+                       "Test parsing of the cache (filename)\n", argv[0]);
+               exit(1);
+       }
+       if ((ret = blkid_read_cache(&cache, argv[1])) < 0)
+               fprintf(stderr, "error %d reading cache file %s\n", ret,
+                       argv[1] ? argv[1] : BLKID_CACHE_FILE);
+
+       blkid_free_cache(cache);
+
+       return ret;
+}
+#endif
diff --git a/lib/blkid/resolve.c b/lib/blkid/resolve.c
new file mode 100644 (file)
index 0000000..88acc66
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * resolve.c - resolve names and tags into specific devices
+ *
+ * Copyright (C) 2001 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>
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "blkid/blkid.h"
+#include "probe.h"
+
+#ifdef DEBUG_RESOLVE
+#define DEB_RESOLVE(fmt, arg...) printf("resolve: " fmt, ## arg)
+#else
+#define DEB_RESOLVE(fmt, arg...) do {} while (0)
+#endif
+
+
+/*
+ * Find a tagname (e.g. LABEL or UUID) on a specific device.
+ */
+char *blkid_get_tagname_devname(blkid_cache *cache, const char *tagname,
+                               const char *devname)
+{
+       blkid_tag *tag, *found;
+       blkid_dev *dev;
+       char *ret = NULL;
+
+       DEB_RESOLVE("looking for %s on %s\n", tagname, devname);
+
+       if (!devname)
+               return NULL;
+
+       if (blkid_create_tag(NULL, &tag, tagname, NULL, 0) < 0)
+               return NULL;
+
+       if (!cache)
+               DEB_RESOLVE("no cache given, direct device probe\n");
+
+       if ((dev = blkid_get_devname(cache, devname)) &&
+           (found = blkid_find_tag_dev(dev, tag)))
+               ret = string_copy(found->bit_val);
+
+       if (!cache)
+               blkid_free_dev(dev);
+
+       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.
+ *
+ * The string returned must be freed with string_free().
+ */
+char *blkid_get_token(blkid_cache *cache, const char *token,
+                     const char *value)
+{
+       blkid_tag *tag = NULL, *found = NULL;
+       blkid_cache *c = cache;
+       char *name = NULL;
+
+       DEB_RESOLVE("looking for %s%c%s %s\n", token, value ? '=' : ' ',
+                   value ? value : "", cache ? "in cache" : "from disk");
+
+       if (!(tag = blkid_token_to_tag(token))) {
+               if (!value)
+                       return string_copy(token);
+               if (blkid_create_tag(NULL,&tag,token,value,strlen(value)) < 0)
+                       return NULL;
+       }
+
+       if (!cache) {
+               if (blkid_read_cache(&c, NULL) < 0)
+                       c = blkid_new_cache();
+               if (!c)
+                       return NULL;
+       }
+
+       if ((found = blkid_get_tag_cache(c, tag)) && found->bit_dev)
+               name = string_copy(found->bit_dev->bid_name);
+
+       if (!cache) {
+               blkid_save_cache(c, NULL);
+               blkid_free_cache(c);
+       }
+
+
+       blkid_free_tag(tag);
+       return name;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+       char *value;
+
+       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 (argv[2]) {
+               value = blkid_get_tagname_devname(NULL, argv[1], argv[2]);
+               printf("%s has tag %s=%s\n", argv[2], argv[1],
+                      value ? value : "<missing>");
+       } else {
+               value = blkid_get_token(NULL, argv[1], NULL);
+               printf("%s has tag %s\n", value ? value : "<none>", argv[1]);
+       }
+       return value ? 0 : 1;
+}
+#endif
diff --git a/lib/blkid/save.c b/lib/blkid/save.c
new file mode 100644 (file)
index 0000000..b55ab65
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * save.c - write the cache struct to disk
+ *
+ * Copyright (C) 2001 by 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>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "blkid/blkid.h"
+
+#ifdef DEBUG_SAVE
+#define DEB_SAVE(fmt, arg...) printf("save: " fmt, ## arg)
+#else
+#define DEB_SAVE(fmt, arg...) do {} while (0)
+#endif
+
+static int save_dev(blkid_dev *dev, FILE *file)
+{
+       struct list_head *p;
+
+       if (!dev)
+               return 0;
+
+       DEB_SAVE("device %s, type %s\n", dev->bid_name, dev->bid_type);
+
+       fprintf(file,
+               "<device TYPE=\"%s\" DEVNO=\"0x%04Lx\" ID=\"%d\" TIME=\"%Ld\"",
+               dev->bid_type, dev->bid_devno,
+               dev->bid_id, (long long)dev->bid_time);
+       list_for_each(p, &dev->bid_tags) {
+               blkid_tag *tag = list_entry(p, blkid_tag, bit_tags);
+               if (strcmp(tag->bit_name, "TYPE"))
+                       fprintf(file, " %s=\"%s\"", tag->bit_name,tag->bit_val);
+       }
+       fprintf(file, ">%s</device>\n", dev->bid_name);
+
+       return 0;
+}
+
+int blkid_save_cache_file(blkid_cache *cache, FILE *file)
+{
+       struct list_head *p;
+       int ret = 0;
+
+       if (!cache || !file)
+               return -BLKID_ERR_PARAM;
+
+       if (list_empty(&cache->bic_devs) ||
+           !cache->bic_flags & BLKID_BIC_FL_CHANGED)
+               return 0;
+
+       list_for_each(p, &cache->bic_devs) {
+               blkid_dev *dev = list_entry(p, blkid_dev, bid_devs);
+               if ((ret = save_dev(dev, file)) < 0)
+                       break;
+       }
+
+       if (ret >= 0) {
+               cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
+               ret = 1;
+       }
+
+       return ret;
+}
+
+/*
+ * Write out the cache struct to the cache file on disk.
+ */
+int blkid_save_cache(blkid_cache *cache, char *filename)
+{
+       char tmp[4096] = { '\0', };
+       char *opened = NULL;
+       FILE *file = NULL;
+       int ret;
+
+       if (!cache)
+               return -BLKID_ERR_PARAM;
+
+       if (list_empty(&cache->bic_devs) ||
+           !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
+               DEB_SAVE("empty cache, not saving\n");
+               return 0;
+       }
+
+       if (!filename || !strlen(filename))
+               filename = BLKID_CACHE_FILE;
+
+       if (!strcmp(filename, "-") || !strcmp(filename, "stdout"))
+               file = stdout;
+       else {
+               struct stat st;
+
+               /* 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)) {
+                       DEB_SAVE("can't write to cache file %s\n", filename);
+                       return 0;
+               }
+
+               /*
+                * Try and create a temporary file in the same directory so
+                * that in case of error we don't overwrite the cache file.
+                * If the cache file doesn't yet exist, it isn't a regular
+                * file (e.g. /dev/null or a socket), or we couldn't create
+                * a temporary file then we open it directly.
+                */
+               if (ret == 0 && S_ISREG(st.st_mode)) {
+                       snprintf(tmp, sizeof(tmp) - 1, "%s-XXXXXX", filename);
+                       if (mktemp(tmp) == tmp && *tmp != '\0') {
+                               file = fopen(tmp, "w");
+                               opened = tmp;
+                       }
+               }
+
+               if (!file) {
+                       file = fopen(filename, "w");
+                       opened = filename;
+               }
+
+               DEB_SAVE("cache file %s (really %s)\n", filename, opened);
+
+               if (!file) {
+                       perror(opened);
+                       return errno;
+               }
+       }
+
+       ret = blkid_save_cache_file(cache, file);
+
+       if (file != stdout) {
+               fclose(file);
+               if (opened != filename) {
+                       if (ret < 0) {
+                               unlink(opened);
+                               DEB_SAVE("unlinked temp cache %s\n", opened);
+                       } else {
+                               char backup[4096];
+
+                               snprintf(backup, sizeof(backup) - 1, "%s.old",
+                                        filename);
+                               unlink(backup);
+                               link(filename, backup);
+                               rename(opened, filename);
+                               DEB_SAVE("moved temp cache %s\n", opened);
+                       }
+               }
+       }
+
+       return ret;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+       blkid_cache *cache = NULL;
+       int ret;
+
+       if (argc > 2) {
+               fprintf(stderr, "Usage: %s [filename]\n"
+                       "Test loading/saving a cache (filename)\n", argv[0]);
+               exit(1);
+       }
+       if ((ret = blkid_probe_all(&cache) < 0))
+               fprintf(stderr, "error probing devices\n");
+       else if ((ret = blkid_save_cache(cache, argv[1])) < 0)
+               fprintf(stderr, "error %d saving cache to %s\n", ret,
+                       argv[1] ? argv[1] : BLKID_CACHE_FILE);
+
+       blkid_free_cache(cache);
+
+       return ret;
+}
+#endif
diff --git a/lib/blkid/tag.c b/lib/blkid/tag.c
new file mode 100644 (file)
index 0000000..0ccd9e0
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * tag.c - allocation/initialization/free routines for tag structs
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "blkid/blkid.h"
+
+#ifdef DEBUG_TAG
+#define DEB_TAG(fmt, arg...) printf("tag: " fmt, ## arg)
+#else
+#define DEB_TAG(fmt, arg...) do {} while (0)
+#endif
+
+blkid_tag *blkid_new_tag(void)
+{
+       blkid_tag *tag;
+
+       if (!(tag = (blkid_tag *)calloc(1, sizeof(blkid_tag))))
+               return NULL;
+
+       INIT_LIST_HEAD(&tag->bit_tags);
+       INIT_LIST_HEAD(&tag->bit_names);
+
+       return tag;
+}
+
+void blkid_free_tag(blkid_tag *tag)
+{
+       if (!tag)
+               return;
+
+       DEB_TAG("    freeing tag %s=%s\n", tag->bit_name, tag->bit_val);
+       DEB_DUMP_TAG(tag);
+
+       list_del(&tag->bit_tags);       /* list of tags for this device */
+       list_del(&tag->bit_names);      /* list of tags with this type */
+
+       if (tag->bit_name)
+               string_free(tag->bit_name);
+       if (tag->bit_val)
+               string_free(tag->bit_val);
+
+       free(tag);
+}
+
+/*
+ * Find the desired tag on a list of tags with the same type.
+ */
+blkid_tag *blkid_find_tv_tags(blkid_tag *head, const char *value)
+{
+       struct blkid_tag *tag = NULL;
+       struct list_head *p;
+
+       if (!head || !value)
+               return NULL;
+
+       DEB_TAG("looking for %s in %s list\n", value, head->bit_name);
+
+       list_for_each(p, &head->bit_names) {
+               blkid_tag *tmp = list_entry(p, blkid_tag, bit_names);
+
+               if (!strcmp(tmp->bit_val, value)) {
+                       tag = tmp;
+                       break;
+               }
+       }
+
+       return tag;
+}
+
+/*
+ * Find the desired tag on a device.  If tag->bit_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, blkid_tag *tag)
+{
+       blkid_tag *found = NULL;
+       struct list_head *p;
+
+       if (!dev || !tag)
+               return NULL;
+
+       list_for_each(p, &dev->bid_tags) {
+               blkid_tag *tmp = list_entry(p, blkid_tag, bit_tags);
+
+               if (!strcmp(tmp->bit_name, tag->bit_name) &&
+                   (!tag->bit_val || !strcmp(tmp->bit_val, tag->bit_val))){
+                       found = tmp;
+                       break;
+               }
+       }
+
+       return found;
+}
+
+/*
+ * Find the desired tag type in the cache.
+ * We return the head tag for this tag type.
+ */
+blkid_tag *blkid_find_head_cache(blkid_cache *cache, blkid_tag *tag)
+{
+       blkid_tag *head = NULL;
+       struct list_head *p;
+
+       if (!cache || !tag)
+               return NULL;
+
+       list_for_each(p, &cache->bic_tags) {
+               blkid_tag *tmp = list_entry(p, blkid_tag, bit_tags);
+
+               if (!strcmp(tmp->bit_name, tag->bit_name)) {
+                       DEB_TAG("    found cache tag head %s\n", tag->bit_name);
+                       head = tmp;
+                       break;
+               }
+       }
+
+       return head;
+}
+
+/*
+ * Find a specific tag value in the cache.  If not found return NULL.
+ */
+blkid_tag *blkid_find_tag_cache(blkid_cache *cache, blkid_tag *tag)
+{
+       blkid_tag *head;
+
+       DEB_TAG("looking for %s=%s in cache\n", tag->bit_name, tag->bit_val);
+
+       head = blkid_find_head_cache(cache, tag);
+
+       return blkid_find_tv_tags(head, tag->bit_val);
+}
+
+/*
+ * Get a specific tag value in the cache.  If not found return NULL.
+ * If we have not already probed the devices, do so and search again.
+ */
+blkid_tag *blkid_get_tag_cache(blkid_cache *cache, blkid_tag *tag)
+{
+       blkid_tag *head, *found;
+
+       if (!tag || !cache)
+               return NULL;
+
+       DEB_TAG("looking for %s=%s in cache\n", tag->bit_name, tag->bit_val);
+
+       head = blkid_find_head_cache(cache, tag);
+       found = blkid_find_tv_tags(head, tag->bit_val);
+
+       if ((!head || !found) && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) {
+               blkid_probe_all(&cache);
+               if (!head)
+                       head = blkid_find_head_cache(cache, tag);
+               found = blkid_find_tv_tags(head, tag->bit_val);
+       }
+
+       return found;
+}
+
+/*
+ * Add a single tag to the given device.
+ * This function is not currently exported because adding arbitrary tags to
+ * a device will just get lost as soon as we verify the device (which
+ * uses the dev struct returned from the device probe).  At some point in
+ * the future it may be desirable to allow adding arbitrary tags to a device,
+ * and ensure that verify keeps all such tags (maybe lower case tag names?)
+ */
+static void add_tag_to_dev(blkid_dev *dev, blkid_tag *tag)
+{
+       if (!dev)
+               return;
+
+       DEB_TAG("adding tag %s=%s\n", tag->bit_name, tag->bit_val);
+
+       tag->bit_dev = dev;
+
+       list_add_tail(&tag->bit_tags, &dev->bid_tags);
+
+       /* Link common tags directly to the device struct */
+       if (!strcmp(tag->bit_name, "TYPE") && !dev->bid_type)
+               dev->bid_type = tag->bit_val;
+       else if (!strcmp(tag->bit_name, "LABEL"))
+               dev->bid_label = tag->bit_val;
+       else if (!strcmp(tag->bit_name, "UUID"))
+               dev->bid_uuid = tag->bit_val;
+}
+
+/*
+ * Allocate and fill out a tag struct.
+ * If dev is valid, the tag will be added to the tags for this device
+ * if an identical tag does not already exist.
+ * If tag is valid, the tag will be returned in this pointer.
+ */
+int blkid_create_tag(blkid_dev *dev, blkid_tag **tag, const char *name,
+                    const char *value, const int vlength)
+{
+       blkid_tag *t, *found;
+
+       if (!tag && !dev)
+               return -BLKID_ERR_PARAM;
+
+       if (!name)
+               return -BLKID_ERR_PARAM;
+
+       t = blkid_new_tag();
+       if (!t)
+               return -BLKID_ERR_MEM;
+
+       t->bit_name = string_copy(name);
+       t->bit_val = stringn_copy(value, vlength);
+
+       if ((found = blkid_find_tag_dev(dev, t))) {
+               if (tag)
+                       *tag = found;
+               blkid_free_tag(t);
+               return 0;
+       }
+
+       add_tag_to_dev(dev, t);
+       if (tag)
+               *tag = t;
+
+       return 0;
+}
+
+/*
+ * Convert a NAME=value pair into a token.  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 al alreay quoted string from the command-line we don't
+ * have to both quote and escape quote so that the quotes make it to us).
+ */
+blkid_tag *blkid_token_to_tag(const char *token)
+{
+       char *name, *value, *cp;
+       blkid_tag *tag = NULL;
+       int len;
+
+       DEB_TAG("trying to make '%s' into a tag\n", token);
+       if (!token || !(cp = strchr(token, '=')))
+               return NULL;
+
+       name = string_copy(token);
+       value = name + (cp - token);
+       *value++ = '\0';
+       if (*value == '"' || *value == '\'') {
+               char c = *value++;
+               if (!(cp = strrchr(value, c))) {
+                       fprintf(stderr, "Missing close quote for %s\n", token);
+                       return NULL;
+               }
+               *cp = '\0';
+               len = cp - value;
+       } else
+               len = strlen(value);
+
+       blkid_create_tag(NULL, &tag, name, value, len);
+
+       string_free(name);
+
+       return tag;
+}
index c5273d4..a0c398f 100644 (file)
@@ -1,3 +1,7 @@
+2003-01-23  Theodore Ts'o  <tytso@mit.edu>
+
+       * Makefile.in: Integrate in new blkid library.
+
 2003-01-22  Theodore Ts'o  <tytso@mit.edu>
 
        * fsck.c (parse_fstab_line, parse_escape): Add support for
index 8536c5a..78a8c28 100644 (file)
@@ -14,10 +14,10 @@ INSTALL = @INSTALL@
 @IMAGER_CMT@E2IMAGE_PROG= e2image
 @IMAGER_CMT@E2IMAGE_MAN= e2image.8
 
-SPROGS=                mke2fs badblocks tune2fs dumpe2fs $(E2IMAGE_PROG) @FSCK_PROG@
+SPROGS=                mke2fs badblocks tune2fs dumpe2fs blkid $(E2IMAGE_PROG) @FSCK_PROG@
 USPROGS=       mklost+found
 SMANPAGES=     tune2fs.8 mklost+found.8 mke2fs.8 dumpe2fs.8 badblocks.8 \
-                       e2label.8 findfs.8 $(E2IMAGE_MAN) @FSCK_MAN@
+                       e2label.8 findfs.8 blkid.8 $(E2IMAGE_MAN) @FSCK_MAN@
 
 UPROGS=                chattr lsattr uuidgen
 UMANPAGES=     chattr.1 lsattr.1 uuidgen.1
@@ -32,11 +32,12 @@ DUMPE2FS_OBJS=      dumpe2fs.o
 BADBLOCKS_OBJS=        badblocks.o
 E2IMAGE_OBJS=  e2image.o
 FSCK_OBJS=     fsck.o get_device_by_label.o base_device.o fstype.o
+BLKID_OBJS=    blkid.o
 
 SRCS=  $(srcdir)/tune2fs.c $(srcdir)/mklost+found.c $(srcdir)/mke2fs.c \
                $(srcdir)/chattr.c $(srcdir)/lsattr.c $(srcdir)/dumpe2fs.c \
                $(srcdir)/badblocks.c $(srcdir)/fsck.c $(srcdir)/util.c \
-               $(srcdir)/uuidgen.c $(srcdir)/fstype.c
+               $(srcdir)/uuidgen.c $(srcdir)/fstype.c $(srcdir)/blkid.c
 
 LIBS= $(LIBEXT2FS) $(LIBCOM_ERR) 
 DEPLIBS= $(LIBEXT2FS) $(LIBCOM_ERR) 
@@ -44,6 +45,9 @@ DEPLIBS= $(LIBEXT2FS) $(LIBCOM_ERR)
 STATIC_LIBS= $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) 
 STATIC_DEPLIBS= $(STATIC_LIBEXT2FS) $(STATIC_LIBCOM_ERR) 
 
+LIBS_BLKID= $(LIBBLKID) $(LIBUUID)
+DEPLIBS_BLKID= $(LIBBLKID) $(LIBUUID)
+
 LIBS_E2P= $(LIBEXT2FS) $(LIBE2P) $(LIBCOM_ERR) 
 DEPLIBS_E2P= $(LIBEXT2FS) $(LIBE2P) $(LIBCOM_ERR) 
 
@@ -58,6 +62,9 @@ findsuper: findsuper.o
 tune2fs: $(TUNE2FS_OBJS) $(DEPLIBS_E2P) $(DEPLIBUUID)
        $(CC) $(ALL_LDFLAGS) -o tune2fs $(TUNE2FS_OBJS) $(LIBS_E2P) $(LIBUUID)
 
+blkid: $(BLKID_OBJS) $(DEPLIBS_E2P) $(DEPLIBS_BLKID)
+       $(CC) $(ALL_LDFLAGS) -o blkid $(BLKID_OBJS) $(LIBS_E2P) $(LIBS_BLKID)
+
 e2image: $(E2IMAGE_OBJS) $(DEPLIBS)
        $(CC) $(ALL_LDFLAGS) -o e2image $(E2IMAGE_OBJS) $(LIBS)
 
@@ -131,6 +138,9 @@ badblocks.8: $(DEP_SUBSTITUTE) $(srcdir)/badblocks.8.in
 fsck.8: $(DEP_SUBSTITUTE) $(srcdir)/fsck.8.in
        $(SUBSTITUTE) $(srcdir)/fsck.8.in fsck.8 
 
+blkid.8: $(DEP_SUBSTITUTE) $(srcdir)/blkid.8.in
+       $(SUBSTITUTE) $(srcdir)/blkid.8.in blkid.8
+
 chattr.1: $(DEP_SUBSTITUTE) $(srcdir)/chattr.1.in
        $(SUBSTITUTE) $(srcdir)/chattr.1.in chattr.1 
 
@@ -140,6 +150,9 @@ lsattr.1: $(DEP_SUBSTITUTE) $(srcdir)/lsattr.1.in
 uuidgen.1: $(DEP_SUBSTITUTE) $(srcdir)/uuidgen.1.in 
        $(SUBSTITUTE) $(srcdir)/uuidgen.1.in uuidgen.1 
 
+blkid.1: $(DEP_SUBSTITUTE) $(srcdir)/blkid.1.in 
+       $(SUBSTITUTE) $(srcdir)/blkid.1.in blkid.1 
+
 installdirs:
        $(top_srcdir)/mkinstalldirs $(DESTDIR)$(sbindir) \
                $(DESTDIR)$(root_sbindir) $(DESTDIR)$(bindir) \
diff --git a/misc/blkid.8.in b/misc/blkid.8.in
new file mode 100644 (file)
index 0000000..1f5970b
--- /dev/null
@@ -0,0 +1,142 @@
+.\" Copyright 2000 Andreas Dilger (adilger@turbolinux.com)
+.\"
+.\" This man page was created for blkid from e2fsprogs-1.25.
+.\" 
+.\" This file may be copied under the terms of the GNU Public License.
+.\" 
+.\" Based on uuidgen, Mon Sep 17 10:42:12 2000, Andreas Dilger
+.TH BLKID 8 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+blkid \- command\-line utility to locate/print block device attributes
+.SH SYNOPSIS
+.B blkid 
+]...
+[
+.B \-h
+]
+[
+[
+.B \-c
+.I cachefile
+]
+.B \-s
+.I savecachefile
+]
+[
+.B \-p
+]
+[
+.B \-t
+.I token
+]...
+[
+.B \-v
+]
+[
+.I device ...
+]
+.SH DESCRIPTION
+The
+.B blkid
+program is the command-line interface to working with
+.BR libuuid (3)
+library.  It can determine the type of content (e.g. filesystem, swap) 
+a block device holds, and also attributes (tokens, NAME=value pairs)
+from the content metadata (e.g. LABEL or UUID fields).
+.PP
+.B blkid
+has two main forms of operation: either searching for a device with a
+specific NAME=value pair, or displaying NAME=value pairs for one or
+more devices.
+.SH OPTIONS
+.TP
+.B \-c
+.I <cachefile>
+Read from
+.I cachefile
+instead of reading from the default cache file
+.IR /etc/blkid.tab .
+If you want to start with a clean cache (i.e. don't report devices previously
+scanned but not necessarily available at this time), specify
+.IR /dev/null.
+.TP
+.B \-h
+Display a usage message and exit.
+.TP
+.B \-p
+Probe all available devices.  This is the default when displaying
+tokens.  When searching for a token normally the cache file is
+used to locate the device and only that device is probed (to ensure
+cache coherency) and all devices are probed only if the token cannot
+be found in the cache.
+.TP
+.B \-s
+.I tag
+.I tag
+is of the form
+.I NAME
+and the resulting token is shown for each (specified) device that has
+such a tag.  It is possible to specify multiple
+.I tag
+options.  If no tag is specified, then all tokens are shown for all
+(specified) devices.
+In order to just refresh the cache without showing any tokens use
+.B "-s none"
+with no other options.
+.TP
+.B \-t
+.I token
+.I token
+is of the form
+.IB NAME = value
+and that specific token is searched for in the cache or among all visible
+block devices and additionally any specified devices.  If that token is
+not found, no output is shown.  Common values for
+.I NAME
+include
+.BR TYPE ,
+.BR LABEL ,
+and
+.BR UUID .
+.TP
+.B \-v
+Display version number and exit.
+.SH RETURN VALUE
+The UUID of the form 1b4e28ba-2fa1-11d2-883f-b9a761bde3fb (in
+.BR printf (3)
+format "%08x-%04x-%04x-%04x-%012x") is output to the standard output.
+.TP
+.B \-w
+.I <writecachefile>
+Write the device cache to
+.I writecachefile
+instead of writing it to the default cache file
+.IR /etc/blkid.tab .
+If you don't want to save the cache to the default file, specify
+.IR /dev/null.
+If not specified it will be the same file as that given by the
+.B -c
+option.
+.TP
+.I <device>
+Display tokens from only the specified device.  It is possible to
+give multiple
+.I <device>
+options on the command line.  If none is given, all devices which
+appear in
+.I /proc/partitions
+are shown, if they are recognized.
+.SH "RETURN CODE"
+If the specified token was found, or if any tags were shown from (specified)
+devices 0 is returned.  If the specified token was not found, or no
+(specified) devices could be identified return 2.  For usage or other errors
+return 4.
+.SH AUTHOR
+.B blkid
+was written by Andreas Dilger for libblkid.
+.SH AVAILABILITY
+.B blkid
+is part the e2fsprogs package since version 1.26 and is available from
+http://e2fsprogs.sourceforge.net.
+.SH "SEE ALSO"
+.BR libblkid (3)
diff --git a/misc/blkid.c b/misc/blkid.c
new file mode 100644 (file)
index 0000000..c39fb1d
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * blkid.c - User command-line interface for libblkid
+ *
+ * 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_GETOPT_H
+#include <getopt.h>
+#else
+extern char *optarg;
+extern int optind;
+#endif
+
+#include "blkid/blkid.h"
+
+char *progname = "blkid";
+void print_version(FILE *out)
+{
+       fprintf(stderr, "%s %s (%s)\n", progname, BLKID_VERSION, BLKID_DATE);
+}
+
+void usage(int error)
+{
+       FILE *out = error ? stderr : stdout;
+
+       print_version(out);
+       fprintf(out,
+               "usage:\t%s [-c <file>] [-h] "
+               "[-p] [-s <tag>] [-t <token>] [-v] [-w <file>] [dev ...]\n"
+               "\t-c\tcache file (default: /etc/blkid.tab, /dev/null = none)\n"
+               "\t-h\tprint this usage message and exit\n"
+               "\t-s\tshow specified tag(s) (default show all tags)\n"
+               "\t-t\tfind device with a specific token (NAME=value pair)\n"
+               "\t-v\tprint version and exit\n"
+               "\t-w\twrite cache to different file (/dev/null = no write)\n"
+               "\tdev\tspecify device(s) to probe (default: all devices)\n",
+               progname);
+       exit(error);
+}
+
+#define PT_FL_START    0x0001
+#define PT_FL_TYPE     0x0002
+
+static void print_tag(blkid_dev *dev, blkid_tag *tag, int *flags)
+{
+       /* Print only one "dev:" per device */
+       if (!*flags & PT_FL_START) {
+               printf("%s: ", dev->bid_name);
+               *flags |= PT_FL_START;
+       }
+       /* Print only the primary TYPE per device */
+       if (!strcmp(tag->bit_name, "TYPE")) {
+               if (*flags & PT_FL_TYPE)
+                       return;
+               *flags |= PT_FL_TYPE;
+       }
+       printf("%s=\"%s\" ", tag->bit_name, tag->bit_val);
+}
+
+void print_tags(blkid_dev *dev, char *show[], int numtag)
+{
+       struct list_head *p;
+       int flags = 0;
+
+       if (!dev)
+               return;
+
+       list_for_each(p, &dev->bid_tags) {
+               blkid_tag *tag = list_entry(p, blkid_tag, bit_tags);
+               int i;
+
+               /* Print all tokens if none is specified */
+               if (numtag == 0 || !show) {
+                       print_tag(dev, tag, &flags);
+               /* Otherwise, only print specific tokens */
+               } else for (i = 0; i < numtag; i++) {
+                       if (!strcmp(tag->bit_name, show[i]))
+                               print_tag(dev, tag, &flags);
+               }
+       }
+
+       if (flags)
+               printf("\n");
+}
+
+int main(int argc, char **argv)
+{
+       blkid_cache *cache = NULL;
+       char *devices[128] = { NULL, };
+       char *show[128] = { NULL, };
+       blkid_tag *tag = NULL;
+       char *read = NULL;
+       char *write = NULL;
+       int numdev = 0, numtag = 0;
+       int version = 0;
+       int err = 4;
+       int i;
+       char c;
+
+       while ((c = getopt (argc, argv, "c:d:f:hps:t:w:v")) != EOF)
+               switch (c) {
+               case 'd':       /* deprecated */
+                       if (numdev >= sizeof(devices) / sizeof(*devices)) {
+                               fprintf(stderr,
+                                       "Too many devices specified\n");
+                               usage(err);
+                       }
+                       devices[numdev++] = optarg;
+                       break;
+               case 'c':
+                       if (optarg && !*optarg)
+                               read = NULL;
+                       else
+                               read = optarg;
+                       if (!write)
+                               write = read;
+                       break;
+               case 's':
+                       if (numtag >= sizeof(show) / sizeof(*show)) {
+                               fprintf(stderr, "Too many tags specified\n");
+                               usage(err);
+                       }
+                       show[numtag++] = optarg;
+                       break;
+               case 't':
+                       if (tag) {
+                               fprintf(stderr, "Can only search for "
+                                               "one NAME=value pair\n");
+                               usage(err);
+                       }
+                       if (!(tag = blkid_token_to_tag(optarg))) {
+                               fprintf(stderr, "-t needs NAME=value pair\n");
+                               usage(err);
+                       }
+                       break;
+               case 'v':
+                       version = 1;
+                       break;
+               case 'w':
+                       if (optarg && !*optarg)
+                               write = NULL;
+                       else
+                               write = optarg;
+                       break;
+               case 'h':
+                       err = 0;
+               default:
+                       usage(err);
+               }
+
+       while (optind < argc)
+               devices[numdev++] = argv[optind++];
+
+       if (version) {
+               print_version(stdout);
+               goto exit;
+       }
+
+       if (blkid_read_cache(&cache, read) < 0)
+               goto exit;
+
+       err = 2;
+       /* If looking for a specific NAME=value pair, print only that */
+       if (tag) {
+               blkid_tag *found = NULL;
+
+               /* Load any additional devices not in the cache */
+               for (i = 0; i < numdev; i++)
+                       blkid_get_devname(cache, devices[i]);
+
+               if ((found = blkid_get_tag_cache(cache, tag))) {
+                       print_tags(found->bit_dev, show, numtag);
+                       err = 0;
+               }
+       /* If we didn't specify a single device, show all available devices */
+       } else if (!numdev) {
+               struct list_head *p;
+
+               blkid_probe_all(&cache);
+
+               list_for_each(p, &cache->bic_devs) {
+                       blkid_dev *dev = list_entry(p, blkid_dev, bid_devs);
+                       print_tags(dev, show, numtag);
+                       err = 0;
+               }
+       /* Add all specified devices to cache (optionally display tags) */
+       } else for (i = 0; i < numdev; i++) {
+               blkid_dev *dev = blkid_get_devname(cache, devices[i]);
+
+               if (dev) {
+                       print_tags(dev, show, numtag);
+                       err = 0;
+               }
+       }
+
+exit:
+       blkid_free_tag(tag);
+       blkid_save_cache(cache, write);
+       blkid_free_cache(cache);
+       return err;
+}