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