Integrate new blkid library.

bitmap-optimize
Theodore Ts'o 2003-01-23 16:45:16 -05:00
parent 76ea3a2c7f
commit e12f2ae74c
27 changed files with 4587 additions and 9 deletions

View File

@ -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

View 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@
#

View 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

6
configure vendored
View File

@ -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"}

View 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)

7
lib/blkid/ChangeLog Normal file
View File

@ -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.

156
lib/blkid/Makefile.in Normal file
View File

@ -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

275
lib/blkid/blkid.h Normal file
View File

@ -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 */

View File

@ -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 */

90
lib/blkid/cache.c Normal file
View File

@ -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

264
lib/blkid/dev.c Normal file
View File

@ -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

358
lib/blkid/devname.c Normal file
View File

@ -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

296
lib/blkid/devno.c Normal file
View File

@ -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

134
lib/blkid/getsize.c Normal file
View File

@ -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

96
lib/blkid/libblkid.3.in Normal file
View File

@ -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)

171
lib/blkid/list.h Normal file
View File

@ -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 */

138
lib/blkid/llseek.c Normal file
View File

@ -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 */

838
lib/blkid/probe.c Normal file
View File

@ -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

228
lib/blkid/probe.h Normal file
View File

@ -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 */

508
lib/blkid/read.c Normal file
View File

@ -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

130
lib/blkid/resolve.c Normal file
View File

@ -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

190
lib/blkid/save.c Normal file
View File

@ -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

273
lib/blkid/tag.c Normal file
View File

@ -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;
}

View 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

View 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) \

142
misc/blkid.8.in Normal file
View File

@ -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)

207
misc/blkid.c Normal file
View File

@ -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;
}