Update blkid to 2.25.0
Break libblkid into 4 libraries: libblkid, libuuid, libutil-linux and libfdisk.

This should help in later patch updates.

Change-Id: I680d9a7feb031e5c29a603e9c58aff4b65826262
diff --git a/libblkid/lib/Makemodule.am b/libblkid/lib/Makemodule.am
new file mode 100644
index 0000000..565294e
--- /dev/null
+++ b/libblkid/lib/Makemodule.am
@@ -0,0 +1,115 @@
+
+noinst_LTLIBRARIES += libcommon.la
+libcommon_la_CFLAGS = $(AM_CFLAGS)
+libcommon_la_SOURCES = \
+	lib/at.c \
+	lib/blkdev.c \
+	lib/canonicalize.c \
+	lib/colors.c \
+	lib/crc32.c \
+	lib/crc64.c \
+	lib/env.c \
+	lib/fileutils.c \
+	lib/ismounted.c \
+	lib/mangle.c \
+	lib/match.c \
+	lib/mbsalign.c \
+	lib/md5.c \
+	lib/pager.c \
+	lib/path.c \
+	lib/procutils.c \
+	lib/randutils.c \
+	lib/setproctitle.c \
+	lib/strutils.c \
+	lib/sysfs.c \
+	lib/timeutils.c \
+	lib/ttyutils.c \
+	lib/exec_shell.c \
+	lib/readutmp.c
+
+if LINUX
+libcommon_la_SOURCES += \
+	lib/linux_version.c \
+	lib/loopdev.c
+endif
+
+if !HAVE_LANGINFO
+libcommon_la_SOURCES += lib/langinfo.c
+endif
+
+if HAVE_CPU_SET_T
+libcommon_la_SOURCES += lib/cpuset.c
+endif
+
+dist_man_MANS += lib/terminal-colors.d.5
+
+check_PROGRAMS += \
+	test_at \
+	test_blkdev \
+	test_canonicalize \
+	test_colors \
+	test_fileutils \
+	test_ismounted \
+	test_mangle \
+	test_procutils \
+	test_randutils \
+	test_strutils \
+	test_ttyutils
+
+if LINUX
+if HAVE_CPU_SET_T
+check_PROGRAMS += test_cpuset
+endif
+check_PROGRAMS += \
+	test_sysfs \
+	test_pager
+endif
+
+test_ttyutils_SOURCES = lib/ttyutils.c
+test_ttyutils_CFLAGS = -DTEST_PROGRAM
+test_ttyutils_LDADD = libcommon.la
+
+test_blkdev_SOURCES = lib/blkdev.c
+test_blkdev_CFLAGS = -DTEST_PROGRAM_BLKDEV
+test_blkdev_LDADD = libcommon.la
+
+test_ismounted_SOURCES = lib/ismounted.c
+test_ismounted_CFLAGS = -DTEST_PROGRAM
+test_ismounted_LDADD = libcommon.la
+
+test_mangle_SOURCES = lib/mangle.c
+test_mangle_CFLAGS = -DTEST_PROGRAM
+
+test_at_SOURCES = lib/at.c
+test_at_CFLAGS = -DTEST_PROGRAM_AT
+
+test_strutils_SOURCES = lib/strutils.c
+test_strutils_CFLAGS = -DTEST_PROGRAM
+
+test_colors_SOURCES = lib/colors.c
+test_colors_CFLAGS = -DTEST_PROGRAM
+
+test_randutils_SOURCES = lib/randutils.c
+test_randutils_CFLAGS = -DTEST_PROGRAM
+
+test_procutils_SOURCES = lib/procutils.c lib/at.c
+test_procutils_CFLAGS = -DTEST_PROGRAM
+
+if LINUX
+test_cpuset_SOURCES = lib/cpuset.c
+test_cpuset_CFLAGS = -DTEST_PROGRAM
+
+test_sysfs_SOURCES = lib/sysfs.c
+test_sysfs_CFLAGS = -DTEST_PROGRAM_SYSFS
+test_sysfs_LDADD = libcommon.la
+
+test_pager_SOURCES = lib/pager.c
+test_pager_CFLAGS = -DTEST_PROGRAM
+endif
+
+test_fileutils_SOURCES = lib/fileutils.c
+test_fileutils_CFLAGS = -DTEST_PROGRAM
+
+test_canonicalize_SOURCES = lib/canonicalize.c
+test_canonicalize_CFLAGS = -DTEST_PROGRAM_CANONICALIZE
+
diff --git a/libblkid/lib/at.c b/libblkid/lib/at.c
new file mode 100644
index 0000000..f8bfe13
--- /dev/null
+++ b/libblkid/lib/at.c
@@ -0,0 +1,143 @@
+/*
+ * Portable xxxat() functions.
+ *
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "at.h"
+#include "c.h"
+
+#ifdef HAVE_FSTATAT
+int fstat_at(int dir, const char *dirname __attribute__ ((__unused__)),
+	     const char *filename, struct stat *st, int nofollow)
+{
+	return fstatat(dir, filename, st,
+			nofollow ? AT_SYMLINK_NOFOLLOW : 0);
+}
+#else
+int fstat_at(int dir, const char *dirname, const char *filename,
+				struct stat *st, int nofollow)
+{
+
+	if (*filename != '/') {
+		char path[PATH_MAX];
+		int len;
+
+		len = snprintf(path, sizeof(path), "%s/%s", dirname, filename);
+		if (len < 0 || len + 1 > sizeof(path))
+			return -1;
+
+		return nofollow ? lstat(path, st) : stat(path, st);
+	}
+
+	return nofollow ? lstat(filename, st) : stat(filename, st);
+}
+#endif
+
+#ifdef HAVE_FSTATAT
+int open_at(int dir, const char *dirname __attribute__ ((__unused__)),
+	    const char *filename, int flags)
+{
+	return openat(dir, filename, flags);
+}
+#else
+int open_at(int dir, const char *dirname, const char *filename, int flags)
+{
+	if (*filename != '/') {
+		char path[PATH_MAX];
+		int len;
+
+		len = snprintf(path, sizeof(path), "%s/%s", dirname, filename);
+		if (len < 0 || len + 1 > sizeof(path))
+			return -1;
+
+		return open(path, flags);
+	}
+	return open(filename, flags);
+}
+#endif
+
+FILE *fopen_at(int dir, const char *dirname, const char *filename, int flags,
+			const char *mode)
+{
+	int fd = open_at(dir, dirname, filename, flags);
+
+	if (fd < 0)
+		return NULL;
+
+	return fdopen(fd, mode);
+}
+
+#ifdef HAVE_FSTATAT
+ssize_t readlink_at(int dir, const char *dirname __attribute__ ((__unused__)),
+		    const char *pathname, char *buf, size_t bufsiz)
+{
+	return readlinkat(dir, pathname, buf, bufsiz);
+}
+#else
+ssize_t readlink_at(int dir, const char *dirname, const char *pathname,
+		    char *buf, size_t bufsiz)
+{
+	if (*pathname != '/') {
+		char path[PATH_MAX];
+		int len;
+
+		len = snprintf(path, sizeof(path), "%s/%s", dirname, pathname);
+		if (len < 0 || len + 1 > sizeof(path))
+			return -1;
+
+		return readlink(path, buf, bufsiz);
+	}
+	return readlink(pathname, buf, bufsiz);
+}
+#endif
+
+#ifdef TEST_PROGRAM_AT
+#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+	DIR *dir;
+	struct dirent *d;
+	char *dirname;
+
+	if (argc != 2) {
+		fprintf(stderr, "usage: %s <directory>\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+	dirname = argv[1];
+
+	dir = opendir(dirname);
+	if (!dir)
+		err(EXIT_FAILURE, "cannot open %s", dirname);
+
+	while ((d = readdir(dir))) {
+		struct stat st;
+		FILE *f;
+
+		printf("%32s ", d->d_name);
+
+		if (fstat_at(dirfd(dir), dirname, d->d_name, &st, 0) == 0)
+			printf("%16jd bytes ", st.st_size);
+		else
+			printf("%16s bytes ", "???");
+
+		f = fopen_at(dirfd(dir), dirname, d->d_name, O_RDONLY, "r");
+		printf("   %s\n", f ? "OK" : strerror(errno));
+		if (f)
+			fclose(f);
+	}
+	closedir(dir);
+	return EXIT_SUCCESS;
+}
+#endif
diff --git a/libblkid/lib/blkdev.c b/libblkid/lib/blkdev.c
new file mode 100644
index 0000000..a293529
--- /dev/null
+++ b/libblkid/lib/blkdev.c
@@ -0,0 +1,378 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdint.h>
+
+#ifdef HAVE_LINUX_FD_H
+#include <linux/fd.h>
+#endif
+
+#ifdef HAVE_SYS_DISKLABEL_H
+#include <sys/disklabel.h>
+#endif
+
+#ifdef HAVE_SYS_DISK_H
+#ifdef HAVE_SYS_QUEUE_H
+#include <sys/queue.h> /* for LIST_HEAD */
+#endif
+#include <sys/disk.h>
+#endif
+
+#ifdef __FreeBSD_kernel__
+#include <sys/disk.h>
+#endif
+
+#include "blkdev.h"
+#include "c.h"
+#include "linux_version.h"
+
+static long
+blkdev_valid_offset (int fd, off_t offset) {
+	char ch;
+
+	if (lseek (fd, offset, 0) < 0)
+		return 0;
+	if (read (fd, &ch, 1) < 1)
+		return 0;
+	return 1;
+}
+
+int is_blkdev(int fd)
+{
+	struct stat st;
+	return (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode));
+}
+
+off_t
+blkdev_find_size (int fd) {
+	uintmax_t high, low = 0;
+
+	for (high = 1024; blkdev_valid_offset (fd, high); ) {
+		if (high == UINTMAX_MAX)
+			return -1;
+
+		low = high;
+
+		if (high >= UINTMAX_MAX/2)
+			high = UINTMAX_MAX;
+		else
+			high *= 2;
+	}
+
+	while (low < high - 1)
+	{
+		uintmax_t mid = (low + high) / 2;
+
+		if (blkdev_valid_offset (fd, mid))
+			low = mid;
+		else
+			high = mid;
+	}
+	blkdev_valid_offset (fd, 0);
+	return (low + 1);
+}
+
+/* get size in bytes */
+int
+blkdev_get_size(int fd, unsigned long long *bytes)
+{
+#ifdef DKIOCGETBLOCKCOUNT
+	/* Apple Darwin */
+	if (ioctl(fd, DKIOCGETBLOCKCOUNT, bytes) >= 0) {
+		*bytes <<= 9;
+		return 0;
+	}
+#endif
+
+#ifdef BLKGETSIZE64
+	{
+#ifdef __linux__
+		int ver = get_linux_version();
+
+		/* kernels 2.4.15-2.4.17, had a broken BLKGETSIZE64 */
+		if (ver >= KERNEL_VERSION (2,6,0) ||
+		   (ver >= KERNEL_VERSION (2,4,18) && ver < KERNEL_VERSION (2,5,0)))
+#endif
+			if (ioctl(fd, BLKGETSIZE64, bytes) >= 0)
+				return 0;
+	}
+#endif /* BLKGETSIZE64 */
+
+#ifdef BLKGETSIZE
+	{
+		unsigned long size;
+
+		if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+			*bytes = ((unsigned long long)size << 9);
+			return 0;
+		}
+	}
+
+#endif /* BLKGETSIZE */
+
+#ifdef DIOCGMEDIASIZE
+	/* FreeBSD */
+	if (ioctl(fd, DIOCGMEDIASIZE, bytes) >= 0)
+		return 0;
+#endif
+
+#ifdef FDGETPRM
+	{
+		struct floppy_struct this_floppy;
+
+		if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) {
+			*bytes = this_floppy.size << 9;
+			return 0;
+		}
+	}
+#endif /* FDGETPRM */
+
+#ifdef HAVE_SYS_DISKLABEL_H
+	{
+		/*
+		 * This code works for FreeBSD 4.11 i386, except for the full device
+		 * (such as /dev/ad0). It doesn't work properly for newer FreeBSD
+		 * though. FreeBSD >= 5.0 should be covered by the DIOCGMEDIASIZE
+		 * above however.
+		 *
+		 * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw,
+		 * character) devices, so we need to check for S_ISCHR, too.
+		 */
+		int part = -1;
+		struct disklabel lab;
+		struct partition *pp;
+		char ch;
+		struct stat st;
+
+		if ((fstat(fd, &st) >= 0) &&
+		    (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)))
+			part = st.st_rdev & 7;
+
+		if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) {
+			pp = &lab.d_partitions[part];
+			if (pp->p_size) {
+				 *bytes = pp->p_size << 9;
+				 return 0;
+			}
+		}
+	}
+#endif /* HAVE_SYS_DISKLABEL_H */
+
+	{
+		struct stat st;
+
+		if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) {
+			*bytes = st.st_size;
+			return 0;
+		}
+		if (!S_ISBLK(st.st_mode))
+			return -1;
+	}
+
+	*bytes = blkdev_find_size(fd);
+	return 0;
+}
+
+/* get 512-byte sector count */
+int
+blkdev_get_sectors(int fd, unsigned long long *sectors)
+{
+	unsigned long long bytes;
+
+	if (blkdev_get_size(fd, &bytes) == 0) {
+		*sectors = (bytes >> 9);
+		return 0;
+	}
+
+	return -1;
+}
+
+/*
+ * Get logical sector size.
+ *
+ * This is the smallest unit the storage device can
+ * address. It is typically 512 bytes.
+ */
+int blkdev_get_sector_size(int fd, int *sector_size)
+{
+#ifdef BLKSSZGET
+	if (ioctl(fd, BLKSSZGET, sector_size) >= 0)
+		return 0;
+	return -1;
+#else
+	*sector_size = DEFAULT_SECTOR_SIZE;
+	return 0;
+#endif
+}
+
+/*
+ * Get physical block device size. The BLKPBSZGET is supported since Linux
+ * 2.6.32. For old kernels is probably the best to assume that physical sector
+ * size is the same as logical sector size.
+ *
+ * Example:
+ *
+ * rc = blkdev_get_physector_size(fd, &physec);
+ * if (rc || physec == 0) {
+ *	rc = blkdev_get_sector_size(fd, &physec);
+ *	if (rc)
+ *		physec = DEFAULT_SECTOR_SIZE;
+ * }
+ */
+int blkdev_get_physector_size(int fd, int *sector_size)
+{
+#ifdef BLKPBSZGET
+	if (ioctl(fd, BLKPBSZGET, &sector_size) >= 0)
+		return 0;
+	return -1;
+#else
+	*sector_size = DEFAULT_SECTOR_SIZE;
+	return 0;
+#endif
+}
+
+/*
+ * Return the alignment status of a device
+ */
+int blkdev_is_misaligned(int fd)
+{
+#ifdef BLKALIGNOFF
+	int aligned;
+
+	if (ioctl(fd, BLKALIGNOFF, &aligned) < 0)
+		return 0;			/* probably kernel < 2.6.32 */
+	/*
+	 * Note that kernel returns -1 as alignement offset if no compatible
+	 * sizes and alignments exist for stacked devices
+	 */
+	return aligned != 0 ? 1 : 0;
+#else
+	return 0;
+#endif
+}
+
+int blkdev_is_cdrom(int fd)
+{
+#ifdef CDROM_GET_CAPABILITY
+	int ret;
+
+	if ((ret = ioctl(fd, CDROM_GET_CAPABILITY, NULL)) < 0)
+		return 0;
+	else
+		return ret;
+#else
+	return 0;
+#endif
+}
+
+/*
+ * Get kernel's interpretation of the device's geometry.
+ *
+ * Returns the heads and sectors - but not cylinders
+ * as it's truncated for disks with more than 65535 tracks.
+ *
+ * Note that this is deprecated in favor of LBA addressing.
+ */
+int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s)
+{
+#ifdef HDIO_GETGEO
+	struct hd_geometry geometry;
+
+	if (ioctl(fd, HDIO_GETGEO, &geometry) == 0) {
+		*h = geometry.heads;
+		*s = geometry.sectors;
+		return 0;
+	}
+#else
+	*h = 0;
+	*s = 0;
+#endif
+	return -1;
+}
+
+/*
+ * Convert scsi type to human readable string.
+ */
+const char *blkdev_scsi_type_to_name(int type)
+{
+	switch (type) {
+	case SCSI_TYPE_DISK:
+		return "disk";
+	case SCSI_TYPE_TAPE:
+		return "tape";
+	case SCSI_TYPE_PRINTER:
+		return "printer";
+	case SCSI_TYPE_PROCESSOR:
+		return "processor";
+	case SCSI_TYPE_WORM:
+		return "worm";
+	case SCSI_TYPE_ROM:
+		return "rom";
+	case SCSI_TYPE_SCANNER:
+		return "scanner";
+	case SCSI_TYPE_MOD:
+		return "mo-disk";
+	case SCSI_TYPE_MEDIUM_CHANGER:
+		return "changer";
+	case SCSI_TYPE_COMM:
+		return "comm";
+	case SCSI_TYPE_RAID:
+		return "raid";
+	case SCSI_TYPE_ENCLOSURE:
+		return "enclosure";
+	case SCSI_TYPE_RBC:
+		return "rbc";
+	case SCSI_TYPE_OSD:
+		return "osd";
+	case SCSI_TYPE_NO_LUN:
+		return "no-lun";
+	default:
+		break;
+	}
+	return NULL;
+}
+
+#ifdef TEST_PROGRAM_BLKDEV
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+int
+main(int argc, char **argv)
+{
+	unsigned long long bytes;
+	unsigned long long sectors;
+	int sector_size, phy_sector_size;
+	int fd;
+
+	if (argc != 2) {
+		fprintf(stderr, "usage: %s device\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if ((fd = open(argv[1], O_RDONLY|O_CLOEXEC)) < 0)
+		err(EXIT_FAILURE, "open %s failed", argv[1]);
+
+	if (blkdev_get_size(fd, &bytes) < 0)
+		err(EXIT_FAILURE, "blkdev_get_size() failed");
+	if (blkdev_get_sectors(fd, &sectors) < 0)
+		err(EXIT_FAILURE, "blkdev_get_sectors() failed");
+	if (blkdev_get_sector_size(fd, &sector_size) < 0)
+		err(EXIT_FAILURE, "blkdev_get_sector_size() failed");
+	if (blkdev_get_physector_size(fd, &phy_sector_size) < 0)
+		err(EXIT_FAILURE, "blkdev_get_physector_size() failed");
+
+	printf("          bytes: %llu\n", bytes);
+	printf("        sectors: %llu\n", sectors);
+	printf("    sector size: %d\n", sector_size);
+	printf("phy-sector size: %d\n", phy_sector_size);
+
+	return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/lib/canonicalize.c b/libblkid/lib/canonicalize.c
new file mode 100644
index 0000000..303703b
--- /dev/null
+++ b/libblkid/lib/canonicalize.c
@@ -0,0 +1,145 @@
+/*
+ * canonicalize.c -- canonicalize pathname by removing symlinks
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Copyright (C) 2009-2013 Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "canonicalize.h"
+
+/*
+ * Converts private "dm-N" names to "/dev/mapper/<name>"
+ *
+ * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs
+ * provides the real DM device names in /sys/block/<ptname>/dm/name
+ */
+char *canonicalize_dm_name(const char *ptname)
+{
+	FILE	*f;
+	size_t	sz;
+	char	path[256], name[256], *res = NULL;
+
+	if (!ptname || !*ptname)
+		return NULL;
+
+	snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname);
+	if (!(f = fopen(path, "r")))
+		return NULL;
+
+	/* read "<name>\n" from sysfs */
+	if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) {
+		name[sz - 1] = '\0';
+		snprintf(path, sizeof(path), "/dev/mapper/%s", name);
+
+		if (access(path, F_OK) == 0)
+			res = strdup(path);
+	}
+	fclose(f);
+	return res;
+}
+
+static int is_dm_devname(char *canonical, char **name)
+{
+	struct stat sb;
+	char *p = strrchr(canonical, '/');
+
+	*name = NULL;
+
+	if (!p
+	    || strncmp(p, "/dm-", 4) != 0
+	    || !isdigit(*(p + 4))
+	    || stat(canonical, &sb) != 0
+	    || !S_ISBLK(sb.st_mode))
+		return 0;
+
+	*name = p + 1;
+	return 1;
+}
+
+char *canonicalize_path(const char *path)
+{
+	char *canonical, *dmname;
+
+	if (!path || !*path)
+		return NULL;
+
+	canonical = realpath(path, NULL);
+	if (!canonical)
+		return strdup(path);
+
+	if (is_dm_devname(canonical, &dmname)) {
+		char *dm = canonicalize_dm_name(dmname);
+		if (dm) {
+			free(canonical);
+			return dm;
+		}
+	}
+
+	return canonical;
+}
+
+char *canonicalize_path_restricted(const char *path)
+{
+	char *canonical, *dmname;
+	int errsv;
+	uid_t euid;
+	gid_t egid;
+
+	if (!path || !*path)
+		return NULL;
+
+	euid = geteuid();
+	egid = getegid();
+
+	/* drop permissions */
+	if (setegid(getgid()) < 0 || seteuid(getuid()) < 0)
+		return NULL;
+
+	errsv = errno = 0;
+
+	canonical = realpath(path, NULL);
+	if (!canonical)
+		errsv = errno;
+	else if (is_dm_devname(canonical, &dmname)) {
+		char *dm = canonicalize_dm_name(dmname);
+		if (dm) {
+			free(canonical);
+			canonical = dm;
+		}
+	}
+
+	/* restore */
+	if (setegid(egid) < 0 || seteuid(euid) < 0) {
+		free(canonical);
+		return NULL;
+	}
+
+	errno = errsv;
+	return canonical;
+}
+
+
+#ifdef TEST_PROGRAM_CANONICALIZE
+int main(int argc, char **argv)
+{
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <device>\n", argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	fprintf(stdout, "orig: %s\n", argv[1]);
+	fprintf(stdout, "real: %s\n", canonicalize_path(argv[1]));
+
+	exit(EXIT_SUCCESS);
+}
+#endif
diff --git a/libblkid/lib/colors.c b/libblkid/lib/colors.c
new file mode 100644
index 0000000..6f79ac4
--- /dev/null
+++ b/libblkid/lib/colors.c
@@ -0,0 +1,877 @@
+/*
+ * Copyright (C) 2012 Ondrej Oprala <ooprala@redhat.com>
+ * Copyright (C) 2012-2014 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be distributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <assert.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include "c.h"
+#include "colors.h"
+#include "pathnames.h"
+#include "strutils.h"
+
+#include "debug.h"
+
+/*
+ * terminal-colors.d debug stuff
+ */
+UL_DEBUG_DEFINE_MASK(termcolors);
+UL_DEBUG_DEFINE_MASKNAMES(termcolors) = UL_DEBUG_EMPTY_MASKNAMES;
+
+#define TERMCOLORS_DEBUG_INIT	(1 << 1)
+#define TERMCOLORS_DEBUG_CONF	(1 << 2)
+#define TERMCOLORS_DEBUG_SCHEME	(1 << 3)
+#define TERMCOLORS_DEBUG_ALL	0xFFFF
+
+#define DBG(m, x)       __UL_DBG(termcolors, TERMCOLORS_DEBUG_, m, x)
+#define ON_DBG(m, x)    __UL_DBG_CALL(termcolors, TERMCOLORS_DEBUG_, m, x)
+
+/*
+ * terminal-colors.d file types
+ */
+enum {
+	UL_COLORFILE_DISABLE,		/* .disable */
+	UL_COLORFILE_ENABLE,		/* .enable */
+	UL_COLORFILE_SCHEME,		/* .scheme */
+
+	__UL_COLORFILE_COUNT
+};
+
+struct ul_color_scheme {
+	char *name;
+	char *seq;
+};
+
+/*
+ * Global colors control struct
+ *
+ * The terminal-colors.d/ evaluation is based on "scores":
+ *
+ *  filename                    score
+ *  ---------------------------------------
+ *  type			1
+ *  @termname.type		10 + 1
+ *  utilname.type		20 + 1
+ *  utilname@termname.type	20 + 10 + 1
+ *
+ * the match with higher score wins. The score is per type.
+ */
+struct ul_color_ctl {
+	const char	*utilname;	/* util name */
+	const char	*termname;	/* terminal name ($TERM) */
+
+	char		*sfile;	/* path to scheme */
+
+	struct ul_color_scheme	*schemes;	/* array with color schemes */
+	size_t			nschemes;	/* number of the items */
+	size_t			schemes_sz;	/* number of the allocated items */
+
+	int		mode;		/* UL_COLORMODE_* */
+	unsigned int	has_colors : 1,	/* based on mode and scores[] */
+			disabled   : 1, /* disable colors */
+			cs_configured : 1, /* color schemes read */
+			configured : 1; /* terminal-colors.d parsed */
+
+	int		scores[__UL_COLORFILE_COUNT];	/* the best match */
+};
+
+/*
+ * Control struct, globally shared.
+ */
+static struct ul_color_ctl ul_colors;
+
+static void colors_free_schemes(struct ul_color_ctl *cc);
+static int colors_read_schemes(struct ul_color_ctl *cc);
+
+/*
+ * qsort/bsearch buddy
+ */
+static int cmp_scheme_name(const void *a0, const void *b0)
+{
+	struct ul_color_scheme	*a = (struct ul_color_scheme *) a0,
+				*b = (struct ul_color_scheme *) b0;
+	return strcmp(a->name, b->name);
+}
+
+/*
+ * Maintains human readable color names
+ */
+const char *color_sequence_from_colorname(const char *str)
+{
+	static const struct ul_color_scheme basic_schemes[] = {
+		{ "black",	UL_COLOR_BLACK           },
+		{ "blue",	UL_COLOR_BLUE            },
+		{ "brown",	UL_COLOR_BROWN           },
+		{ "cyan",	UL_COLOR_CYAN            },
+		{ "darkgray",	UL_COLOR_DARK_GRAY       },
+		{ "gray",	UL_COLOR_GRAY            },
+		{ "green",	UL_COLOR_GREEN           },
+		{ "lightblue",	UL_COLOR_BOLD_BLUE       },
+		{ "lightcyan",	UL_COLOR_BOLD_CYAN       },
+		{ "lightgray,",	UL_COLOR_GRAY            },
+		{ "lightgreen", UL_COLOR_BOLD_GREEN      },
+		{ "lightmagenta", UL_COLOR_BOLD_MAGENTA  },
+		{ "lightred",	UL_COLOR_BOLD_RED        },
+		{ "magenta",	UL_COLOR_MAGENTA         },
+		{ "red",	UL_COLOR_RED             },
+		{ "yellow",	UL_COLOR_BOLD_YELLOW     },
+	};
+	struct ul_color_scheme key = { .name = (char *) str }, *res;
+
+	if (!str)
+		return NULL;
+
+	res = bsearch(&key, basic_schemes, ARRAY_SIZE(basic_schemes),
+				sizeof(struct ul_color_scheme),
+				cmp_scheme_name);
+	return res ? res->seq : NULL;
+}
+
+
+/*
+ * Resets control struct (note that we don't allocate the struct)
+ */
+static void colors_reset(struct ul_color_ctl *cc)
+{
+	if (!cc)
+		return;
+
+	colors_free_schemes(cc);
+
+	free(cc->sfile);
+
+	cc->sfile = NULL;
+	cc->utilname = NULL;
+	cc->termname = NULL;
+	cc->mode = UL_COLORMODE_UNDEF;
+
+	memset(cc->scores, 0, sizeof(cc->scores));
+}
+
+static void colors_debug(struct ul_color_ctl *cc)
+{
+	size_t i;
+
+	if (!cc)
+		return;
+
+	printf("Colors:\n");
+	printf("\tutilname = '%s'\n", cc->utilname);
+	printf("\ttermname = '%s'\n", cc->termname);
+	printf("\tscheme file = '%s'\n", cc->sfile);
+	printf("\tmode = %s\n",
+			cc->mode == UL_COLORMODE_UNDEF ? "undefined" :
+			cc->mode == UL_COLORMODE_AUTO ?  "auto" :
+			cc->mode == UL_COLORMODE_NEVER ? "never" :
+			cc->mode == UL_COLORMODE_ALWAYS ? "always" : "???");
+	printf("\thas_colors = %d\n", cc->has_colors);
+	printf("\tdisabled = %d\n", cc->disabled);
+	printf("\tconfigured = %d\n", cc->configured);
+	printf("\tcs configured = %d\n", cc->cs_configured);
+
+	fputc('\n', stdout);
+
+	for (i = 0; i < ARRAY_SIZE(cc->scores); i++)
+		printf("\tscore %s = %d\n",
+				i == UL_COLORFILE_DISABLE ? "disable" :
+				i == UL_COLORFILE_ENABLE ? "enable" :
+				i == UL_COLORFILE_SCHEME ? "scheme" : "???",
+				cc->scores[i]);
+
+	fputc('\n', stdout);
+
+	for (i = 0; i < cc->nschemes; i++) {
+		printf("\tscheme #%02zu ", i);
+		color_scheme_enable(cc->schemes[i].name, NULL);
+		fputs(cc->schemes[i].name, stdout);
+		color_disable();
+		fputc('\n', stdout);
+	}
+	fputc('\n', stdout);
+}
+
+/*
+ * Parses [[<utilname>][@<termname>].]<type>
+ */
+static int filename_to_tokens(const char *str,
+			      const char **name, size_t *namesz,
+			      const char **term, size_t *termsz,
+			      int  *filetype)
+{
+	const char *type_start, *term_start, *p;
+
+	if (!str || !*str || *str == '.' || strlen(str) > PATH_MAX)
+		return -EINVAL;
+
+	/* parse .type */
+	p = strrchr(str, '.');
+	type_start = p ? p + 1 : str;
+
+	if (strcmp(type_start, "disable") == 0)
+		*filetype = UL_COLORFILE_DISABLE;
+	else if (strcmp(type_start, "enable") == 0)
+		*filetype = UL_COLORFILE_ENABLE;
+	else if (strcmp(type_start, "scheme") == 0)
+		*filetype = UL_COLORFILE_SCHEME;
+	else {
+		DBG(CONF, ul_debug("unknown type '%s'", type_start));
+		return 1;				/* unknown type */
+	}
+
+	if (type_start == str)
+		return 0;				/* "type" only */
+
+	/* parse @termname */
+	p = strchr(str, '@');
+	term_start = p ? p + 1 : NULL;
+	if (term_start) {
+		*term = term_start;
+		*termsz = type_start - term_start - 1;
+		if (term_start - 1 == str)
+			return 0;			/* "@termname.type" */
+	}
+
+	/* parse utilname */
+	p = term_start ? term_start : type_start;
+	*name =  str;
+	*namesz	= p - str - 1;
+
+	return 0;
+}
+
+/*
+ * Scans @dirname and select the best matches for UL_COLORFILE_* types.
+ * The result is stored to cc->scores. The path to the best "scheme"
+ * file is stored to cc->scheme.
+ */
+static int colors_readdir(struct ul_color_ctl *cc, const char *dirname)
+{
+	DIR *dir;
+	int rc = 0;
+	struct dirent *d;
+	char sfile[PATH_MAX] = { '\0' };
+	size_t namesz, termsz;
+
+	if (!dirname || !cc || !cc->utilname || !*cc->utilname)
+		return -EINVAL;
+
+	DBG(CONF, ul_debug("reading dir: '%s'", dirname));
+
+	dir = opendir(dirname);
+	if (!dir)
+		return -errno;
+
+	namesz = strlen(cc->utilname);
+	termsz = cc->termname ? strlen(cc->termname) : 0;
+
+	while ((d = readdir(dir))) {
+		int type, score = 1;
+		const char *tk_name = NULL, *tk_term = NULL;
+		size_t tk_namesz = 0, tk_termsz = 0;
+
+		if (*d->d_name == '.')
+			continue;
+#ifdef _DIRENT_HAVE_D_TYPE
+		if (d->d_type != DT_UNKNOWN && d->d_type != DT_LNK &&
+		    d->d_type != DT_REG)
+			continue;
+#endif
+		if (filename_to_tokens(d->d_name,
+				       &tk_name, &tk_namesz,
+				       &tk_term, &tk_termsz, &type) != 0)
+			continue;
+
+		/* count teoretical score before we check names to avoid
+		 * unnecessary strcmp() */
+		if (tk_name)
+			score += 20;
+		if (tk_term)
+			score += 10;
+
+		DBG(CONF, ul_debug("item '%s': score=%d "
+			"[cur: %d, name(%zu): %s, term(%zu): %s]",
+			d->d_name, score, cc->scores[type],
+			tk_namesz, tk_name,
+			tk_termsz, tk_term));
+
+
+		if (score < cc->scores[type])
+			continue;
+
+		/* filter out by names */
+		if (tk_namesz && (tk_namesz != namesz ||
+				 strncmp(tk_name, cc->utilname, namesz) != 0))
+			continue;
+
+		if (tk_termsz && (termsz == 0 || tk_termsz != termsz ||
+				  strncmp(tk_term, cc->termname, termsz) != 0))
+			continue;
+
+		DBG(CONF, ul_debug("setting '%s' from %d -to-> %d",
+					type == UL_COLORFILE_SCHEME ? "scheme" :
+					type == UL_COLORFILE_DISABLE ? "disable" :
+					type == UL_COLORFILE_ENABLE ? "enable" : "???",
+					cc->scores[type], score));
+		cc->scores[type] = score;
+		if (type == UL_COLORFILE_SCHEME)
+			strncpy(sfile, d->d_name, sizeof(sfile));
+	}
+
+	if (*sfile) {
+		sfile[sizeof(sfile) - 1] = '\0';
+		if (asprintf(&cc->sfile, "%s/%s", dirname, sfile) <= 0)
+			rc = -ENOMEM;
+	}
+
+	closedir(dir);
+	return rc;
+}
+
+/* atexit() wrapper */
+static void colors_deinit(void)
+{
+	colors_reset(&ul_colors);
+}
+
+/*
+ * Returns path to $XDG_CONFIG_HOME/terminal-colors.d
+ */
+static char *colors_get_homedir(char *buf, size_t bufsz)
+{
+	char *p = getenv("XDG_CONFIG_HOME");
+
+	if (p) {
+		snprintf(buf, bufsz, "%s/" _PATH_TERMCOLORS_DIRNAME, p);
+		return buf;
+	}
+
+	p = getenv("HOME");
+	if (p) {
+		snprintf(buf, bufsz, "%s/.config/" _PATH_TERMCOLORS_DIRNAME, p);
+		return buf;
+	}
+
+	return NULL;
+}
+
+/* canonicalize sequence */
+static int cn_sequence(const char *str, char **seq)
+{
+	char *in, *out;
+
+	if (!str)
+		return -EINVAL;
+
+	*seq = NULL;
+
+	/* convert logical names like "red" to the real sequence */
+	if (*str != '\\' && isalpha(*str)) {
+		const char *s = color_sequence_from_colorname(str);
+		*seq = strdup(s ? s : str);
+
+		return *seq ? 0 : -ENOMEM;
+	}
+
+	/* convert xx;yy sequences to "\033[xx;yy" */
+	if (asprintf(seq, "\033[%sm", str) < 1)
+		return -ENOMEM;
+
+	for (in = *seq, out = *seq; in && *in; in++) {
+		if (*in != '\\') {
+			*out++ = *in;
+			continue;
+		}
+		switch(*(in + 1)) {
+		case 'a':
+			*out++ = '\a';	/* Bell */
+			break;
+		case 'b':
+			*out++ = '\b';	/* Backspace */
+			break;
+		case 'e':
+			*out++ = '\033';	/* Escape */
+			break;
+		case 'f':
+			*out++ = '\f';	/* Form Feed */
+			break;
+		case 'n':
+			*out++ = '\n';	/* Newline */
+			break;
+		case 'r':
+			*out++ = '\r';	/* Carriage Return */
+			break;
+		case 't':
+			*out++ = '\t';	/* Tab */
+			break;
+		case 'v':
+			*out++ = '\v';	/* Vertical Tab */
+			break;
+		case '\\':
+			*out++ = '\\';	/* Backslash */
+			break;
+		case '_':
+			*out++ = ' ';	/* Space */
+			break;
+		case '#':
+			*out++ = '#';	/* Hash mark */
+			break;
+		case '?':
+			*out++ = '?';	/* Qestion mark */
+			break;
+		default:
+			*out++ = *in;
+			*out++ = *(in + 1);
+			break;
+		}
+		in++;
+	}
+	*out = '\0';
+
+	return 0;
+}
+
+
+/*
+ * Adds one color sequence to array with color scheme.
+ * When returning success (0) this function takes ownership of
+ * @seq and @name, which have to be allocated strings.
+ */
+static int colors_add_scheme(struct ul_color_ctl *cc,
+			     char *name,
+			     char *seq0)
+{
+	struct ul_color_scheme *cs = NULL;
+	char *seq = NULL;
+	int rc;
+
+	if (!cc || !name || !*name || !seq0 || !*seq0)
+		return -EINVAL;
+
+	DBG(SCHEME, ul_debug("add '%s'", name));
+
+	rc = cn_sequence(seq0, &seq);
+	if (rc)
+		return rc;
+
+	rc = -ENOMEM;
+
+	/* convert logical name (e.g. "red") to real ESC code */
+	if (isalpha(*seq)) {
+		const char *s = color_sequence_from_colorname(seq);
+		char *p;
+
+		if (!s) {
+			DBG(SCHEME, ul_debug("unknown logical name: %s", seq));
+			rc = -EINVAL;
+			goto err;
+		}
+
+		p = strdup(s);
+		if (!p)
+			goto err;
+		free(seq);
+		seq = p;
+	}
+
+	/* enlarge the array */
+	if (cc->nschemes == cc->schemes_sz) {
+		void *tmp = realloc(cc->schemes, (cc->nschemes + 10)
+					* sizeof(struct ul_color_scheme));
+		if (!tmp)
+			goto err;
+		cc->schemes = tmp;
+		cc->schemes_sz = cc->nschemes + 10;
+	}
+
+	/* add a new item */
+	cs = &cc->schemes[cc->nschemes];
+	cs->seq = seq;
+	cs->name = strdup(name);
+	if (!cs->name)
+		goto err;
+
+	cc->nschemes++;
+	return 0;
+err:
+	if (cs) {
+		free(cs->seq);
+		free(cs->name);
+		cs->seq = cs->name = NULL;
+	} else
+		free(seq);
+	return rc;
+}
+
+/*
+ * Deallocates all regards to color schemes
+ */
+static void colors_free_schemes(struct ul_color_ctl *cc)
+{
+	size_t i;
+
+	DBG(SCHEME, ul_debug("free scheme"));
+
+	for (i = 0; i < cc->nschemes; i++) {
+		free(cc->schemes[i].name);
+		free(cc->schemes[i].seq);
+	}
+
+	free(cc->schemes);
+	cc->schemes = NULL;
+	cc->nschemes = 0;
+	cc->schemes_sz = 0;
+}
+
+/*
+ * The scheme configuration has to be sorted for bsearch
+ */
+static void colors_sort_schemes(struct ul_color_ctl *cc)
+{
+	if (!cc->nschemes)
+		return;
+
+	DBG(SCHEME, ul_debug("sort scheme"));
+
+	qsort(cc->schemes, cc->nschemes,
+	      sizeof(struct ul_color_scheme), cmp_scheme_name);
+}
+
+/*
+ * Returns just one color scheme
+ */
+static struct ul_color_scheme *colors_get_scheme(struct ul_color_ctl *cc,
+						 const char *name)
+{
+	struct ul_color_scheme key = { .name = (char *) name}, *res;
+
+	if (!cc || !name || !*name)
+		return NULL;
+
+	if (!cc->cs_configured) {
+		int rc = colors_read_schemes(cc);
+		if (rc)
+			return NULL;
+	}
+	if (!cc->nschemes)
+		return NULL;
+
+	DBG(SCHEME, ul_debug("search '%s'", name));
+
+	res = bsearch(&key, cc->schemes, cc->nschemes,
+				sizeof(struct ul_color_scheme),
+				cmp_scheme_name);
+
+	return res && res->seq ? res  : NULL;
+}
+
+/*
+ * Parses filenames in terminal-colors.d
+ */
+static int colors_read_configuration(struct ul_color_ctl *cc)
+{
+	int rc = -ENOENT;
+	char *dirname, buf[PATH_MAX];
+
+	cc->termname = getenv("TERM");
+
+	dirname = colors_get_homedir(buf, sizeof(buf));
+	if (dirname)
+		rc = colors_readdir(cc, dirname);		/* ~/.config */
+	if (rc == -EPERM || rc == -EACCES || rc == -ENOENT)
+		rc = colors_readdir(cc, _PATH_TERMCOLORS_DIR);	/* /etc */
+
+	cc->configured = 1;
+	return rc;
+}
+
+/*
+ * Reads terminal-colors.d/ scheme file into array schemes
+ */
+static int colors_read_schemes(struct ul_color_ctl *cc)
+{
+	int rc = 0;
+	FILE *f = NULL;
+	char buf[BUFSIZ],
+	     cn[129], seq[129];
+
+	if (!cc->configured)
+		rc = colors_read_configuration(cc);
+
+	cc->cs_configured = 1;
+
+	if (rc || !cc->sfile)
+		goto done;
+
+	DBG(SCHEME, ul_debug("reading file '%s'", cc->sfile));
+
+	f = fopen(cc->sfile, "r");
+	if (!f) {
+		rc = -errno;
+		goto done;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		char *p = strchr(buf, '\n');
+
+		if (!p) {
+			if (feof(f))
+				p = strchr(buf, '\0');
+			else {
+				rc = -errno;
+				goto done;
+			}
+		}
+		*p = '\0';
+		p = (char *) skip_blank(buf);
+		if (*p == '\0' || *p == '#')
+			continue;
+
+		rc = sscanf(p, "%128[^ ] %128[^\n ]", cn, seq);
+		if (rc == 2 && *cn && *seq) {
+			rc = colors_add_scheme(cc, cn, seq);	/* set rc=0 on success */
+			if (rc)
+				goto done;
+		}
+	}
+	rc = 0;
+
+done:
+	if (f)
+		fclose(f);
+	colors_sort_schemes(cc);
+
+	return rc;
+}
+
+
+static void termcolors_init_debug(void)
+{
+	__UL_INIT_DEBUG(termcolors, TERMCOLORS_DEBUG_, 0, TERMINAL_COLORS_DEBUG);
+}
+
+/**
+ * colors_init:
+ * @mode: UL_COLORMODE_*
+ * @name: util argv[0]
+ *
+ * Initialize private color control struct and initialize the colors
+ * status. The color schemes are parsed on demand by colors_get_scheme().
+ *
+ * Returns: >0 on success.
+ */
+int colors_init(int mode, const char *name)
+{
+	int atty = -1;
+	struct ul_color_ctl *cc = &ul_colors;
+
+	cc->utilname = name;
+	cc->mode = mode;
+
+	termcolors_init_debug();
+
+	if (mode == UL_COLORMODE_UNDEF && (atty = isatty(STDOUT_FILENO))) {
+		int rc = colors_read_configuration(cc);
+		if (rc)
+			cc->mode = UL_COLORMODE_AUTO;
+		else {
+
+			/* evaluate scores */
+			if (cc->scores[UL_COLORFILE_DISABLE] >
+			    cc->scores[UL_COLORFILE_ENABLE])
+				cc->mode = UL_COLORMODE_NEVER;
+			else
+				cc->mode = UL_COLORMODE_AUTO;
+
+			atexit(colors_deinit);
+		}
+	}
+
+	switch (cc->mode) {
+	case UL_COLORMODE_AUTO:
+		cc->has_colors = atty == -1 ? isatty(STDOUT_FILENO) : atty;
+		break;
+	case UL_COLORMODE_ALWAYS:
+		cc->has_colors = 1;
+		break;
+	case UL_COLORMODE_NEVER:
+	default:
+		cc->has_colors = 0;
+	}
+
+	ON_DBG(CONF, colors_debug(cc));
+
+	return cc->has_colors;
+}
+
+/*
+ * Temporary disable colors (this setting is independent on terminal-colors.d/)
+ */
+void colors_off(void)
+{
+	ul_colors.disabled = 1;
+}
+
+/*
+ * Enable colors
+ */
+void colors_on(void)
+{
+	ul_colors.disabled = 0;
+}
+
+/*
+ * Is terminal-colors.d/ configured to use colors?
+ */
+int colors_wanted(void)
+{
+	return ul_colors.has_colors;
+}
+
+/*
+ * Enable @seq color
+ */
+void color_fenable(const char *seq, FILE *f)
+{
+	if (!ul_colors.disabled && ul_colors.has_colors && seq)
+		fputs(seq, f);
+}
+
+/*
+ * Returns escape sequence by logical @name, if undefined then returns @dflt.
+ */
+const char *color_scheme_get_sequence(const char *name, const char *dflt)
+{
+	struct ul_color_scheme *cs;
+
+	if (ul_colors.disabled || !ul_colors.has_colors)
+		return NULL;
+
+	cs = colors_get_scheme(&ul_colors, name);
+	return cs && cs->seq ? cs->seq : dflt;
+}
+
+/*
+ * Enable color by logical @name, if undefined enable @dflt.
+ */
+void color_scheme_fenable(const char *name, const char *dflt, FILE *f)
+{
+	const char *seq = color_scheme_get_sequence(name, dflt);
+
+	if (!seq)
+		return;
+	color_fenable(seq, f);
+}
+
+
+/*
+ * Disable previously enabled color
+ */
+void color_fdisable(FILE *f)
+{
+	if (!ul_colors.disabled && ul_colors.has_colors)
+		fputs(UL_COLOR_RESET, f);
+}
+
+/*
+ * Parses @str to return UL_COLORMODE_*
+ */
+int colormode_from_string(const char *str)
+{
+	size_t i;
+	static const char *modes[] = {
+		[UL_COLORMODE_AUTO]   = "auto",
+		[UL_COLORMODE_NEVER]  = "never",
+		[UL_COLORMODE_ALWAYS] = "always",
+		[UL_COLORMODE_UNDEF] = ""
+	};
+
+	if (!str || !*str)
+		return -EINVAL;
+
+	assert(ARRAY_SIZE(modes) == __UL_NCOLORMODES);
+
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (strcasecmp(str, modes[i]) == 0)
+			return i;
+	}
+
+	return -EINVAL;
+}
+
+/*
+ * Parses @str and exit(EXIT_FAILURE) on error
+ */
+int colormode_or_err(const char *str, const char *errmsg)
+{
+	const char *p = str && *str == '=' ? str + 1 : str;
+	int colormode;
+
+	colormode = colormode_from_string(p);
+	if (colormode < 0)
+		errx(EXIT_FAILURE, "%s: '%s'", errmsg, p);
+
+	return colormode;
+}
+
+#ifdef TEST_PROGRAM
+# include <getopt.h>
+int main(int argc, char *argv[])
+{
+	static const struct option longopts[] = {
+		{ "mode",	required_argument, 0, 'm' },
+		{ "color",	required_argument, 0, 'c' },
+		{ "color-scheme", required_argument, 0, 'C' },
+		{ "name",	required_argument, 0, 'n' },
+		{ NULL, 0, 0, 0 }
+	};
+	int c, mode = UL_COLORMODE_UNDEF;	/* default */
+	const char *color = "red", *name = NULL, *color_scheme = NULL;
+	const char *seq = NULL;
+
+	while ((c = getopt_long(argc, argv, "C:c:m:n:", longopts, NULL)) != -1) {
+		switch (c) {
+		case 'c':
+			color = optarg;
+			break;
+		case 'C':
+			color_scheme = optarg;
+			break;
+		case 'm':
+			mode = colormode_or_err(optarg, "unsupported color mode");
+			break;
+		case 'n':
+			name = optarg;
+			break;
+		default:
+			fprintf(stderr, "usage: %s [options]\n"
+			" -m, --mode <auto|never|always>  default is undefined\n"
+			" -c, --color <red|blue|...>      color for the test message\n"
+			" -C, --color-scheme <name>       color for the test message\n"
+			" -n, --name <utilname>           util name\n",
+			program_invocation_short_name);
+			return EXIT_FAILURE;
+		}
+	}
+
+	colors_init(mode, name ? name : program_invocation_short_name);
+
+	seq = color_sequence_from_colorname(color);
+
+	if (color_scheme)
+		color_scheme_enable(color_scheme, seq);
+	else
+		color_enable(seq);
+	printf("Hello World!");
+	color_disable();
+	fputc('\n', stdout);
+
+	return EXIT_SUCCESS;
+}
+#endif
+
diff --git a/libblkid/lib/cpuset.c b/libblkid/lib/cpuset.c
new file mode 100644
index 0000000..d715720
--- /dev/null
+++ b/libblkid/lib/cpuset.c
@@ -0,0 +1,403 @@
+/*
+ * Terminology:
+ *
+ *	cpuset	- (libc) cpu_set_t data structure represents set of CPUs
+ *	cpumask	- string with hex mask (e.g. "0x00000001")
+ *	cpulist - string with CPU ranges (e.g. "0-3,5,7,8")
+ *
+ * Based on code from taskset.c and Linux kernel.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sched.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/syscall.h>
+
+#include "cpuset.h"
+#include "c.h"
+
+static inline int val_to_char(int v)
+{
+	if (v >= 0 && v < 10)
+		return '0' + v;
+	else if (v >= 10 && v < 16)
+		return ('a' - 10) + v;
+	else
+		return -1;
+}
+
+static inline int char_to_val(int c)
+{
+	int cl;
+
+	cl = tolower(c);
+	if (c >= '0' && c <= '9')
+		return c - '0';
+	else if (cl >= 'a' && cl <= 'f')
+		return cl + (10 - 'a');
+	else
+		return -1;
+}
+
+static const char *nexttoken(const char *q,  int sep)
+{
+	if (q)
+		q = strchr(q, sep);
+	if (q)
+		q++;
+	return q;
+}
+
+/*
+ * Number of bits in a CPU bitmask on current system
+ */
+int get_max_number_of_cpus(void)
+{
+#ifdef SYS_sched_getaffinity
+	int n, cpus = 2048;
+	size_t setsize;
+	cpu_set_t *set = cpuset_alloc(cpus, &setsize, NULL);
+
+	if (!set)
+		return -1;	/* error */
+
+	for (;;) {
+		CPU_ZERO_S(setsize, set);
+
+		/* the library version does not return size of cpumask_t */
+		n = syscall(SYS_sched_getaffinity, 0, setsize, set);
+
+		if (n < 0 && errno == EINVAL && cpus < 1024 * 1024) {
+			cpuset_free(set);
+			cpus *= 2;
+			set = cpuset_alloc(cpus, &setsize, NULL);
+			if (!set)
+				return -1;	/* error */
+			continue;
+		}
+		cpuset_free(set);
+		return n * 8;
+	}
+#endif
+	return -1;
+}
+
+/*
+ * Allocates a new set for ncpus and returns size in bytes and size in bits
+ */
+cpu_set_t *cpuset_alloc(int ncpus, size_t *setsize, size_t *nbits)
+{
+	cpu_set_t *set = CPU_ALLOC(ncpus);
+
+	if (!set)
+		return NULL;
+	if (setsize)
+		*setsize = CPU_ALLOC_SIZE(ncpus);
+	if (nbits)
+		*nbits = cpuset_nbits(CPU_ALLOC_SIZE(ncpus));
+	return set;
+}
+
+void cpuset_free(cpu_set_t *set)
+{
+	CPU_FREE(set);
+}
+
+#if !HAVE_DECL_CPU_ALLOC
+/* Please, use CPU_COUNT_S() macro. This is fallback */
+int __cpuset_count_s(size_t setsize, const cpu_set_t *set)
+{
+	int s = 0;
+	const __cpu_mask *p = set->__bits;
+	const __cpu_mask *end = &set->__bits[setsize / sizeof (__cpu_mask)];
+
+	while (p < end) {
+		__cpu_mask l = *p++;
+
+		if (l == 0)
+			continue;
+# if LONG_BIT > 32
+		l = (l & 0x5555555555555555ul) + ((l >> 1) & 0x5555555555555555ul);
+		l = (l & 0x3333333333333333ul) + ((l >> 2) & 0x3333333333333333ul);
+		l = (l & 0x0f0f0f0f0f0f0f0ful) + ((l >> 4) & 0x0f0f0f0f0f0f0f0ful);
+		l = (l & 0x00ff00ff00ff00fful) + ((l >> 8) & 0x00ff00ff00ff00fful);
+		l = (l & 0x0000ffff0000fffful) + ((l >> 16) & 0x0000ffff0000fffful);
+		l = (l & 0x00000000fffffffful) + ((l >> 32) & 0x00000000fffffffful);
+# else
+		l = (l & 0x55555555ul) + ((l >> 1) & 0x55555555ul);
+		l = (l & 0x33333333ul) + ((l >> 2) & 0x33333333ul);
+		l = (l & 0x0f0f0f0ful) + ((l >> 4) & 0x0f0f0f0ful);
+		l = (l & 0x00ff00fful) + ((l >> 8) & 0x00ff00fful);
+		l = (l & 0x0000fffful) + ((l >> 16) & 0x0000fffful);
+# endif
+		s += l;
+	}
+	return s;
+}
+#endif
+
+/*
+ * Returns human readable representation of the cpuset. The output format is
+ * a list of CPUs with ranges (for example, "0,1,3-9").
+ */
+char *cpulist_create(char *str, size_t len,
+			cpu_set_t *set, size_t setsize)
+{
+	size_t i;
+	char *ptr = str;
+	int entry_made = 0;
+	size_t max = cpuset_nbits(setsize);
+
+	for (i = 0; i < max; i++) {
+		if (CPU_ISSET_S(i, setsize, set)) {
+			int rlen;
+			size_t j, run = 0;
+			entry_made = 1;
+			for (j = i + 1; j < max; j++) {
+				if (CPU_ISSET_S(j, setsize, set))
+					run++;
+				else
+					break;
+			}
+			if (!run)
+				rlen = snprintf(ptr, len, "%zd,", i);
+			else if (run == 1) {
+				rlen = snprintf(ptr, len, "%zd,%zd,", i, i + 1);
+				i++;
+			} else {
+				rlen = snprintf(ptr, len, "%zd-%zd,", i, i + run);
+				i += run;
+			}
+			if (rlen < 0 || (size_t) rlen + 1 > len)
+				return NULL;
+			ptr += rlen;
+			if (rlen > 0 && len > (size_t) rlen)
+				len -= rlen;
+			else
+				len = 0;
+		}
+	}
+	ptr -= entry_made;
+	*ptr = '\0';
+
+	return str;
+}
+
+/*
+ * Returns string with CPU mask.
+ */
+char *cpumask_create(char *str, size_t len,
+			cpu_set_t *set, size_t setsize)
+{
+	char *ptr = str;
+	char *ret = NULL;
+	int cpu;
+
+	for (cpu = cpuset_nbits(setsize) - 4; cpu >= 0; cpu -= 4) {
+		char val = 0;
+
+		if (len == (size_t) (ptr - str))
+			break;
+
+		if (CPU_ISSET_S(cpu, setsize, set))
+			val |= 1;
+		if (CPU_ISSET_S(cpu + 1, setsize, set))
+			val |= 2;
+		if (CPU_ISSET_S(cpu + 2, setsize, set))
+			val |= 4;
+		if (CPU_ISSET_S(cpu + 3, setsize, set))
+			val |= 8;
+
+		if (!ret && val)
+			ret = ptr;
+		*ptr++ = val_to_char(val);
+	}
+	*ptr = '\0';
+	return ret ? ret : ptr - 1;
+}
+
+/*
+ * Parses string with CPUs mask.
+ */
+int cpumask_parse(const char *str, cpu_set_t *set, size_t setsize)
+{
+	int len = strlen(str);
+	const char *ptr = str + len - 1;
+	int cpu = 0;
+
+	/* skip 0x, it's all hex anyway */
+	if (len > 1 && !memcmp(str, "0x", 2L))
+		str += 2;
+
+	CPU_ZERO_S(setsize, set);
+
+	while (ptr >= str) {
+		char val;
+
+		/* cpu masks in /sys uses comma as a separator */
+		if (*ptr == ',')
+			ptr--;
+
+		val = char_to_val(*ptr);
+		if (val == (char) -1)
+			return -1;
+		if (val & 1)
+			CPU_SET_S(cpu, setsize, set);
+		if (val & 2)
+			CPU_SET_S(cpu + 1, setsize, set);
+		if (val & 4)
+			CPU_SET_S(cpu + 2, setsize, set);
+		if (val & 8)
+			CPU_SET_S(cpu + 3, setsize, set);
+		len--;
+		ptr--;
+		cpu += 4;
+	}
+
+	return 0;
+}
+
+/*
+ * Parses string with list of CPU ranges.
+ * Returns 0 on success.
+ * Returns 1 on error.
+ * Returns 2 if fail is set and a cpu number passed in the list doesn't fit
+ * into the cpu_set. If fail is not set cpu numbers that do not fit are
+ * ignored and 0 is returned instead.
+ */
+int cpulist_parse(const char *str, cpu_set_t *set, size_t setsize, int fail)
+{
+	size_t max = cpuset_nbits(setsize);
+	const char *p, *q;
+	int r = 0;
+
+	q = str;
+	CPU_ZERO_S(setsize, set);
+
+	while (p = q, q = nexttoken(q, ','), p) {
+		unsigned int a;	/* beginning of range */
+		unsigned int b;	/* end of range */
+		unsigned int s;	/* stride */
+		const char *c1, *c2;
+		char c;
+
+		if ((r = sscanf(p, "%u%c", &a, &c)) < 1)
+			return 1;
+		b = a;
+		s = 1;
+
+		c1 = nexttoken(p, '-');
+		c2 = nexttoken(p, ',');
+		if (c1 != NULL && (c2 == NULL || c1 < c2)) {
+			if ((r = sscanf(c1, "%u%c", &b, &c)) < 1)
+				return 1;
+			c1 = nexttoken(c1, ':');
+			if (c1 != NULL && (c2 == NULL || c1 < c2)) {
+				if ((r = sscanf(c1, "%u%c", &s, &c)) < 1)
+					return 1;
+				if (s == 0)
+					return 1;
+			}
+		}
+
+		if (!(a <= b))
+			return 1;
+		while (a <= b) {
+			if (fail && (a >= max))
+				return 2;
+			CPU_SET_S(a, setsize, set);
+			a += s;
+		}
+	}
+
+	if (r == 2)
+		return 1;
+	return 0;
+}
+
+#ifdef TEST_PROGRAM
+
+#include <getopt.h>
+
+int main(int argc, char *argv[])
+{
+	cpu_set_t *set;
+	size_t setsize, buflen, nbits;
+	char *buf, *mask = NULL, *range = NULL;
+	int ncpus = 2048, rc, c;
+
+	static const struct option longopts[] = {
+	    { "ncpus", 1, 0, 'n' },
+	    { "mask",  1, 0, 'm' },
+	    { "range", 1, 0, 'r' },
+	    { NULL,    0, 0, 0 }
+	};
+
+	while ((c = getopt_long(argc, argv, "n:m:r:", longopts, NULL)) != -1) {
+		switch(c) {
+		case 'n':
+			ncpus = atoi(optarg);
+			break;
+		case 'm':
+			mask = strdup(optarg);
+			break;
+		case 'r':
+			range = strdup(optarg);
+			break;
+		default:
+			goto usage_err;
+		}
+	}
+
+	if (!mask && !range)
+		goto usage_err;
+
+	set = cpuset_alloc(ncpus, &setsize, &nbits);
+	if (!set)
+		err(EXIT_FAILURE, "failed to allocate cpu set");
+
+	/*
+	fprintf(stderr, "ncpus: %d, cpuset bits: %zd, cpuset bytes: %zd\n",
+			ncpus, nbits, setsize);
+	*/
+
+	buflen = 7 * nbits;
+	buf = malloc(buflen);
+	if (!buf)
+		err(EXIT_FAILURE, "failed to allocate cpu set buffer");
+
+	if (mask)
+		rc = cpumask_parse(mask, set, setsize);
+	else
+		rc = cpulist_parse(range, set, setsize, 0);
+
+	if (rc)
+		errx(EXIT_FAILURE, "failed to parse string: %s", mask ? : range);
+
+	printf("%-15s = %15s ", mask ? : range,
+				cpumask_create(buf, buflen, set, setsize));
+	printf("[%s]\n", cpulist_create(buf, buflen, set, setsize));
+
+	free(buf);
+	free(mask);
+	free(range);
+	cpuset_free(set);
+
+	return EXIT_SUCCESS;
+
+usage_err:
+	fprintf(stderr,
+		"usage: %s [--ncpus <num>] --mask <mask> | --range <list>",
+		program_invocation_short_name);
+	exit(EXIT_FAILURE);
+}
+#endif
diff --git a/libblkid/lib/crc32.c b/libblkid/lib/crc32.c
new file mode 100644
index 0000000..be98f1a
--- /dev/null
+++ b/libblkid/lib/crc32.c
@@ -0,0 +1,118 @@
+/*
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ *
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1.
+ *
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way,
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly.
+ *
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      is shown later.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera-
+ *      tions for all combinations of data and CRC register values.
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc"
+ *      logic; the shift must be unsigned (bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions.
+ *      polynomial $edb88320
+ *
+ */
+
+#include <stdio.h>
+
+#include "crc32.h"
+
+
+static const uint32_t crc32_tab[] = {
+	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+	0x2d02ef8dL
+};
+
+/*
+ * This a generic crc32() function, it takes seed as an argument,
+ * and does __not__ xor at the end. Then individual users can do
+ * whatever they need.
+ */
+uint32_t crc32(uint32_t seed, const unsigned char *buf, size_t len)
+{
+	uint32_t crc = seed;
+	const unsigned char *p = buf;
+
+	while (len) {
+		crc = crc32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+		len--;
+	}
+
+	return crc;
+}
+
diff --git a/libblkid/lib/crc64.c b/libblkid/lib/crc64.c
new file mode 100644
index 0000000..0be78e6
--- /dev/null
+++ b/libblkid/lib/crc64.c
@@ -0,0 +1,109 @@
+#include "crc64.h"
+
+static const uint64_t crc64_tab[256] = {
+	0x0000000000000000ULL, 0x42F0E1EBA9EA3693ULL, 0x85E1C3D753D46D26ULL,
+	0xC711223CFA3E5BB5ULL, 0x493366450E42ECDFULL, 0x0BC387AEA7A8DA4CULL,
+	0xCCD2A5925D9681F9ULL, 0x8E224479F47CB76AULL, 0x9266CC8A1C85D9BEULL,
+	0xD0962D61B56FEF2DULL, 0x17870F5D4F51B498ULL, 0x5577EEB6E6BB820BULL,
+	0xDB55AACF12C73561ULL, 0x99A54B24BB2D03F2ULL, 0x5EB4691841135847ULL,
+	0x1C4488F3E8F96ED4ULL, 0x663D78FF90E185EFULL, 0x24CD9914390BB37CULL,
+	0xE3DCBB28C335E8C9ULL, 0xA12C5AC36ADFDE5AULL, 0x2F0E1EBA9EA36930ULL,
+	0x6DFEFF5137495FA3ULL, 0xAAEFDD6DCD770416ULL, 0xE81F3C86649D3285ULL,
+	0xF45BB4758C645C51ULL, 0xB6AB559E258E6AC2ULL, 0x71BA77A2DFB03177ULL,
+	0x334A9649765A07E4ULL, 0xBD68D2308226B08EULL, 0xFF9833DB2BCC861DULL,
+	0x388911E7D1F2DDA8ULL, 0x7A79F00C7818EB3BULL, 0xCC7AF1FF21C30BDEULL,
+	0x8E8A101488293D4DULL, 0x499B3228721766F8ULL, 0x0B6BD3C3DBFD506BULL,
+	0x854997BA2F81E701ULL, 0xC7B97651866BD192ULL, 0x00A8546D7C558A27ULL,
+	0x4258B586D5BFBCB4ULL, 0x5E1C3D753D46D260ULL, 0x1CECDC9E94ACE4F3ULL,
+	0xDBFDFEA26E92BF46ULL, 0x990D1F49C77889D5ULL, 0x172F5B3033043EBFULL,
+	0x55DFBADB9AEE082CULL, 0x92CE98E760D05399ULL, 0xD03E790CC93A650AULL,
+	0xAA478900B1228E31ULL, 0xE8B768EB18C8B8A2ULL, 0x2FA64AD7E2F6E317ULL,
+	0x6D56AB3C4B1CD584ULL, 0xE374EF45BF6062EEULL, 0xA1840EAE168A547DULL,
+	0x66952C92ECB40FC8ULL, 0x2465CD79455E395BULL, 0x3821458AADA7578FULL,
+	0x7AD1A461044D611CULL, 0xBDC0865DFE733AA9ULL, 0xFF3067B657990C3AULL,
+	0x711223CFA3E5BB50ULL, 0x33E2C2240A0F8DC3ULL, 0xF4F3E018F031D676ULL,
+	0xB60301F359DBE0E5ULL, 0xDA050215EA6C212FULL, 0x98F5E3FE438617BCULL,
+	0x5FE4C1C2B9B84C09ULL, 0x1D14202910527A9AULL, 0x93366450E42ECDF0ULL,
+	0xD1C685BB4DC4FB63ULL, 0x16D7A787B7FAA0D6ULL, 0x5427466C1E109645ULL,
+	0x4863CE9FF6E9F891ULL, 0x0A932F745F03CE02ULL, 0xCD820D48A53D95B7ULL,
+	0x8F72ECA30CD7A324ULL, 0x0150A8DAF8AB144EULL, 0x43A04931514122DDULL,
+	0x84B16B0DAB7F7968ULL, 0xC6418AE602954FFBULL, 0xBC387AEA7A8DA4C0ULL,
+	0xFEC89B01D3679253ULL, 0x39D9B93D2959C9E6ULL, 0x7B2958D680B3FF75ULL,
+	0xF50B1CAF74CF481FULL, 0xB7FBFD44DD257E8CULL, 0x70EADF78271B2539ULL,
+	0x321A3E938EF113AAULL, 0x2E5EB66066087D7EULL, 0x6CAE578BCFE24BEDULL,
+	0xABBF75B735DC1058ULL, 0xE94F945C9C3626CBULL, 0x676DD025684A91A1ULL,
+	0x259D31CEC1A0A732ULL, 0xE28C13F23B9EFC87ULL, 0xA07CF2199274CA14ULL,
+	0x167FF3EACBAF2AF1ULL, 0x548F120162451C62ULL, 0x939E303D987B47D7ULL,
+	0xD16ED1D631917144ULL, 0x5F4C95AFC5EDC62EULL, 0x1DBC74446C07F0BDULL,
+	0xDAAD56789639AB08ULL, 0x985DB7933FD39D9BULL, 0x84193F60D72AF34FULL,
+	0xC6E9DE8B7EC0C5DCULL, 0x01F8FCB784FE9E69ULL, 0x43081D5C2D14A8FAULL,
+	0xCD2A5925D9681F90ULL, 0x8FDAB8CE70822903ULL, 0x48CB9AF28ABC72B6ULL,
+	0x0A3B7B1923564425ULL, 0x70428B155B4EAF1EULL, 0x32B26AFEF2A4998DULL,
+	0xF5A348C2089AC238ULL, 0xB753A929A170F4ABULL, 0x3971ED50550C43C1ULL,
+	0x7B810CBBFCE67552ULL, 0xBC902E8706D82EE7ULL, 0xFE60CF6CAF321874ULL,
+	0xE224479F47CB76A0ULL, 0xA0D4A674EE214033ULL, 0x67C58448141F1B86ULL,
+	0x253565A3BDF52D15ULL, 0xAB1721DA49899A7FULL, 0xE9E7C031E063ACECULL,
+	0x2EF6E20D1A5DF759ULL, 0x6C0603E6B3B7C1CAULL, 0xF6FAE5C07D3274CDULL,
+	0xB40A042BD4D8425EULL, 0x731B26172EE619EBULL, 0x31EBC7FC870C2F78ULL,
+	0xBFC9838573709812ULL, 0xFD39626EDA9AAE81ULL, 0x3A28405220A4F534ULL,
+	0x78D8A1B9894EC3A7ULL, 0x649C294A61B7AD73ULL, 0x266CC8A1C85D9BE0ULL,
+	0xE17DEA9D3263C055ULL, 0xA38D0B769B89F6C6ULL, 0x2DAF4F0F6FF541ACULL,
+	0x6F5FAEE4C61F773FULL, 0xA84E8CD83C212C8AULL, 0xEABE6D3395CB1A19ULL,
+	0x90C79D3FEDD3F122ULL, 0xD2377CD44439C7B1ULL, 0x15265EE8BE079C04ULL,
+	0x57D6BF0317EDAA97ULL, 0xD9F4FB7AE3911DFDULL, 0x9B041A914A7B2B6EULL,
+	0x5C1538ADB04570DBULL, 0x1EE5D94619AF4648ULL, 0x02A151B5F156289CULL,
+	0x4051B05E58BC1E0FULL, 0x87409262A28245BAULL, 0xC5B073890B687329ULL,
+	0x4B9237F0FF14C443ULL, 0x0962D61B56FEF2D0ULL, 0xCE73F427ACC0A965ULL,
+	0x8C8315CC052A9FF6ULL, 0x3A80143F5CF17F13ULL, 0x7870F5D4F51B4980ULL,
+	0xBF61D7E80F251235ULL, 0xFD913603A6CF24A6ULL, 0x73B3727A52B393CCULL,
+	0x31439391FB59A55FULL, 0xF652B1AD0167FEEAULL, 0xB4A25046A88DC879ULL,
+	0xA8E6D8B54074A6ADULL, 0xEA16395EE99E903EULL, 0x2D071B6213A0CB8BULL,
+	0x6FF7FA89BA4AFD18ULL, 0xE1D5BEF04E364A72ULL, 0xA3255F1BE7DC7CE1ULL,
+	0x64347D271DE22754ULL, 0x26C49CCCB40811C7ULL, 0x5CBD6CC0CC10FAFCULL,
+	0x1E4D8D2B65FACC6FULL, 0xD95CAF179FC497DAULL, 0x9BAC4EFC362EA149ULL,
+	0x158E0A85C2521623ULL, 0x577EEB6E6BB820B0ULL, 0x906FC95291867B05ULL,
+	0xD29F28B9386C4D96ULL, 0xCEDBA04AD0952342ULL, 0x8C2B41A1797F15D1ULL,
+	0x4B3A639D83414E64ULL, 0x09CA82762AAB78F7ULL, 0x87E8C60FDED7CF9DULL,
+	0xC51827E4773DF90EULL, 0x020905D88D03A2BBULL, 0x40F9E43324E99428ULL,
+	0x2CFFE7D5975E55E2ULL, 0x6E0F063E3EB46371ULL, 0xA91E2402C48A38C4ULL,
+	0xEBEEC5E96D600E57ULL, 0x65CC8190991CB93DULL, 0x273C607B30F68FAEULL,
+	0xE02D4247CAC8D41BULL, 0xA2DDA3AC6322E288ULL, 0xBE992B5F8BDB8C5CULL,
+	0xFC69CAB42231BACFULL, 0x3B78E888D80FE17AULL, 0x7988096371E5D7E9ULL,
+	0xF7AA4D1A85996083ULL, 0xB55AACF12C735610ULL, 0x724B8ECDD64D0DA5ULL,
+	0x30BB6F267FA73B36ULL, 0x4AC29F2A07BFD00DULL, 0x08327EC1AE55E69EULL,
+	0xCF235CFD546BBD2BULL, 0x8DD3BD16FD818BB8ULL, 0x03F1F96F09FD3CD2ULL,
+	0x41011884A0170A41ULL, 0x86103AB85A2951F4ULL, 0xC4E0DB53F3C36767ULL,
+	0xD8A453A01B3A09B3ULL, 0x9A54B24BB2D03F20ULL, 0x5D45907748EE6495ULL,
+	0x1FB5719CE1045206ULL, 0x919735E51578E56CULL, 0xD367D40EBC92D3FFULL,
+	0x1476F63246AC884AULL, 0x568617D9EF46BED9ULL, 0xE085162AB69D5E3CULL,
+	0xA275F7C11F7768AFULL, 0x6564D5FDE549331AULL, 0x279434164CA30589ULL,
+	0xA9B6706FB8DFB2E3ULL, 0xEB46918411358470ULL, 0x2C57B3B8EB0BDFC5ULL,
+	0x6EA7525342E1E956ULL, 0x72E3DAA0AA188782ULL, 0x30133B4B03F2B111ULL,
+	0xF7021977F9CCEAA4ULL, 0xB5F2F89C5026DC37ULL, 0x3BD0BCE5A45A6B5DULL,
+	0x79205D0E0DB05DCEULL, 0xBE317F32F78E067BULL, 0xFCC19ED95E6430E8ULL,
+	0x86B86ED5267CDBD3ULL, 0xC4488F3E8F96ED40ULL, 0x0359AD0275A8B6F5ULL,
+	0x41A94CE9DC428066ULL, 0xCF8B0890283E370CULL, 0x8D7BE97B81D4019FULL,
+	0x4A6ACB477BEA5A2AULL, 0x089A2AACD2006CB9ULL, 0x14DEA25F3AF9026DULL,
+	0x562E43B4931334FEULL, 0x913F6188692D6F4BULL, 0xD3CF8063C0C759D8ULL,
+	0x5DEDC41A34BBEEB2ULL, 0x1F1D25F19D51D821ULL, 0xD80C07CD676F8394ULL,
+	0x9AFCE626CE85B507ULL
+};
+
+/*
+ * This a generic crc64() function, it takes seed as an argument,
+ * and does __not__ xor at the end. Then individual users can do
+ * whatever they need.
+ */
+uint64_t crc64(uint64_t seed, const unsigned char *data, size_t len)
+{
+	uint64_t crc = seed;
+
+	while (len) {
+		int i = ((int) (crc >> 56) ^ *data++) & 0xFF;
+		crc = crc64_tab[i] ^ (crc << 8);
+		len--;
+	}
+
+	return crc;
+}
+
diff --git a/libblkid/lib/env.c b/libblkid/lib/env.c
new file mode 100644
index 0000000..c79e0e0
--- /dev/null
+++ b/libblkid/lib/env.c
@@ -0,0 +1,110 @@
+/*
+ * Security checks of environment
+ * Added from shadow-utils package
+ * by Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#else
+#define PR_GET_DUMPABLE 3
+#endif
+#if (!defined(HAVE_PRCTL) && defined(linux))
+#include <sys/syscall.h>
+#endif
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "env.h"
+
+#ifndef HAVE_ENVIRON_DECL
+extern char **environ;
+#endif
+
+static char * const forbid[] = {
+        "_RLD_=",
+        "BASH_ENV=",    /* GNU creeping featurism strikes again... */
+        "ENV=",
+        "HOME=",
+        "IFS=",
+        "KRB_CONF=",
+        "LD_",          /* anything with the LD_ prefix */
+        "LIBPATH=",
+        "MAIL=",
+        "NLSPATH=",
+        "PATH=",
+        "SHELL=",
+        "SHLIB_PATH=",
+        (char *) 0
+};
+
+/* these are allowed, but with no slashes inside
+   (to work around security problems in GNU gettext) */
+static char * const noslash[] = {
+        "LANG=",
+        "LANGUAGE=",
+        "LC_",          /* anything with the LC_ prefix */
+        (char *) 0
+};
+
+void
+sanitize_env(void)
+{
+        char **envp = environ;
+        char * const *bad;
+        char **cur;
+        char **move;
+
+        for (cur = envp; *cur; cur++) {
+                for (bad = forbid; *bad; bad++) {
+                        if (strncmp(*cur, *bad, strlen(*bad)) == 0) {
+                                for (move = cur; *move; move++)
+                                        *move = *(move + 1);
+                                cur--;
+                                break;
+                        }
+                }
+        }
+
+        for (cur = envp; *cur; cur++) {
+                for (bad = noslash; *bad; bad++) {
+                        if (strncmp(*cur, *bad, strlen(*bad)) != 0)
+                                continue;
+                        if (!strchr(*cur, '/'))
+                                continue;  /* OK */
+                        for (move = cur; *move; move++)
+                                *move = *(move + 1);
+                        cur--;
+                        break;
+                }
+        }
+}
+
+
+char *safe_getenv(const char *arg)
+{
+	uid_t ruid = getuid();
+
+	if (ruid != 0 || (ruid != geteuid()) || (getgid() != getegid()))
+		return NULL;
+#ifdef HAVE_PRCTL
+	if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+		return NULL;
+#else
+#if (defined(linux) && defined(SYS_prctl))
+	if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0)
+		return NULL;
+#endif
+#endif
+#ifdef HAVE_SECURE_GETENV
+return secure_getenv(arg);
+#elif HAVE___SECURE_GETENV
+	return __secure_getenv(arg);
+#else
+	return getenv(arg);
+#endif
+}
diff --git a/libblkid/lib/exec_shell.c b/libblkid/lib/exec_shell.c
new file mode 100644
index 0000000..2b26364
--- /dev/null
+++ b/libblkid/lib/exec_shell.c
@@ -0,0 +1,46 @@
+/*
+ * exec_shell() - launch a shell, else exit!
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "nls.h"
+#include "c.h"
+#include "xalloc.h"
+
+#include "exec_shell.h"
+
+#define DEFAULT_SHELL "/bin/sh"
+
+void exec_shell(void)
+{
+	const char *shell = getenv("SHELL"), *shell_basename;
+	char *arg0;
+	if (!shell)
+		shell = DEFAULT_SHELL;
+
+	shell_basename = basename(shell);
+	arg0 = xmalloc(strlen(shell_basename) + 2);
+	arg0[0] = '-';
+	strcpy(arg0 + 1, shell_basename);
+
+	execl(shell, arg0, NULL);
+	err(EXIT_FAILURE, _("failed to execute %s"), shell);
+}
diff --git a/libblkid/lib/fileutils.c b/libblkid/lib/fileutils.c
new file mode 100644
index 0000000..4e884d3
--- /dev/null
+++ b/libblkid/lib/fileutils.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2012 Sami Kerola <kerolasa@iki.fi>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <paths.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include "c.h"
+#include "fileutils.h"
+#include "pathnames.h"
+
+/* Create open temporary file in safe way.  Please notice that the
+ * file permissions are -rw------- by default. */
+int xmkstemp(char **tmpname, char *dir)
+{
+	char *localtmp;
+	char *tmpenv;
+	mode_t old_mode;
+	int fd, rc;
+
+	/* Some use cases must be capable of being moved atomically
+	 * with rename(2), which is the reason why dir is here.  */
+	if (dir != NULL)
+		tmpenv = dir;
+	else
+		tmpenv = getenv("TMPDIR");
+
+	if (tmpenv)
+		rc = asprintf(&localtmp, "%s/%s.XXXXXX", tmpenv,
+			  program_invocation_short_name);
+	else
+		rc = asprintf(&localtmp, "%s/%s.XXXXXX", _PATH_TMP,
+			  program_invocation_short_name);
+
+	if (rc < 0)
+		return -1;
+
+	old_mode = umask(077);
+	fd = mkstemp(localtmp);
+	umask(old_mode);
+	if (fd == -1) {
+		free(localtmp);
+		localtmp = NULL;
+	}
+	*tmpname = localtmp;
+	return fd;
+}
+
+/*
+ * portable getdtablesize()
+ */
+int get_fd_tabsize(void)
+{
+	int m;
+
+#if defined(HAVE_GETDTABLESIZE)
+	m = getdtablesize();
+#elif defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)
+	struct rlimit rl;
+
+	getrlimit(RLIMIT_NOFILE, &rl);
+	m = rl.rlim_cur;
+#elif defined(HAVE_SYSCONF) && defined(_SC_OPEN_MAX)
+	m = sysconf(_SC_OPEN_MAX);
+#else
+	m = OPEN_MAX;
+#endif
+	return m;
+}
+
+#ifdef TEST_PROGRAM
+int main(void)
+{
+	FILE *f;
+	char *tmpname;
+	f = xfmkstemp(&tmpname, NULL);
+	unlink(tmpname);
+	free(tmpname);
+	fclose(f);
+	return EXIT_FAILURE;
+}
+#endif
+
+
+int mkdir_p(const char *path, mode_t mode)
+{
+	char *p, *dir;
+	int rc = 0;
+
+	if (!path || !*path)
+		return -EINVAL;
+
+	dir = p = strdup(path);
+	if (!dir)
+		return -ENOMEM;
+
+	if (*p == '/')
+		p++;
+
+	while (p && *p) {
+		char *e = strchr(p, '/');
+		if (e)
+			*e = '\0';
+		if (*p) {
+			rc = mkdir(dir, mode);
+			if (rc && errno != EEXIST)
+				break;
+			rc = 0;
+		}
+		if (!e)
+			break;
+		*e = '/';
+		p = e + 1;
+	}
+
+	free(dir);
+	return rc;
+}
+
+/* returns basename and keeps dirname in the @path, if @path is "/" (root)
+ * then returns empty string */
+char *stripoff_last_component(char *path)
+{
+	char *p = path ? strrchr(path, '/') : NULL;
+
+	if (!p)
+		return NULL;
+	*p = '\0';
+	return p + 1;
+}
diff --git a/libblkid/lib/ismounted.c b/libblkid/lib/ismounted.c
new file mode 100644
index 0000000..8099bd7
--- /dev/null
+++ b/libblkid/lib/ismounted.c
@@ -0,0 +1,388 @@
+/*
+ * ismounted.c --- Check to see if the filesystem was mounted
+ *
+ * Copyright (C) 1995,1996,1997,1998,1999,2000,2008 Theodore Ts'o.
+ *
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#endif
+#include <string.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <sys/param.h>
+#ifdef __APPLE__
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#endif
+
+#include "pathnames.h"
+#include "ismounted.h"
+#include "c.h"
+#ifdef __linux__
+# include "loopdev.h"
+#endif
+
+
+
+#ifdef HAVE_MNTENT_H
+/*
+ * Helper function which checks a file in /etc/mtab format to see if a
+ * filesystem is mounted.  Returns an error if the file doesn't exist
+ * or can't be opened.
+ */
+static int check_mntent_file(const char *mtab_file, const char *file,
+				   int *mount_flags, char *mtpt, int mtlen)
+{
+	struct mntent	*mnt;
+	struct stat	st_buf;
+	int		retval = 0;
+	dev_t		file_dev=0, file_rdev=0;
+	ino_t		file_ino=0;
+	FILE		*f;
+	int		fd;
+
+	*mount_flags = 0;
+	if ((f = setmntent (mtab_file, "r")) == NULL)
+		return errno;
+
+	if (stat(file, &st_buf) == 0) {
+		if (S_ISBLK(st_buf.st_mode)) {
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+			file_rdev = st_buf.st_rdev;
+#endif	/* __GNU__ */
+		} else {
+			file_dev = st_buf.st_dev;
+			file_ino = st_buf.st_ino;
+		}
+	}
+
+	while ((mnt = getmntent (f)) != NULL) {
+		if (mnt->mnt_fsname[0] != '/')
+			continue;
+		if (strcmp(file, mnt->mnt_fsname) == 0)
+			break;
+		if (stat(mnt->mnt_fsname, &st_buf) != 0)
+			continue;
+
+		if (S_ISBLK(st_buf.st_mode)) {
+#ifndef __GNU__
+			if (file_rdev && file_rdev == st_buf.st_rdev)
+				break;
+#ifdef __linux__
+			/* maybe the file is loopdev backing file */
+			if (file_dev
+			    && major(st_buf.st_rdev) == LOOPDEV_MAJOR
+			    && loopdev_is_used(mnt->mnt_fsname, file, 0, 0))
+				break;
+#endif /* __linux__ */
+#endif	/* __GNU__ */
+		} else {
+			if (file_dev && ((file_dev == st_buf.st_dev) &&
+					 (file_ino == st_buf.st_ino)))
+				break;
+		}
+	}
+
+	if (mnt == NULL) {
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+		/*
+		 * Do an extra check to see if this is the root device.  We
+		 * can't trust /etc/mtab, and /proc/mounts will only list
+		 * /dev/root for the root filesystem.  Argh.  Instead we
+		 * check if the given device has the same major/minor number
+		 * as the device that the root directory is on.
+		 */
+		if (file_rdev && stat("/", &st_buf) == 0 &&
+		    st_buf.st_dev == file_rdev) {
+			*mount_flags = MF_MOUNTED;
+			if (mtpt)
+				strncpy(mtpt, "/", mtlen);
+			goto is_root;
+		}
+#endif	/* __GNU__ */
+		goto errout;
+	}
+#ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */
+	/* Validate the entry in case /etc/mtab is out of date */
+	/*
+	 * We need to be paranoid, because some broken distributions
+	 * (read: Slackware) don't initialize /etc/mtab before checking
+	 * all of the non-root filesystems on the disk.
+	 */
+	if (stat(mnt->mnt_dir, &st_buf) < 0) {
+		retval = errno;
+		if (retval == ENOENT) {
+#ifdef DEBUG
+			printf("Bogus entry in %s!  (%s does not exist)\n",
+			       mtab_file, mnt->mnt_dir);
+#endif /* DEBUG */
+			retval = 0;
+		}
+		goto errout;
+	}
+	if (file_rdev && (st_buf.st_dev != file_rdev)) {
+#ifdef DEBUG
+		printf("Bogus entry in %s!  (%s not mounted on %s)\n",
+		       mtab_file, file, mnt->mnt_dir);
+#endif /* DEBUG */
+		goto errout;
+	}
+#endif /* __GNU__ */
+	*mount_flags = MF_MOUNTED;
+
+#ifdef MNTOPT_RO
+	/* Check to see if the ro option is set */
+	if (hasmntopt(mnt, MNTOPT_RO))
+		*mount_flags |= MF_READONLY;
+#endif
+
+	if (mtpt)
+		strncpy(mtpt, mnt->mnt_dir, mtlen);
+	/*
+	 * Check to see if we're referring to the root filesystem.
+	 * If so, do a manual check to see if we can open /etc/mtab
+	 * read/write, since if the root is mounted read/only, the
+	 * contents of /etc/mtab may not be accurate.
+	 */
+	if (!strcmp(mnt->mnt_dir, "/")) {
+is_root:
+#define TEST_FILE "/.ismount-test-file"
+		*mount_flags |= MF_ISROOT;
+		fd = open(TEST_FILE, O_RDWR|O_CREAT|O_CLOEXEC, 0600);
+		if (fd < 0) {
+			if (errno == EROFS)
+				*mount_flags |= MF_READONLY;
+		} else
+			close(fd);
+		(void) unlink(TEST_FILE);
+	}
+	retval = 0;
+errout:
+	endmntent (f);
+	return retval;
+}
+
+static int check_mntent(const char *file, int *mount_flags,
+			      char *mtpt, int mtlen)
+{
+	int	retval;
+
+#ifdef DEBUG
+	retval = check_mntent_file("/tmp/mtab", file, mount_flags,
+				   mtpt, mtlen);
+	if (retval == 0)
+		return 0;
+#endif /* DEBUG */
+#ifdef __linux__
+	retval = check_mntent_file("/proc/mounts", file, mount_flags,
+				   mtpt, mtlen);
+	if (retval == 0 && (*mount_flags != 0))
+		return 0;
+	if (access("/proc/mounts", R_OK) == 0) {
+		*mount_flags = 0;
+		return retval;
+	}
+#endif /* __linux__ */
+#if defined(MOUNTED) || defined(_PATH_MOUNTED)
+#ifndef MOUNTED
+#define MOUNTED _PATH_MOUNTED
+#endif /* MOUNTED */
+	retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen);
+	return retval;
+#else
+	*mount_flags = 0;
+	return 0;
+#endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */
+}
+
+#else
+#if defined(HAVE_GETMNTINFO)
+
+static int check_getmntinfo(const char *file, int *mount_flags,
+				  char *mtpt, int mtlen)
+{
+	struct statfs *mp;
+        int    len, n;
+        const  char   *s1;
+	char	*s2;
+
+        n = getmntinfo(&mp, MNT_NOWAIT);
+        if (n == 0)
+		return errno;
+
+        len = sizeof(_PATH_DEV) - 1;
+        s1 = file;
+        if (strncmp(_PATH_DEV, s1, len) == 0)
+                s1 += len;
+
+	*mount_flags = 0;
+        while (--n >= 0) {
+                s2 = mp->f_mntfromname;
+                if (strncmp(_PATH_DEV, s2, len) == 0) {
+                        s2 += len - 1;
+                        *s2 = 'r';
+                }
+                if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) {
+			*mount_flags = MF_MOUNTED;
+			break;
+		}
+                ++mp;
+	}
+	if (mtpt)
+		strncpy(mtpt, mp->f_mntonname, mtlen);
+	return 0;
+}
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+
+/*
+ * Check to see if we're dealing with the swap device.
+ */
+static int is_swap_device(const char *file)
+{
+	FILE		*f;
+	char		buf[1024], *cp;
+	dev_t		file_dev;
+	struct stat	st_buf;
+	int		ret = 0;
+
+	file_dev = 0;
+#ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */
+	if ((stat(file, &st_buf) == 0) &&
+	    S_ISBLK(st_buf.st_mode))
+		file_dev = st_buf.st_rdev;
+#endif	/* __GNU__ */
+
+	if (!(f = fopen("/proc/swaps", "r")))
+		return 0;
+	/* Skip the first line */
+	if (!fgets(buf, sizeof(buf), f))
+		goto leave;
+	if (*buf && strncmp(buf, "Filename\t", 9))
+		/* Linux <=2.6.19 contained a bug in the /proc/swaps
+		 * code where the header would not be displayed
+		 */
+		goto valid_first_line;
+
+	while (fgets(buf, sizeof(buf), f)) {
+valid_first_line:
+		if ((cp = strchr(buf, ' ')) != NULL)
+			*cp = 0;
+		if ((cp = strchr(buf, '\t')) != NULL)
+			*cp = 0;
+		if (strcmp(buf, file) == 0) {
+			ret++;
+			break;
+		}
+#ifndef __GNU__
+		if (file_dev && (stat(buf, &st_buf) == 0) &&
+		    S_ISBLK(st_buf.st_mode) &&
+		    file_dev == st_buf.st_rdev) {
+			ret++;
+			break;
+		}
+#endif	/* __GNU__ */
+	}
+
+leave:
+	fclose(f);
+	return ret;
+}
+
+
+/*
+ * check_mount_point() fills determines if the device is mounted or otherwise
+ * busy, and fills in mount_flags with one or more of the following flags:
+ * MF_MOUNTED, MF_ISROOT, MF_READONLY, MF_SWAP, and MF_BUSY.  If mtpt is
+ * non-NULL, the directory where the device is mounted is copied to where mtpt
+ * is pointing, up to mtlen characters.
+ */
+#ifdef __TURBOC__
+ #pragma argsused
+#endif
+int check_mount_point(const char *device, int *mount_flags,
+				  char *mtpt, int mtlen)
+{
+	struct stat	st_buf;
+	int	retval = 0;
+	int		fd;
+
+	if (is_swap_device(device)) {
+		*mount_flags = MF_MOUNTED | MF_SWAP;
+		if (mtpt && mtlen)
+			strncpy(mtpt, "[SWAP]", mtlen);
+	} else {
+#ifdef HAVE_MNTENT_H
+		retval = check_mntent(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef HAVE_GETMNTINFO
+		retval = check_getmntinfo(device, mount_flags, mtpt, mtlen);
+#else
+#ifdef __GNUC__
+ #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!"
+#endif
+		*mount_flags = 0;
+#endif /* HAVE_GETMNTINFO */
+#endif /* HAVE_MNTENT_H */
+	}
+	if (retval)
+		return retval;
+
+#ifdef __linux__ /* This only works on Linux 2.6+ systems */
+	if ((stat(device, &st_buf) != 0) ||
+	    !S_ISBLK(st_buf.st_mode))
+		return 0;
+	fd = open(device, O_RDONLY|O_EXCL|O_CLOEXEC);
+	if (fd < 0) {
+		if (errno == EBUSY)
+			*mount_flags |= MF_BUSY;
+	} else
+		close(fd);
+#endif
+
+	return 0;
+}
+
+int is_mounted(const char *file)
+{
+	int	retval;
+	int	mount_flags = 0;
+
+	retval = check_mount_point(file, &mount_flags, NULL, 0);
+	if (retval)
+		return 0;
+	return mount_flags & MF_MOUNTED;
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc, char **argv)
+{
+	int flags = 0;
+	char devname[PATH_MAX];
+
+	if (argc < 2) {
+		fprintf(stderr, "Usage: %s device\n", argv[0]);
+		return EXIT_FAILURE;
+	}
+
+	if (check_mount_point(argv[1], &flags, devname, sizeof(devname)) == 0 &&
+	    (flags & MF_MOUNTED)) {
+		if (flags & MF_SWAP)
+			printf("used swap device\n");
+		else
+			printf("mounted on %s\n", devname);
+		return EXIT_SUCCESS;
+	}
+
+	printf("not mounted\n");
+	return EXIT_FAILURE;
+}
+#endif /* DEBUG */
diff --git a/libblkid/lib/langinfo.c b/libblkid/lib/langinfo.c
new file mode 100644
index 0000000..deeab9b
--- /dev/null
+++ b/libblkid/lib/langinfo.c
@@ -0,0 +1,121 @@
+/*
+ * This is callback solution for systems without nl_langinfo(), this function
+ * returns hardcoded and on locale setting independed value.
+ *
+ * See langinfo.h man page for more details.
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ */
+#include "nls.h"
+
+char *langinfo_fallback(nl_item item)
+{
+	switch (item) {
+	case CODESET:
+		return "ISO-8859-1";
+	case THOUSEP:
+		return ",";
+	case D_T_FMT:
+	case ERA_D_T_FMT:
+		return "%a %b %e %H:%M:%S %Y";
+	case D_FMT:
+	case ERA_D_FMT:
+		return "%m/%d/%y";
+	case T_FMT:
+	case ERA_T_FMT:
+		return "%H:%M:%S";
+	case T_FMT_AMPM:
+		return "%I:%M:%S %p";
+	case AM_STR:
+		return "AM";
+	case PM_STR:
+		return "PM";
+	case DAY_1:
+		return "Sunday";
+	case DAY_2:
+		return "Monday";
+	case DAY_3:
+		return "Tuesday";
+	case DAY_4:
+		return "Wednesday";
+	case DAY_5:
+		return "Thursday";
+	case DAY_6:
+		return "Friday";
+	case DAY_7:
+		return "Saturday";
+	case ABDAY_1:
+		return "Sun";
+	case ABDAY_2:
+		return "Mon";
+	case ABDAY_3:
+		return "Tue";
+	case ABDAY_4:
+		return "Wed";
+	case ABDAY_5:
+		return "Thu";
+	case ABDAY_6:
+		return "Fri";
+	case ABDAY_7:
+		return "Sat";
+	case MON_1:
+		return "January";
+	case MON_2:
+		return "February";
+	case MON_3:
+		return "March";
+	case MON_4:
+		return "April";
+	case MON_5:
+		return "May";
+	case MON_6:
+		return "June";
+	case MON_7:
+		return "July";
+	case MON_8:
+		return "August";
+	case MON_9:
+		return "September";
+	case MON_10:
+		return "October";
+	case MON_11:
+		return "November";
+	case MON_12:
+		return "December";
+	case ABMON_1:
+		return "Jan";
+	case ABMON_2:
+		return "Feb";
+	case ABMON_3:
+		return "Mar";
+	case ABMON_4:
+		return "Apr";
+	case ABMON_5:
+		return "May";
+	case ABMON_6:
+		return "Jun";
+	case ABMON_7:
+		return "Jul";
+	case ABMON_8:
+		return "Aug";
+	case ABMON_9:
+		return "Sep";
+	case ABMON_10:
+		return "Oct";
+	case ABMON_11:
+		return "Nov";
+	case ABMON_12:
+		return "Dec";
+	case ALT_DIGITS:
+		return "\0\0\0\0\0\0\0\0\0\0";
+	case CRNCYSTR:
+		return "-";
+	case YESEXPR:
+		return "^[yY]";
+	case NOEXPR:
+		return "^[nN]";
+	default:
+		return "";
+	}
+}
+
diff --git a/libblkid/lib/linux_version.c b/libblkid/lib/linux_version.c
new file mode 100644
index 0000000..2bcc2cc
--- /dev/null
+++ b/libblkid/lib/linux_version.c
@@ -0,0 +1,25 @@
+#include <stdio.h>
+#include <sys/utsname.h>
+
+#include "linux_version.h"
+
+int get_linux_version (void)
+{
+	static int kver = -1;
+	struct utsname uts;
+	int major = 0;
+	int minor = 0;
+	int teeny = 0;
+	int n;
+
+	if (kver != -1)
+		return kver;
+	if (uname (&uts))
+		return kver = 0;
+
+	n = sscanf(uts.release, "%d.%d.%d", &major, &minor, &teeny);
+	if (n < 1 || n > 3)
+		return kver = 0;
+
+	return kver = KERNEL_VERSION(major, minor, teeny);
+}
diff --git a/libblkid/lib/loopdev.c b/libblkid/lib/loopdev.c
new file mode 100644
index 0000000..09b9bbf
--- /dev/null
+++ b/libblkid/lib/loopdev.c
@@ -0,0 +1,1572 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ *
+ * -- based on mount/losetup.c
+ *
+ * Simple library for work with loop devices.
+ *
+ *  - requires kernel 2.6.x
+ *  - reads info from /sys/block/loop<N>/loop/<attr> (new kernels)
+ *  - reads info by ioctl
+ *  - supports *unlimited* number of loop devices
+ *  - supports /dev/loop<N> as well as /dev/loop/<N>
+ *  - minimize overhead (fd, loopinfo, ... are shared for all operations)
+ *  - setup (associate device and backing file)
+ *  - delete (dis-associate file)
+ *  - old LOOP_{SET,GET}_STATUS (32bit) ioctls are unsupported
+ *  - extendible
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/sysmacros.h>
+#include <inttypes.h>
+#include <dirent.h>
+#include <linux/posix_types.h>
+
+#include "linux_version.h"
+#include "c.h"
+#include "sysfs.h"
+#include "pathnames.h"
+#include "loopdev.h"
+#include "canonicalize.h"
+#include "at.h"
+#include "blkdev.h"
+#include "debug.h"
+
+/*
+ * Debug stuff (based on include/debug.h)
+ */
+UL_DEBUG_DEFINE_MASK(loopdev);
+UL_DEBUG_DEFINE_MASKNAMES(loopdev) = UL_DEBUG_EMPTY_MASKNAMES;
+
+#define LOOPDEV_DEBUG_INIT	(1 << 1)
+#define LOOPDEV_DEBUG_CXT	(1 << 2)
+#define LOOPDEV_DEBUG_ITER	(1 << 3)
+#define LOOPDEV_DEBUG_SETUP	(1 << 4)
+#define SFDISKPROG_DEBUG_ALL	0xFFFF
+
+#define DBG(m, x)       __UL_DBG(loopdev, LOOPDEV_DEBUG_, m, x)
+#define ON_DBG(m, x)    __UL_DBG_CALL(loopdev, LOOPDEV_DEBUG_, m, x)
+
+static void loopdev_init_debug(void)
+{
+	if (loopdev_debug_mask)
+		return;
+	__UL_INIT_DEBUG(loopdev, LOOPDEV_DEBUG_, 0, LOOPDEV_DEBUG);
+}
+
+/*
+ * see loopcxt_init()
+ */
+#define loopcxt_ioctl_enabled(_lc)	(!((_lc)->flags & LOOPDEV_FL_NOIOCTL))
+#define loopcxt_sysfs_available(_lc)	(!((_lc)->flags & LOOPDEV_FL_NOSYSFS)) \
+					 && !loopcxt_ioctl_enabled(_lc)
+
+/*
+ * @lc: context
+ * @device: device name, absolute device path or NULL to reset the current setting
+ *
+ * Sets device, absolute paths (e.g. "/dev/loop<N>") are unchanged, device
+ * names ("loop<N>") are converted to the path (/dev/loop<N> or to
+ * /dev/loop/<N>)
+ *
+ * This sets the device name, but does not check if the device exists!
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_set_device(struct loopdev_cxt *lc, const char *device)
+{
+	if (!lc)
+		return -EINVAL;
+
+	if (lc->fd >= 0) {
+		close(lc->fd);
+		DBG(CXT, ul_debugobj(lc, "closing old open fd"));
+	}
+	lc->fd = -1;
+	lc->mode = 0;
+	lc->has_info = 0;
+	lc->info_failed = 0;
+	*lc->device = '\0';
+	memset(&lc->info, 0, sizeof(lc->info));
+
+	/* set new */
+	if (device) {
+		if (*device != '/') {
+			const char *dir = _PATH_DEV;
+
+			/* compose device name for /dev/loop<n> or /dev/loop/<n> */
+			if (lc->flags & LOOPDEV_FL_DEVSUBDIR) {
+				if (strlen(device) < 5)
+					return -1;
+				device += 4;
+				dir = _PATH_DEV_LOOP "/";	/* _PATH_DEV uses tailing slash */
+			}
+			snprintf(lc->device, sizeof(lc->device), "%s%s",
+				dir, device);
+		} else {
+			strncpy(lc->device, device, sizeof(lc->device));
+			lc->device[sizeof(lc->device) - 1] = '\0';
+		}
+		DBG(CXT, ul_debugobj(lc, "%s name assigned", device));
+	}
+
+	sysfs_deinit(&lc->sysfs);
+	return 0;
+}
+
+int loopcxt_has_device(struct loopdev_cxt *lc)
+{
+	return lc && *lc->device;
+}
+
+/*
+ * @lc: context
+ * @flags: LOOPDEV_FL_* flags
+ *
+ * Initilize loop handler.
+ *
+ * We have two sets of the flags:
+ *
+ *	* LOOPDEV_FL_* flags control loopcxt_* API behavior
+ *
+ *	* LO_FLAGS_* are kernel flags used for LOOP_{SET,GET}_STAT64 ioctls
+ *
+ * Note about LOOPDEV_FL_{RDONLY,RDWR} flags. These flags are used for open(2)
+ * syscall to open loop device. By default is the device open read-only.
+ *
+ * The expection is loopcxt_setup_device(), where the device is open read-write
+ * if LO_FLAGS_READ_ONLY flags is not set (see loopcxt_set_flags()).
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int loopcxt_init(struct loopdev_cxt *lc, int flags)
+{
+	int rc;
+	struct stat st;
+	struct loopdev_cxt dummy = UL_LOOPDEVCXT_EMPTY;
+
+	if (!lc)
+		return -EINVAL;
+
+	loopdev_init_debug();
+	DBG(CXT, ul_debugobj(lc, "initialize context"));
+
+	memcpy(lc, &dummy, sizeof(dummy));
+	lc->flags = flags;
+
+	rc = loopcxt_set_device(lc, NULL);
+	if (rc)
+		return rc;
+
+	if (stat(_PATH_SYS_BLOCK, &st) || !S_ISDIR(st.st_mode)) {
+		lc->flags |= LOOPDEV_FL_NOSYSFS;
+		lc->flags &= ~LOOPDEV_FL_NOIOCTL;
+		DBG(CXT, ul_debugobj(lc, "init: disable /sys usage"));
+	}
+
+	if (!(lc->flags & LOOPDEV_FL_NOSYSFS) &&
+	    get_linux_version() >= KERNEL_VERSION(2,6,37)) {
+		/*
+		 * Use only sysfs for basic information about loop devices
+		 */
+		lc->flags |= LOOPDEV_FL_NOIOCTL;
+		DBG(CXT, ul_debugobj(lc, "init: ignore ioctls"));
+	}
+
+	if (!(lc->flags & LOOPDEV_FL_CONTROL) && !stat(_PATH_DEV_LOOPCTL, &st)) {
+		lc->flags |= LOOPDEV_FL_CONTROL;
+		DBG(CXT, ul_debugobj(lc, "init: loop-control detected "));
+	}
+
+	return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Deinitialize loop context
+ */
+void loopcxt_deinit(struct loopdev_cxt *lc)
+{
+	int errsv = errno;
+
+	if (!lc)
+		return;
+
+	DBG(CXT, ul_debugobj(lc, "de-initialize"));
+
+	free(lc->filename);
+	lc->filename = NULL;
+
+	ignore_result( loopcxt_set_device(lc, NULL) );
+	loopcxt_deinit_iterator(lc);
+
+	errno = errsv;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns newly allocated device path.
+ */
+char *loopcxt_strdup_device(struct loopdev_cxt *lc)
+{
+	if (!lc || !*lc->device)
+		return NULL;
+	return strdup(lc->device);
+}
+
+/*
+ * @lc: context
+ *
+ * Returns pointer device name in the @lc struct.
+ */
+const char *loopcxt_get_device(struct loopdev_cxt *lc)
+{
+	return lc && *lc->device ? lc->device : NULL;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns pointer to the sysfs context (see lib/sysfs.c)
+ */
+struct sysfs_cxt *loopcxt_get_sysfs(struct loopdev_cxt *lc)
+{
+	if (!lc || !*lc->device || (lc->flags & LOOPDEV_FL_NOSYSFS))
+		return NULL;
+
+	if (!lc->sysfs.devno) {
+		dev_t devno = sysfs_devname_to_devno(lc->device, NULL);
+		if (!devno) {
+			DBG(CXT, ul_debugobj(lc, "sysfs: failed devname to devno"));
+			return NULL;
+		}
+		if (sysfs_init(&lc->sysfs, devno, NULL)) {
+			DBG(CXT, ul_debugobj(lc, "sysfs: init failed"));
+			return NULL;
+		}
+	}
+
+	return &lc->sysfs;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: file descriptor to the open loop device or <0 on error. The mode
+ *          depends on LOOPDEV_FL_{RDWR,RDONLY} context flags. Default is
+ *          read-only.
+ */
+int loopcxt_get_fd(struct loopdev_cxt *lc)
+{
+	if (!lc || !*lc->device)
+		return -EINVAL;
+
+	if (lc->fd < 0) {
+		lc->mode = lc->flags & LOOPDEV_FL_RDWR ? O_RDWR : O_RDONLY;
+		lc->fd = open(lc->device, lc->mode | O_CLOEXEC);
+		DBG(CXT, ul_debugobj(lc, "open %s [%s]: %m", lc->device,
+				lc->flags & LOOPDEV_FL_RDWR ? "rw" : "ro"));
+	}
+	return lc->fd;
+}
+
+int loopcxt_set_fd(struct loopdev_cxt *lc, int fd, int mode)
+{
+	if (!lc)
+		return -EINVAL;
+
+	lc->fd = fd;
+	lc->mode = mode;
+	return 0;
+}
+
+/*
+ * @lc: context
+ * @flags: LOOPITER_FL_* flags
+ *
+ * Iterator allows to scan list of the free or used loop devices.
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_init_iterator(struct loopdev_cxt *lc, int flags)
+{
+	struct loopdev_iter *iter;
+	struct stat st;
+
+	if (!lc)
+		return -EINVAL;
+
+
+	iter = &lc->iter;
+	DBG(ITER, ul_debugobj(iter, "initialize"));
+
+	/* always zeroize
+	 */
+	memset(iter, 0, sizeof(*iter));
+	iter->ncur = -1;
+	iter->flags = flags;
+	iter->default_check = 1;
+
+	if (!lc->extra_check) {
+		/*
+		 * Check for /dev/loop/<N> subdirectory
+		 */
+		if (!(lc->flags & LOOPDEV_FL_DEVSUBDIR) &&
+		    stat(_PATH_DEV_LOOP, &st) == 0 && S_ISDIR(st.st_mode))
+			lc->flags |= LOOPDEV_FL_DEVSUBDIR;
+
+		lc->extra_check = 1;
+	}
+	return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_deinit_iterator(struct loopdev_cxt *lc)
+{
+	struct loopdev_iter *iter;
+
+	if (!lc)
+		return -EINVAL;
+
+	iter = &lc->iter;
+	DBG(ITER, ul_debugobj(iter, "de-initialize"));
+
+	free(iter->minors);
+	if (iter->proc)
+		fclose(iter->proc);
+	if (iter->sysblock)
+		closedir(iter->sysblock);
+	iter->minors = NULL;
+	iter->proc = NULL;
+	iter->sysblock = NULL;
+	iter->done = 1;
+	return 0;
+}
+
+/*
+ * Same as loopcxt_set_device, but also checks if the device is
+ * associeted with any file.
+ *
+ * Returns: <0 on error, 0 on success, 1 device does not match with
+ *         LOOPITER_FL_{USED,FREE} flags.
+ */
+static int loopiter_set_device(struct loopdev_cxt *lc, const char *device)
+{
+	int rc = loopcxt_set_device(lc, device);
+	int used;
+
+	if (rc)
+		return rc;
+
+	if (!(lc->iter.flags & LOOPITER_FL_USED) &&
+	    !(lc->iter.flags & LOOPITER_FL_FREE))
+		return 0;	/* caller does not care about device status */
+
+	if (!is_loopdev(lc->device)) {
+		DBG(ITER, ul_debugobj(&lc->iter, "%s does not exist", lc->device));
+		return -errno;
+	}
+
+	DBG(ITER, ul_debugobj(&lc->iter, "%s exist", lc->device));
+
+	used = loopcxt_get_offset(lc, NULL) == 0;
+
+	if ((lc->iter.flags & LOOPITER_FL_USED) && used)
+		return 0;
+
+	if ((lc->iter.flags & LOOPITER_FL_FREE) && !used)
+		return 0;
+
+	DBG(ITER, ul_debugobj(&lc->iter, "failed to use %s device", lc->device));
+
+	ignore_result( loopcxt_set_device(lc, NULL) );
+	return 1;
+}
+
+static int cmpnum(const void *p1, const void *p2)
+{
+	return (((* (int *) p1) > (* (int *) p2)) -
+			((* (int *) p1) < (* (int *) p2)));
+}
+
+/*
+ * The classic scandir() is more expensive and less portable.
+ * We needn't full loop device names -- loop numbers (loop<N>)
+ * are enough.
+ */
+static int loop_scandir(const char *dirname, int **ary, int hasprefix)
+{
+	DIR *dir;
+	struct dirent *d;
+	unsigned int n, count = 0, arylen = 0;
+
+	if (!dirname || !ary)
+		return 0;
+
+	DBG(ITER, ul_debug("scan dir: %s", dirname));
+
+	dir = opendir(dirname);
+	if (!dir)
+		return 0;
+	free(*ary);
+	*ary = NULL;
+
+	while((d = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+		if (d->d_type != DT_BLK && d->d_type != DT_UNKNOWN &&
+		    d->d_type != DT_LNK)
+			continue;
+#endif
+		if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+			continue;
+
+		if (hasprefix) {
+			/* /dev/loop<N> */
+			if (sscanf(d->d_name, "loop%u", &n) != 1)
+				continue;
+		} else {
+			/* /dev/loop/<N> */
+			char *end = NULL;
+
+			errno = 0;
+			n = strtol(d->d_name, &end, 10);
+			if (d->d_name == end || (end && *end) || errno)
+				continue;
+		}
+		if (n < LOOPDEV_DEFAULT_NNODES)
+			continue;			/* ignore loop<0..7> */
+
+		if (count + 1 > arylen) {
+			int *tmp;
+
+			arylen += 1;
+
+			tmp = realloc(*ary, arylen * sizeof(int));
+			if (!tmp) {
+				free(*ary);
+				closedir(dir);
+				return -1;
+			}
+			*ary = tmp;
+		}
+		if (*ary)
+			(*ary)[count++] = n;
+	}
+	if (count && *ary)
+		qsort(*ary, count, sizeof(int), cmpnum);
+
+	closedir(dir);
+	return count;
+}
+
+/*
+ * Set the next *used* loop device according to /proc/partitions.
+ *
+ * Loop devices smaller than 512 bytes are invisible for this function.
+ */
+static int loopcxt_next_from_proc(struct loopdev_cxt *lc)
+{
+	struct loopdev_iter *iter = &lc->iter;
+	char buf[BUFSIZ];
+
+	DBG(ITER, ul_debugobj(iter, "scan /proc/partitions"));
+
+	if (!iter->proc)
+		iter->proc = fopen(_PATH_PROC_PARTITIONS, "r");
+	if (!iter->proc)
+		return 1;
+
+	while (fgets(buf, sizeof(buf), iter->proc)) {
+		unsigned int m;
+		char name[128 + 1];
+
+
+		if (sscanf(buf, " %u %*s %*s %128[^\n ]",
+			   &m, name) != 2 || m != LOOPDEV_MAJOR)
+			continue;
+
+		DBG(ITER, ul_debugobj(iter, "checking %s", name));
+
+		if (loopiter_set_device(lc, name) == 0)
+			return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Set the next *used* loop device according to
+ * /sys/block/loopN/loop/backing_file (kernel >= 2.6.37 is required).
+ *
+ * This is preferred method.
+ */
+static int loopcxt_next_from_sysfs(struct loopdev_cxt *lc)
+{
+	struct loopdev_iter *iter = &lc->iter;
+	struct dirent *d;
+	int fd;
+
+	DBG(ITER, ul_debugobj(iter, "scanning /sys/block"));
+
+	if (!iter->sysblock)
+		iter->sysblock = opendir(_PATH_SYS_BLOCK);
+
+	if (!iter->sysblock)
+		return 1;
+
+	fd = dirfd(iter->sysblock);
+
+	while ((d = readdir(iter->sysblock))) {
+		char name[256];
+		struct stat st;
+
+		DBG(ITER, ul_debugobj(iter, "check %s", d->d_name));
+
+		if (strcmp(d->d_name, ".") == 0
+		    || strcmp(d->d_name, "..") == 0
+		    || strncmp(d->d_name, "loop", 4) != 0)
+			continue;
+
+		snprintf(name, sizeof(name), "%s/loop/backing_file", d->d_name);
+		if (fstat_at(fd, _PATH_SYS_BLOCK, name, &st, 0) != 0)
+			continue;
+
+		if (loopiter_set_device(lc, d->d_name) == 0)
+			return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * @lc: context, has to initialized by loopcxt_init_iterator()
+ *
+ * Returns: 0 on success, -1 on error, 1 at the end of scanning. The details
+ *          about the current loop device are available by
+ *          loopcxt_get_{fd,backing_file,device,offset, ...} functions.
+ */
+int loopcxt_next(struct loopdev_cxt *lc)
+{
+	struct loopdev_iter *iter;
+
+	if (!lc)
+		return -EINVAL;
+
+
+	iter = &lc->iter;
+	if (iter->done)
+		return 1;
+
+	DBG(ITER, ul_debugobj(iter, "next"));
+
+	/* A) Look for used loop devices in /proc/partitions ("losetup -a" only)
+	 */
+	if (iter->flags & LOOPITER_FL_USED) {
+		int rc;
+
+		if (loopcxt_sysfs_available(lc))
+			rc = loopcxt_next_from_sysfs(lc);
+		else
+			rc = loopcxt_next_from_proc(lc);
+		if (rc == 0)
+			return 0;
+		goto done;
+	}
+
+	/* B) Classic way, try first eight loop devices (default number
+	 *    of loop devices). This is enough for 99% of all cases.
+	 */
+	if (iter->default_check) {
+		DBG(ITER, ul_debugobj(iter, "next: default check"));
+		for (++iter->ncur; iter->ncur < LOOPDEV_DEFAULT_NNODES;
+							iter->ncur++) {
+			char name[16];
+			snprintf(name, sizeof(name), "loop%d", iter->ncur);
+
+			if (loopiter_set_device(lc, name) == 0)
+				return 0;
+		}
+		iter->default_check = 0;
+	}
+
+	/* C) the worst possibility, scan whole /dev or /dev/loop/<N>
+	 */
+	if (!iter->minors) {
+		DBG(ITER, ul_debugobj(iter, "next: scanning /dev"));
+		iter->nminors = (lc->flags & LOOPDEV_FL_DEVSUBDIR) ?
+			loop_scandir(_PATH_DEV_LOOP, &iter->minors, 0) :
+			loop_scandir(_PATH_DEV, &iter->minors, 1);
+		iter->ncur = -1;
+	}
+	for (++iter->ncur; iter->ncur < iter->nminors; iter->ncur++) {
+		char name[16];
+		snprintf(name, sizeof(name), "loop%d", iter->minors[iter->ncur]);
+
+		if (loopiter_set_device(lc, name) == 0)
+			return 0;
+	}
+done:
+	loopcxt_deinit_iterator(lc);
+	return 1;
+}
+
+/*
+ * @device: path to device
+ */
+int is_loopdev(const char *device)
+{
+	struct stat st;
+
+	if (!device)
+		return 0;
+
+	return (stat(device, &st) == 0 &&
+		S_ISBLK(st.st_mode) &&
+		major(st.st_rdev) == LOOPDEV_MAJOR);
+}
+
+/*
+ * @lc: context
+ *
+ * Returns result from LOOP_GET_STAT64 ioctl or NULL on error.
+ */
+struct loop_info64 *loopcxt_get_info(struct loopdev_cxt *lc)
+{
+	int fd;
+
+	if (!lc || lc->info_failed) {
+		errno = EINVAL;
+		return NULL;
+	}
+	errno = 0;
+	if (lc->has_info)
+		return &lc->info;
+
+	fd = loopcxt_get_fd(lc);
+	if (fd < 0)
+		return NULL;
+
+	if (ioctl(fd, LOOP_GET_STATUS64, &lc->info) == 0) {
+		lc->has_info = 1;
+		lc->info_failed = 0;
+		DBG(CXT, ul_debugobj(lc, "reading loop_info64 OK"));
+		return &lc->info;
+	}
+
+	lc->info_failed = 1;
+	DBG(CXT, ul_debugobj(lc, "reading loop_info64 FAILED"));
+
+	return NULL;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns (allocated) string with path to the file assicieted
+ * with the current loop device.
+ */
+char *loopcxt_get_backing_file(struct loopdev_cxt *lc)
+{
+	struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+	char *res = NULL;
+
+	if (sysfs)
+		/*
+		 * This is always preffered, the loop_info64
+		 * has too small buffer for the filename.
+		 */
+		res = sysfs_strdup(sysfs, "loop/backing_file");
+
+	if (!res && loopcxt_ioctl_enabled(lc)) {
+		struct loop_info64 *lo = loopcxt_get_info(lc);
+
+		if (lo) {
+			lo->lo_file_name[LO_NAME_SIZE - 2] = '*';
+			lo->lo_file_name[LO_NAME_SIZE - 1] = '\0';
+			res = strdup((char *) lo->lo_file_name);
+		}
+	}
+
+	DBG(CXT, ul_debugobj(lc, "get_backing_file [%s]", res));
+	return res;
+}
+
+/*
+ * @lc: context
+ * @offset: returns offset number for the given device
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_offset(struct loopdev_cxt *lc, uint64_t *offset)
+{
+	struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+	int rc = -EINVAL;
+
+	if (sysfs)
+		rc = sysfs_read_u64(sysfs, "loop/offset", offset);
+
+	if (rc && loopcxt_ioctl_enabled(lc)) {
+		struct loop_info64 *lo = loopcxt_get_info(lc);
+		if (lo) {
+			if (offset)
+				*offset = lo->lo_offset;
+			rc = 0;
+		} else
+			rc = -errno;
+	}
+
+	DBG(CXT, ul_debugobj(lc, "get_offset [rc=%d]", rc));
+	return rc;
+}
+
+/*
+ * @lc: context
+ * @sizelimit: returns size limit for the given device
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_sizelimit(struct loopdev_cxt *lc, uint64_t *size)
+{
+	struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+	int rc = -EINVAL;
+
+	if (sysfs)
+		rc = sysfs_read_u64(sysfs, "loop/sizelimit", size);
+
+	if (rc && loopcxt_ioctl_enabled(lc)) {
+		struct loop_info64 *lo = loopcxt_get_info(lc);
+		if (lo) {
+			if (size)
+				*size = lo->lo_sizelimit;
+			rc = 0;
+		} else
+			rc = -errno;
+	}
+
+	DBG(CXT, ul_debugobj(lc, "get_sizelimit [rc=%d]", rc));
+	return rc;
+}
+
+/*
+ * @lc: context
+ * @devno: returns encryption type
+ *
+ * Cryptoloop is DEPRECATED!
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_encrypt_type(struct loopdev_cxt *lc, uint32_t *type)
+{
+	struct loop_info64 *lo = loopcxt_get_info(lc);
+	int rc;
+
+	/* not provided by sysfs */
+	if (lo) {
+		if (type)
+			*type = lo->lo_encrypt_type;
+		rc = 0;
+	} else
+		rc = -errno;
+
+	DBG(CXT, ul_debugobj(lc, "get_encrypt_type [rc=%d]", rc));
+	return rc;
+}
+
+/*
+ * @lc: context
+ * @devno: returns crypt name
+ *
+ * Cryptoloop is DEPRECATED!
+ *
+ * Returns: <0 on error, 0 on success
+ */
+const char *loopcxt_get_crypt_name(struct loopdev_cxt *lc)
+{
+	struct loop_info64 *lo = loopcxt_get_info(lc);
+
+	if (lo)
+		return (char *) lo->lo_crypt_name;
+
+	DBG(CXT, ul_debugobj(lc, "get_crypt_name failed"));
+	return NULL;
+}
+
+/*
+ * @lc: context
+ * @devno: returns backing file devno
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_backing_devno(struct loopdev_cxt *lc, dev_t *devno)
+{
+	struct loop_info64 *lo = loopcxt_get_info(lc);
+	int rc;
+
+	if (lo) {
+		if (devno)
+			*devno = lo->lo_device;
+		rc = 0;
+	} else
+		rc = -errno;
+
+	DBG(CXT, ul_debugobj(lc, "get_backing_devno [rc=%d]", rc));
+	return rc;
+}
+
+/*
+ * @lc: context
+ * @ino: returns backing file inode
+ *
+ * Returns: <0 on error, 0 on success
+ */
+int loopcxt_get_backing_inode(struct loopdev_cxt *lc, ino_t *ino)
+{
+	struct loop_info64 *lo = loopcxt_get_info(lc);
+	int rc;
+
+	if (lo) {
+		if (ino)
+			*ino = lo->lo_inode;
+		rc = 0;
+	} else
+		rc = -errno;
+
+	DBG(CXT, ul_debugobj(lc, "get_backing_inode [rc=%d]", rc));
+	return rc;
+}
+
+/*
+ * Check if the kernel supports partitioned loop devices.
+ *
+ * Notes:
+ *   - kernels < 3.2 support partitioned loop devices and PT scanning
+ *     only if max_part= module paremeter is non-zero
+ *
+ *   - kernels >= 3.2 always support partitioned loop devices
+ *
+ *   - kernels >= 3.2 always support BLKPG_{ADD,DEL}_PARTITION ioctls
+ *
+ *   - kernels >= 3.2 enable PT scanner only if max_part= is non-zero or if the
+ *     LO_FLAGS_PARTSCAN flag is set for the device. The PT scanner is disabled
+ *     by default.
+ *
+ *  See kernel commit e03c8dd14915fabc101aa495828d58598dc5af98.
+ */
+int loopmod_supports_partscan(void)
+{
+	int rc, ret = 0;
+	FILE *f;
+
+	if (get_linux_version() >= KERNEL_VERSION(3,2,0))
+		return 1;
+
+	f = fopen("/sys/module/loop/parameters/max_part", "r");
+	if (!f)
+		return 0;
+	rc = fscanf(f, "%d", &ret);
+	fclose(f);
+	return rc == 1 ? ret : 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: 1 if the partscan flags is set *or* (for old kernels) partitions
+ * scannig is enabled for all loop devices.
+ */
+int loopcxt_is_partscan(struct loopdev_cxt *lc)
+{
+	struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+	if (sysfs) {
+		/* kernel >= 3.2 */
+		int fl;
+		if (sysfs_read_int(sysfs, "loop/partscan", &fl) == 0)
+			return fl;
+	}
+
+	/* old kernels (including kernels without loopN/loop/<flags> directory */
+	return loopmod_supports_partscan();
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: 1 if the autoclear flags is set.
+ */
+int loopcxt_is_autoclear(struct loopdev_cxt *lc)
+{
+	struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+	if (sysfs) {
+		int fl;
+		if (sysfs_read_int(sysfs, "loop/autoclear", &fl) == 0)
+			return fl;
+	}
+
+	if (loopcxt_ioctl_enabled(lc)) {
+		struct loop_info64 *lo = loopcxt_get_info(lc);
+		if (lo)
+			return lo->lo_flags & LO_FLAGS_AUTOCLEAR;
+	}
+	return 0;
+}
+
+/*
+ * @lc: context
+ *
+ * Returns: 1 if the readonly flags is set.
+ */
+int loopcxt_is_readonly(struct loopdev_cxt *lc)
+{
+	struct sysfs_cxt *sysfs = loopcxt_get_sysfs(lc);
+
+	if (sysfs) {
+		int fl;
+		if (sysfs_read_int(sysfs, "ro", &fl) == 0)
+			return fl;
+	}
+
+	if (loopcxt_ioctl_enabled(lc)) {
+		struct loop_info64 *lo = loopcxt_get_info(lc);
+		if (lo)
+			return lo->lo_flags & LO_FLAGS_READ_ONLY;
+	}
+	return 0;
+}
+
+/*
+ * @lc: context
+ * @st: backing file stat or NULL
+ * @backing_file: filename
+ * @offset: offset
+ * @flags: LOOPDEV_FL_OFFSET if @offset should not be ignored
+ *
+ * Returns 1 if the current @lc loopdev is associated with the given backing
+ * file. Note that the preferred way is to use devno and inode number rather
+ * than filename. The @backing_file filename is poor solution usable in case
+ * that you don't have rights to call stat().
+ *
+ * Don't forget that old kernels provide very restricted (in size) backing
+ * filename by LOOP_GET_STAT64 ioctl only.
+ */
+int loopcxt_is_used(struct loopdev_cxt *lc,
+		    struct stat *st,
+		    const char *backing_file,
+		    uint64_t offset,
+		    int flags)
+{
+	ino_t ino;
+	dev_t dev;
+
+	if (!lc)
+		return 0;
+
+	DBG(CXT, ul_debugobj(lc, "checking %s vs. %s",
+				loopcxt_get_device(lc),
+				backing_file));
+
+	if (st && loopcxt_get_backing_inode(lc, &ino) == 0 &&
+		  loopcxt_get_backing_devno(lc, &dev) == 0) {
+
+		if (ino == st->st_ino && dev == st->st_dev)
+			goto found;
+
+		/* don't use filename if we have devno and inode */
+		return 0;
+	}
+
+	/* poor man's solution */
+	if (backing_file) {
+		char *name = loopcxt_get_backing_file(lc);
+		int rc = name && strcmp(name, backing_file) == 0;
+
+		free(name);
+		if (rc)
+			goto found;
+	}
+
+	return 0;
+found:
+	if (flags & LOOPDEV_FL_OFFSET) {
+		uint64_t off;
+
+		return loopcxt_get_offset(lc, &off) == 0 && off == offset;
+	}
+	return 1;
+}
+
+/*
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ */
+int loopcxt_set_offset(struct loopdev_cxt *lc, uint64_t offset)
+{
+	if (!lc)
+		return -EINVAL;
+	lc->info.lo_offset = offset;
+
+	DBG(CXT, ul_debugobj(lc, "set offset=%jd", offset));
+	return 0;
+}
+
+/*
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ */
+int loopcxt_set_sizelimit(struct loopdev_cxt *lc, uint64_t sizelimit)
+{
+	if (!lc)
+		return -EINVAL;
+	lc->info.lo_sizelimit = sizelimit;
+
+	DBG(CXT, ul_debugobj(lc, "set sizelimit=%jd", sizelimit));
+	return 0;
+}
+
+/*
+ * @lc: context
+ * @flags: kernel LO_FLAGS_{READ_ONLY,USE_AOPS,AUTOCLEAR} flags
+ *
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int loopcxt_set_flags(struct loopdev_cxt *lc, uint32_t flags)
+{
+	if (!lc)
+		return -EINVAL;
+	lc->info.lo_flags = flags;
+
+	DBG(CXT, ul_debugobj(lc, "set flags=%u", (unsigned) flags));
+	return 0;
+}
+
+/*
+ * @lc: context
+ * @filename: backing file path (the path will be canonicalized)
+ *
+ * The setting is removed by loopcxt_set_device() loopcxt_next()!
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int loopcxt_set_backing_file(struct loopdev_cxt *lc, const char *filename)
+{
+	if (!lc)
+		return -EINVAL;
+
+	lc->filename = canonicalize_path(filename);
+	if (!lc->filename)
+		return -errno;
+
+	strncpy((char *)lc->info.lo_file_name, lc->filename, LO_NAME_SIZE);
+	lc->info.lo_file_name[LO_NAME_SIZE- 1] = '\0';
+
+	DBG(CXT, ul_debugobj(lc, "set backing file=%s", lc->info.lo_file_name));
+	return 0;
+}
+
+/*
+ * In kernels prior to v3.9, if the offset or sizelimit options
+ * are used, the block device's size won't be synced automatically.
+ * blockdev --getsize64 and filesystems will use the backing
+ * file size until the block device has been re-opened or the
+ * LOOP_SET_CAPACITY ioctl is called to sync the sizes.
+ *
+ * Since mount -oloop uses the LO_FLAGS_AUTOCLEAR option and passes
+ * the open file descriptor to the mount system call, we need to use
+ * the ioctl. Calling losetup directly doesn't have this problem since
+ * it closes the device when it exits and whatever consumes the device
+ * next will re-open it, causing the resync.
+ */
+static int loopcxt_check_size(struct loopdev_cxt *lc, int file_fd)
+{
+	uint64_t size, expected_size;
+	int dev_fd;
+	struct stat st;
+
+	if (!lc->info.lo_offset && !lc->info.lo_sizelimit)
+		return 0;
+
+	if (fstat(file_fd, &st)) {
+		DBG(CXT, ul_debugobj(lc, "failed to fstat backing file"));
+		return -errno;
+	}
+	if (S_ISBLK(st.st_mode)) {
+		if (blkdev_get_size(file_fd,
+				(unsigned long long *) &expected_size)) {
+			DBG(CXT, ul_debugobj(lc, "failed to determine device size"));
+			return -errno;
+		}
+	} else
+		expected_size = st.st_size;
+
+	if (expected_size == 0 || expected_size <= lc->info.lo_offset) {
+		DBG(CXT, ul_debugobj(lc, "failed to determine expected size"));
+		return 0;	/* ignore this error */
+	}
+
+	if (lc->info.lo_offset > 0)
+		expected_size -= lc->info.lo_offset;
+
+	if (lc->info.lo_sizelimit > 0 && lc->info.lo_sizelimit < expected_size)
+		expected_size = lc->info.lo_sizelimit;
+
+	dev_fd = loopcxt_get_fd(lc);
+	if (dev_fd < 0) {
+		DBG(CXT, ul_debugobj(lc, "failed to get loop FD"));
+		return -errno;
+	}
+
+	if (blkdev_get_size(dev_fd, (unsigned long long *) &size)) {
+		DBG(CXT, ul_debugobj(lc, "failed to determine loopdev size"));
+		return -errno;
+	}
+
+	/* It's block device, so, align to 512-byte sectors */
+	if (expected_size % 512) {
+		DBG(CXT, ul_debugobj(lc, "expected size misaligned to 512-byte sectors"));
+		expected_size = (expected_size >> 9) << 9;
+	}
+
+	if (expected_size != size) {
+		DBG(CXT, ul_debugobj(lc, "warning: loopdev and expected "
+				      "size dismatch (%ju/%ju)",
+				      size, expected_size));
+
+		if (loopcxt_set_capacity(lc)) {
+			/* ioctl not available */
+			if (errno == ENOTTY || errno == EINVAL)
+				errno = ERANGE;
+			return -errno;
+		}
+
+		if (blkdev_get_size(dev_fd, (unsigned long long *) &size))
+			return -errno;
+
+		if (expected_size != size) {
+			errno = ERANGE;
+			DBG(CXT, ul_debugobj(lc, "failed to set loopdev size, "
+					"size: %ju, expected: %ju",
+					size, expected_size));
+			return -errno;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * @cl: context
+ *
+ * Associate the current device (see loopcxt_{set,get}_device()) with
+ * a file (see loopcxt_set_backing_file()).
+ *
+ * The device is initialized read-write by default. If you want read-only
+ * device then set LO_FLAGS_READ_ONLY by loopcxt_set_flags(). The LOOPDEV_FL_*
+ * flags are ignored and modified according to LO_FLAGS_*.
+ *
+ * If the device is already open by loopcxt_get_fd() then this setup device
+ * function will re-open the device to fix read/write mode.
+ *
+ * The device is also initialized read-only if the backing file is not
+ * possible to open read-write (e.g. read-only FS).
+ *
+ * Returns: <0 on error, 0 on success.
+ */
+int loopcxt_setup_device(struct loopdev_cxt *lc)
+{
+	int file_fd, dev_fd, mode = O_RDWR, rc = -1, cnt = 0;
+
+	if (!lc || !*lc->device || !lc->filename)
+		return -EINVAL;
+
+	DBG(SETUP, ul_debugobj(lc, "device setup requested"));
+
+	/*
+	 * Open backing file and device
+	 */
+	if (lc->info.lo_flags & LO_FLAGS_READ_ONLY)
+		mode = O_RDONLY;
+
+	if ((file_fd = open(lc->filename, mode | O_CLOEXEC)) < 0) {
+		if (mode != O_RDONLY && (errno == EROFS || errno == EACCES))
+			file_fd = open(lc->filename, mode = O_RDONLY);
+
+		if (file_fd < 0) {
+			DBG(SETUP, ul_debugobj(lc, "open backing file failed: %m"));
+			return -errno;
+		}
+	}
+	DBG(SETUP, ul_debugobj(lc, "backing file open: OK"));
+
+	if (lc->fd != -1 && lc->mode != mode) {
+		DBG(SETUP, ul_debugobj(lc, "closing already open device (mode mismatch)"));
+		close(lc->fd);
+		lc->fd = -1;
+		lc->mode = 0;
+	}
+
+	if (mode == O_RDONLY) {
+		lc->flags |= LOOPDEV_FL_RDONLY;			/* open() mode */
+		lc->info.lo_flags |= LO_FLAGS_READ_ONLY;	/* kernel loopdev mode */
+	} else {
+		lc->flags |= LOOPDEV_FL_RDWR;			/* open() mode */
+		lc->info.lo_flags &= ~LO_FLAGS_READ_ONLY;
+		lc->flags &= ~LOOPDEV_FL_RDONLY;
+	}
+
+	do {
+		errno = 0;
+		dev_fd = loopcxt_get_fd(lc);
+		if (dev_fd >= 0 || lc->control_ok == 0)
+			break;
+		if (errno != EACCES && errno != ENOENT)
+			break;
+		/* We have permissions to open /dev/loop-control, but open
+		 * /dev/loopN failed with EACCES, it's probably because udevd
+		 * does not applied chown yet. Let's wait a moment. */
+		usleep(25000);
+	} while (cnt++ < 16);
+
+	if (dev_fd < 0) {
+		rc = -errno;
+		goto err;
+	}
+
+	DBG(SETUP, ul_debugobj(lc, "device open: OK"));
+
+	/*
+	 * Set FD
+	 */
+	if (ioctl(dev_fd, LOOP_SET_FD, file_fd) < 0) {
+		rc = -errno;
+		DBG(SETUP, ul_debugobj(lc, "LOOP_SET_FD failed: %m"));
+		goto err;
+	}
+
+	DBG(SETUP, ul_debugobj(lc, "LOOP_SET_FD: OK"));
+
+	if (ioctl(dev_fd, LOOP_SET_STATUS64, &lc->info)) {
+		DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64 failed: %m"));
+		goto err;
+	}
+
+	DBG(SETUP, ul_debugobj(lc, "LOOP_SET_STATUS64: OK"));
+
+	if ((rc = loopcxt_check_size(lc, file_fd)))
+		goto err;
+
+	close(file_fd);
+
+	memset(&lc->info, 0, sizeof(lc->info));
+	lc->has_info = 0;
+	lc->info_failed = 0;
+
+	DBG(SETUP, ul_debugobj(lc, "success [rc=0]"));
+	return 0;
+err:
+	if (file_fd >= 0)
+		close(file_fd);
+	if (dev_fd >= 0 && rc != -EBUSY)
+		ioctl(dev_fd, LOOP_CLR_FD, 0);
+
+	DBG(SETUP, ul_debugobj(lc, "failed [rc=%d]", rc));
+	return rc;
+}
+
+int loopcxt_set_capacity(struct loopdev_cxt *lc)
+{
+	int fd = loopcxt_get_fd(lc);
+
+	if (fd < 0)
+		return -EINVAL;
+
+	/* Kernels prior to v2.6.30 don't support this ioctl */
+	if (ioctl(fd, LOOP_SET_CAPACITY, 0) < 0) {
+		int rc = -errno;
+		DBG(CXT, ul_debugobj(lc, "LOOP_SET_CAPACITY failed: %m"));
+		return rc;
+	}
+
+	DBG(CXT, ul_debugobj(lc, "capacity set"));
+	return 0;
+}
+
+int loopcxt_delete_device(struct loopdev_cxt *lc)
+{
+	int fd = loopcxt_get_fd(lc);
+
+	if (fd < 0)
+		return -EINVAL;
+
+	if (ioctl(fd, LOOP_CLR_FD, 0) < 0) {
+		DBG(CXT, ul_debugobj(lc, "LOOP_CLR_FD failed: %m"));
+		return -errno;
+	}
+
+	DBG(CXT, ul_debugobj(lc, "device removed"));
+	return 0;
+}
+
+int loopcxt_add_device(struct loopdev_cxt *lc)
+{
+	int rc = -EINVAL;
+	int ctl, nr = -1;
+	const char *p, *dev = loopcxt_get_device(lc);
+
+	if (!dev)
+		goto done;
+
+	if (!(lc->flags & LOOPDEV_FL_CONTROL)) {
+		rc = -ENOSYS;
+		goto done;
+	}
+
+	p = strrchr(dev, '/');
+	if (!p || (sscanf(p, "/loop%d", &nr) != 1 && sscanf(p, "/%d", &nr) != 1)
+	       || nr < 0)
+		goto done;
+
+	ctl = open(_PATH_DEV_LOOPCTL, O_RDWR|O_CLOEXEC);
+	if (ctl >= 0) {
+		DBG(CXT, ul_debugobj(lc, "add_device %d", nr));
+		rc = ioctl(ctl, LOOP_CTL_ADD, nr);
+		close(ctl);
+	}
+	lc->control_ok = rc >= 0 ? 1 : 0;
+done:
+	DBG(CXT, ul_debugobj(lc, "add_device done [rc=%d]", rc));
+	return rc;
+}
+
+/*
+ * Note that LOOP_CTL_GET_FREE ioctl is supported since kernel 3.1. In older
+ * kernels we have to check all loop devices to found unused one.
+ *
+ * See kernel commit 770fe30a46a12b6fb6b63fbe1737654d28e8484.
+ */
+int loopcxt_find_unused(struct loopdev_cxt *lc)
+{
+	int rc = -1;
+
+	DBG(CXT, ul_debugobj(lc, "find_unused requested"));
+
+	if (lc->flags & LOOPDEV_FL_CONTROL) {
+		int ctl = open(_PATH_DEV_LOOPCTL, O_RDWR|O_CLOEXEC);
+
+		if (ctl >= 0)
+			rc = ioctl(ctl, LOOP_CTL_GET_FREE);
+		if (rc >= 0) {
+			char name[16];
+			snprintf(name, sizeof(name), "loop%d", rc);
+
+			rc = loopiter_set_device(lc, name);
+		}
+		lc->control_ok = ctl >= 0 && rc == 0 ? 1 : 0;
+		if (ctl >= 0)
+			close(ctl);
+		DBG(CXT, ul_debugobj(lc, "find_unused by loop-control [rc=%d]", rc));
+	}
+
+	if (rc < 0) {
+		rc = loopcxt_init_iterator(lc, LOOPITER_FL_FREE);
+		if (rc)
+			return rc;
+
+		rc = loopcxt_next(lc);
+		loopcxt_deinit_iterator(lc);
+		DBG(CXT, ul_debugobj(lc, "find_unused by scan [rc=%d]", rc));
+	}
+	return rc;
+}
+
+
+
+/*
+ * Return: TRUE/FALSE
+ */
+int loopdev_is_autoclear(const char *device)
+{
+	struct loopdev_cxt lc;
+	int rc;
+
+	if (!device)
+		return 0;
+
+	rc = loopcxt_init(&lc, 0);
+	if (!rc)
+		rc = loopcxt_set_device(&lc, device);
+	if (!rc)
+		rc = loopcxt_is_autoclear(&lc);
+
+	loopcxt_deinit(&lc);
+	return rc;
+}
+
+char *loopdev_get_backing_file(const char *device)
+{
+	struct loopdev_cxt lc;
+	char *res = NULL;
+
+	if (!device)
+		return NULL;
+	if (loopcxt_init(&lc, 0))
+		return NULL;
+	if (loopcxt_set_device(&lc, device) == 0)
+		res = loopcxt_get_backing_file(&lc);
+
+	loopcxt_deinit(&lc);
+	return res;
+}
+
+/*
+ * Returns: TRUE/FALSE
+ */
+int loopdev_is_used(const char *device, const char *filename,
+		    uint64_t offset, int flags)
+{
+	struct loopdev_cxt lc;
+	struct stat st;
+	int rc = 0;
+
+	if (!device || !filename)
+		return 0;
+
+	rc = loopcxt_init(&lc, 0);
+	if (!rc)
+		rc = loopcxt_set_device(&lc, device);
+	if (rc)
+		return rc;
+
+	rc = !stat(filename, &st);
+	rc = loopcxt_is_used(&lc, rc ? &st : NULL, filename, offset, flags);
+
+	loopcxt_deinit(&lc);
+	return rc;
+}
+
+int loopdev_delete(const char *device)
+{
+	struct loopdev_cxt lc;
+	int rc;
+
+	if (!device)
+		return -EINVAL;
+
+	rc = loopcxt_init(&lc, 0);
+	if (!rc)
+		rc = loopcxt_set_device(&lc, device);
+	if (!rc)
+		rc = loopcxt_delete_device(&lc);
+	loopcxt_deinit(&lc);
+	return rc;
+}
+
+/*
+ * Returns: 0 = success, < 0 error, 1 not found
+ */
+int loopcxt_find_by_backing_file(struct loopdev_cxt *lc, const char *filename,
+				 uint64_t offset, int flags)
+{
+	int rc, hasst;
+	struct stat st;
+
+	if (!filename)
+		return -EINVAL;
+
+	hasst = !stat(filename, &st);
+
+	rc = loopcxt_init_iterator(lc, LOOPITER_FL_USED);
+	if (rc)
+		return rc;
+
+	while ((rc = loopcxt_next(lc)) == 0) {
+
+		if (loopcxt_is_used(lc, hasst ? &st : NULL,
+					filename, offset, flags))
+			break;
+	}
+
+	loopcxt_deinit_iterator(lc);
+	return rc;
+}
+
+/*
+ * Returns allocated string with device name
+ */
+char *loopdev_find_by_backing_file(const char *filename, uint64_t offset, int flags)
+{
+	struct loopdev_cxt lc;
+	char *res = NULL;
+
+	if (!filename)
+		return NULL;
+
+	if (loopcxt_init(&lc, 0))
+		return NULL;
+	if (loopcxt_find_by_backing_file(&lc, filename, offset, flags) == 0)
+		res = loopcxt_strdup_device(&lc);
+	loopcxt_deinit(&lc);
+
+	return res;
+}
+
+/*
+ * Returns number of loop devices associated with @file, if only one loop
+ * device is associeted with the given @filename and @loopdev is not NULL then
+ * @loopdev returns name of the device.
+ */
+int loopdev_count_by_backing_file(const char *filename, char **loopdev)
+{
+	struct loopdev_cxt lc;
+	int count = 0, rc;
+
+	if (!filename)
+		return -1;
+
+	rc = loopcxt_init(&lc, 0);
+	if (rc)
+		return rc;
+	if (loopcxt_init_iterator(&lc, LOOPITER_FL_USED))
+		return -1;
+
+	while(loopcxt_next(&lc) == 0) {
+		char *backing = loopcxt_get_backing_file(&lc);
+
+		if (!backing || strcmp(backing, filename)) {
+			free(backing);
+			continue;
+		}
+
+		free(backing);
+		if (loopdev && count == 0)
+			*loopdev = loopcxt_strdup_device(&lc);
+		count++;
+	}
+
+	loopcxt_deinit(&lc);
+
+	if (loopdev && count > 1) {
+		free(*loopdev);
+		*loopdev = NULL;
+	}
+	return count;
+}
+
diff --git a/libblkid/lib/mangle.c b/libblkid/lib/mangle.c
new file mode 100644
index 0000000..5236e97
--- /dev/null
+++ b/libblkid/lib/mangle.c
@@ -0,0 +1,166 @@
+/*
+ * Functions for \oct encoding used in mtab/fstab/swaps/etc.
+ *
+ * Based on code from mount(8).
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "mangle.h"
+#include "c.h"
+
+#define isoctal(a)		(((a) & ~7) == '0')
+
+#define from_hex(c)		(isdigit(c) ? c - '0' : tolower(c) - 'a' + 10)
+
+#define is_unwanted_char(x)	(strchr(" \t\n\\", (unsigned int) x) != NULL)
+
+
+char *mangle(const char *s)
+{
+	char *ss, *sp;
+
+	if (!s)
+		return NULL;
+
+	ss = sp = malloc(4 * strlen(s) + 1);
+	if (!sp)
+		return NULL;
+	while(1) {
+		if (!*s) {
+			*sp = '\0';
+			break;
+		}
+		if (is_unwanted_char(*s)) {
+			*sp++ = '\\';
+			*sp++ = '0' + ((*s & 0300) >> 6);
+			*sp++ = '0' + ((*s & 070) >> 3);
+			*sp++ = '0' + (*s & 07);
+		} else
+			*sp++ = *s;
+		s++;
+	}
+	return ss;
+}
+
+
+void unmangle_to_buffer(const char *s, char *buf, size_t len)
+{
+	size_t sz = 0;
+
+	if (!s)
+		return;
+
+	while(*s && sz < len - 1) {
+		if (*s == '\\' && sz + 3 < len - 1 && isoctal(s[1]) &&
+		    isoctal(s[2]) && isoctal(s[3])) {
+
+			*buf++ = 64*(s[1] & 7) + 8*(s[2] & 7) + (s[3] & 7);
+			s += 4;
+			sz += 4;
+		} else {
+			*buf++ = *s++;
+			sz++;
+		}
+	}
+	*buf = '\0';
+}
+
+void unhexmangle_to_buffer(const char *s, char *buf, size_t len)
+{
+	size_t sz = 0;
+
+	if (!s)
+		return;
+
+	while(*s && sz < len - 1) {
+		if (*s == '\\' && sz + 3 < len - 1 && s[1] == 'x' &&
+		    isxdigit(s[2]) && isxdigit(s[3])) {
+
+			*buf++ = from_hex(s[2]) << 4 | from_hex(s[3]);
+			s += 4;
+			sz += 4;
+		} else {
+			*buf++ = *s++;
+			sz++;
+		}
+	}
+	*buf = '\0';
+}
+
+static inline char *skip_nonspaces(const char *s)
+{
+	while (*s && !(*s == ' ' || *s == '\t'))
+		s++;
+	return (char *) s;
+}
+
+/*
+ * Returns mallocated buffer or NULL in case of error.
+ */
+char *unmangle(const char *s, char **end)
+{
+	char *buf;
+	char *e;
+	size_t sz;
+
+	if (!s)
+		return NULL;
+
+	e = skip_nonspaces(s);
+	sz = e - s + 1;
+
+	if (end)
+		*end = e;
+	if (e == s)
+		return NULL;	/* empty string */
+
+	buf = malloc(sz);
+	if (!buf)
+		return NULL;
+
+	unmangle_to_buffer(s, buf, sz);
+	return buf;
+}
+
+#ifdef TEST_PROGRAM
+#include <errno.h>
+int main(int argc, char *argv[])
+{
+	char *p = NULL;
+	if (argc < 3) {
+		fprintf(stderr, "usage: %s --mangle|unmangle <string>\n",
+						program_invocation_short_name);
+		return EXIT_FAILURE;
+	}
+
+	if (!strcmp(argv[1], "--mangle")) {
+		p = mangle(argv[2]);
+		printf("mangled: '%s'\n", p);
+		free(p);
+	}
+
+	else if (!strcmp(argv[1], "--unmangle")) {
+		char *x = unmangle(argv[2], NULL);
+
+		if (x) {
+			printf("unmangled: '%s'\n", x);
+			free(x);
+		}
+
+		x = strdup(argv[2]);
+		unmangle_to_buffer(x, x, strlen(x) + 1);
+
+		if (x) {
+			printf("self-unmangled: '%s'\n", x);
+			free(x);
+		}
+	}
+
+	return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/lib/match.c b/libblkid/lib/match.c
new file mode 100644
index 0000000..9be82b0
--- /dev/null
+++ b/libblkid/lib/match.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+
+#include <string.h>
+
+#include "match.h"
+
+/*
+ * match_fstype:
+ * @type: filesystem type
+ * @pattern: filesystem name or comma delimited list of names
+ *
+ * The @pattern list of filesystem can be prefixed with a global
+ * "no" prefix to invert matching of the whole list. The "no" could
+ * also be used for individual items in the @pattern list. So,
+ * "nofoo,bar" has the same meaning as "nofoo,nobar".
+ */
+int match_fstype(const char *type, const char *pattern)
+{
+	int no = 0;		/* negated types list */
+	int len;
+	const char *p;
+
+	if (!pattern && !type)
+		return 1;
+	if (!pattern)
+		return 0;
+
+	if (!strncmp(pattern, "no", 2)) {
+		no = 1;
+		pattern += 2;
+	}
+
+	/* Does type occur in types, separated by commas? */
+	len = strlen(type);
+	p = pattern;
+	while(1) {
+		if (!strncmp(p, "no", 2) && !strncmp(p+2, type, len) &&
+		    (p[len+2] == 0 || p[len+2] == ','))
+			return 0;
+		if (strncmp(p, type, len) == 0 && (p[len] == 0 || p[len] == ','))
+			return !no;
+		p = strchr(p,',');
+		if (!p)
+			break;
+		p++;
+	}
+	return no;
+}
diff --git a/libblkid/lib/mbsalign.c b/libblkid/lib/mbsalign.c
new file mode 100644
index 0000000..5e52e8f
--- /dev/null
+++ b/libblkid/lib/mbsalign.c
@@ -0,0 +1,466 @@
+/* Align/Truncate a string in a given screen width
+   Copyright (C) 2009-2010 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as published by
+   the Free Software Foundation, either version 2.1 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* Written by Pádraig Brady.  */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <ctype.h>
+
+#include "c.h"
+#include "mbsalign.h"
+#include "widechar.h"
+
+#ifdef HAVE_WIDECHAR
+/* Replace non printable chars.
+   Note \t and \n etc. are non printable.
+   Return 1 if replacement made, 0 otherwise.  */
+
+/*
+ * Counts number of cells in multibyte string. For all control and
+ * non-printable chars is the result width enlarged to store \x?? hex
+ * sequence. See mbs_safe_encode().
+ *
+ * Returns: number of cells, @sz returns number of bytes.
+ */
+size_t mbs_safe_nwidth(const char *buf, size_t bufsz, size_t *sz)
+{
+	mbstate_t st;
+	const char *p = buf, *last = buf;
+	size_t width = 0, bytes = 0;
+
+	memset(&st, 0, sizeof(st));
+
+	if (p && *p && bufsz)
+		last = p + (bufsz - 1);
+
+	while (p && *p && p <= last) {
+		if (iscntrl((unsigned char) *p)) {
+			width += 4, bytes += 4;		/* *p encoded to \x?? */
+			p++;
+		}
+#ifdef HAVE_WIDECHAR
+		else {
+			wchar_t wc;
+			size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st);
+
+			if (len == 0)
+				break;
+
+			if (len == (size_t) -1 || len == (size_t) -2) {
+				len = 1;
+				if (isprint((unsigned char) *p))
+					width += 1, bytes += 1;
+				else
+					width += 4, bytes += 4;
+
+			} else if (!iswprint(wc)) {
+				width += len * 4;	/* hex encode whole sequence */
+				bytes += len * 4;
+			} else {
+				width += wcwidth(wc);	/* number of cells */
+				bytes += len;		/* number of bytes */
+			}
+			p += len;
+		}
+#else
+		else if (!isprint((unsigned char) *p)) {
+			width += 4, bytes += 4;		/* *p encoded to \x?? */
+			p++;
+		} else {
+			width++, bytes++;
+			p++;
+		}
+#endif
+	}
+
+	if (sz)
+		*sz = bytes;
+	return width;
+}
+
+size_t mbs_safe_width(const char *s)
+{
+	if (!s || !*s)
+		return 0;
+	return mbs_safe_nwidth(s, strlen(s), NULL);
+}
+
+/*
+ * Copy @s to @buf and replace control and non-printable chars with
+ * \x?? hex sequence. The @width returns number of cells.
+ *
+ * The @buf has to be big enough to store mbs_safe_encode_size(strlen(s)))
+ * bytes.
+ */
+char *mbs_safe_encode_to_buffer(const char *s, size_t *width, char *buf)
+{
+	mbstate_t st;
+	const char *p = s;
+	char *r;
+	size_t sz = s ? strlen(s) : 0;
+
+	if (!sz || !buf)
+		return NULL;
+
+	memset(&st, 0, sizeof(st));
+
+	r = buf;
+	*width = 0;
+
+	while (p && *p) {
+		if (iscntrl((unsigned char) *p)) {
+			sprintf(r, "\\x%02x", (unsigned char) *p);
+			r += 4;
+			*width += 4;
+			p++;
+		}
+#ifdef HAVE_WIDECHAR
+		else {
+			wchar_t wc;
+			size_t len = mbrtowc(&wc, p, MB_CUR_MAX, &st);
+
+			if (len == 0)
+				break;		/* end of string */
+
+			if (len == (size_t) -1 || len == (size_t) -2) {
+				len = 1;
+				/*
+				 * Not valid multibyte sequence -- maybe it's
+				 * printable char according to the current locales.
+				 */
+				if (!isprint((unsigned char) *p)) {
+					sprintf(r, "\\x%02x", (unsigned char) *p);
+					r += 4;
+					*width += 4;
+				} else {
+					width++;
+					*r++ = *p;
+				}
+			} else if (!iswprint(wc)) {
+				size_t i;
+				for (i = 0; i < len; i++) {
+					sprintf(r, "\\x%02x", (unsigned char) *p);
+					r += 4;
+					*width += 4;
+				}
+			} else {
+				memcpy(r, p, len);
+				r += len;
+				*width += wcwidth(wc);
+			}
+			p += len;
+		}
+#else
+		else if (!isprint((unsigned char) *p)) {
+			sprintf(r, "\\x%02x", (unsigned char) *p);
+			p++;
+			r += 4;
+			*width += 4;
+		} else {
+			*r++ = *p++;
+			*width++;
+		}
+#endif
+	}
+
+	*r = '\0';
+
+	return buf;
+}
+
+size_t mbs_safe_encode_size(size_t bytes)
+{
+	return (bytes * 4) + 1;
+}
+
+/*
+ * Returns allocated string where all control and non-printable chars are
+ * replaced with \x?? hex sequence.
+ */
+char *mbs_safe_encode(const char *s, size_t *width)
+{
+	size_t sz = s ? strlen(s) : 0;
+	char *buf;
+
+	if (!sz)
+		return NULL;
+	buf = malloc(mbs_safe_encode_size(sz));
+	if (!buf)
+		return NULL;
+
+	return mbs_safe_encode_to_buffer(s, width, buf);
+}
+
+static bool
+wc_ensure_printable (wchar_t *wchars)
+{
+  bool replaced = false;
+  wchar_t *wc = wchars;
+  while (*wc)
+    {
+      if (!iswprint ((wint_t) *wc))
+        {
+          *wc = 0xFFFD; /* L'\uFFFD' (replacement char) */
+          replaced = true;
+        }
+      wc++;
+    }
+  return replaced;
+}
+
+/* Truncate wchar string to width cells.
+ * Returns number of cells used.  */
+
+static size_t
+wc_truncate (wchar_t *wc, size_t width)
+{
+  size_t cells = 0;
+  int next_cells = 0;
+
+  while (*wc)
+    {
+      next_cells = wcwidth (*wc);
+      if (next_cells == -1) /* non printable */
+        {
+          *wc = 0xFFFD; /* L'\uFFFD' (replacement char) */
+          next_cells = 1;
+        }
+      if (cells + next_cells > width)
+        break;
+      cells += next_cells;
+      wc++;
+    }
+  *wc = L'\0';
+  return cells;
+}
+
+/* FIXME: move this function to gnulib as it's missing on:
+   OpenBSD 3.8, IRIX 5.3, Solaris 2.5.1, mingw, BeOS  */
+
+static int
+rpl_wcswidth (const wchar_t *s, size_t n)
+{
+  int ret = 0;
+
+  while (n-- > 0 && *s != L'\0')
+    {
+      int nwidth = wcwidth (*s++);
+      if (nwidth == -1)             /* non printable */
+        return -1;
+      if (ret > (INT_MAX - nwidth)) /* overflow */
+        return -1;
+      ret += nwidth;
+    }
+
+  return ret;
+}
+#endif
+
+/* Truncate multi-byte string to @width and returns number of
+ * bytes of the new string @str, and in @width returns number
+ * of cells.
+ */
+size_t
+mbs_truncate(char *str, size_t *width)
+{
+	ssize_t bytes = strlen(str);
+#ifdef HAVE_WIDECHAR
+	ssize_t sz = mbstowcs(NULL, str, 0);
+	wchar_t *wcs = NULL;
+
+	if (sz == (ssize_t) -1)
+		goto done;
+
+	wcs = malloc((sz + 1) * sizeof(wchar_t));
+	if (!wcs)
+		goto done;
+
+	if (!mbstowcs(wcs, str, sz))
+		goto done;
+	*width = wc_truncate(wcs, *width);
+	bytes = wcstombs(str, wcs, bytes);
+done:
+	free(wcs);
+#else
+	if (*width < bytes)
+		bytes = *width;
+#endif
+	if (bytes >= 0)
+		str[bytes] = '\0';
+	return bytes;
+}
+
+/* Write N_SPACES space characters to DEST while ensuring
+   nothing is written beyond DEST_END. A terminating NUL
+   is always added to DEST.
+   A pointer to the terminating NUL is returned.  */
+
+static char*
+mbs_align_pad (char *dest, const char* dest_end, size_t n_spaces)
+{
+  /* FIXME: Should we pad with "figure space" (\u2007)
+     if non ascii data present?  */
+  for (/* nothing */; n_spaces && (dest < dest_end); n_spaces--)
+    *dest++ = ' ';
+  *dest = '\0';
+  return dest;
+}
+
+/* Align a string, SRC, in a field of *WIDTH columns, handling multi-byte
+   characters; write the result into the DEST_SIZE-byte buffer, DEST.
+   ALIGNMENT specifies whether to left- or right-justify or to center.
+   If SRC requires more than *WIDTH columns, truncate it to fit.
+   When centering, the number of trailing spaces may be one less than the
+   number of leading spaces. The FLAGS parameter is unused at present.
+   Return the length in bytes required for the final result, not counting
+   the trailing NUL.  A return value of DEST_SIZE or larger means there
+   wasn't enough space.  DEST will be NUL terminated in any case.
+   Return (size_t) -1 upon error (invalid multi-byte sequence in SRC,
+   or malloc failure), unless MBA_UNIBYTE_FALLBACK is specified.
+   Update *WIDTH to indicate how many columns were used before padding.  */
+
+size_t
+mbsalign (const char *src, char *dest, size_t dest_size,
+          size_t *width, mbs_align_t align, int flags)
+{
+  size_t ret = -1;
+  size_t src_size = strlen (src) + 1;
+  char *newstr = NULL;
+  wchar_t *str_wc = NULL;
+  const char *str_to_print = src;
+  size_t n_cols = src_size - 1;
+  size_t n_used_bytes = n_cols; /* Not including NUL */
+  size_t n_spaces = 0, space_left;
+  bool conversion = false;
+  bool wc_enabled = false;
+
+#ifdef HAVE_WIDECHAR
+  /* In multi-byte locales convert to wide characters
+     to allow easy truncation. Also determine number
+     of screen columns used.  */
+  if (MB_CUR_MAX > 1)
+    {
+      size_t src_chars = mbstowcs (NULL, src, 0);
+      if (src_chars == (size_t) -1)
+        {
+          if (flags & MBA_UNIBYTE_FALLBACK)
+            goto mbsalign_unibyte;
+          else
+            goto mbsalign_cleanup;
+        }
+      src_chars += 1; /* make space for NUL */
+      str_wc = malloc (src_chars * sizeof (wchar_t));
+      if (str_wc == NULL)
+        {
+          if (flags & MBA_UNIBYTE_FALLBACK)
+            goto mbsalign_unibyte;
+          else
+            goto mbsalign_cleanup;
+        }
+      if (mbstowcs (str_wc, src, src_chars) != 0)
+        {
+          str_wc[src_chars - 1] = L'\0';
+          wc_enabled = true;
+          conversion = wc_ensure_printable (str_wc);
+          n_cols = rpl_wcswidth (str_wc, src_chars);
+        }
+    }
+
+  /* If we transformed or need to truncate the source string
+     then create a modified copy of it.  */
+  if (wc_enabled && (conversion || (n_cols > *width)))
+    {
+        if (conversion)
+          {
+             /* May have increased the size by converting
+                \t to \uFFFD for example.  */
+            src_size = wcstombs(NULL, str_wc, 0) + 1;
+          }
+        newstr = malloc (src_size);
+        if (newstr == NULL)
+        {
+          if (flags & MBA_UNIBYTE_FALLBACK)
+            goto mbsalign_unibyte;
+          else
+            goto mbsalign_cleanup;
+        }
+        str_to_print = newstr;
+        n_cols = wc_truncate (str_wc, *width);
+        n_used_bytes = wcstombs (newstr, str_wc, src_size);
+    }
+#endif
+
+mbsalign_unibyte:
+
+  if (n_cols > *width) /* Unibyte truncation required.  */
+    {
+      n_cols = *width;
+      n_used_bytes = n_cols;
+    }
+
+  if (*width > n_cols) /* Padding required.  */
+    n_spaces = *width - n_cols;
+
+  /* indicate to caller how many cells needed (not including padding).  */
+  *width = n_cols;
+
+  /* indicate to caller how many bytes needed (not including NUL).  */
+  ret = n_used_bytes + (n_spaces * 1);
+
+  /* Write as much NUL terminated output to DEST as possible.  */
+  if (dest_size != 0)
+    {
+      char *dest_end = dest + dest_size - 1;
+      size_t start_spaces;
+      size_t end_spaces;
+
+      switch (align)
+        {
+        case MBS_ALIGN_CENTER:
+          start_spaces = n_spaces / 2 + n_spaces % 2;
+          end_spaces = n_spaces / 2;
+          break;
+        case MBS_ALIGN_LEFT:
+          start_spaces = 0;
+          end_spaces = n_spaces;
+          break;
+        case MBS_ALIGN_RIGHT:
+          start_spaces = n_spaces;
+          end_spaces = 0;
+          break;
+	default:
+	  abort();
+        }
+
+      dest = mbs_align_pad (dest, dest_end, start_spaces);
+      space_left = dest_end - dest;
+      dest = memcpy (dest, str_to_print, min (n_used_bytes, space_left));
+      mbs_align_pad (dest, dest_end, end_spaces);
+    }
+
+mbsalign_cleanup:
+
+  free (str_wc);
+  free (newstr);
+
+  return ret;
+}
diff --git a/libblkid/lib/md5.c b/libblkid/lib/md5.c
new file mode 100644
index 0000000..488d16e
--- /dev/null
+++ b/libblkid/lib/md5.c
@@ -0,0 +1,257 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+#include <string.h>		/* for memcpy() */
+
+#include "md5.h"
+
+#if !defined(WORDS_BIGENDIAN)
+#define byteReverse(buf, len)	/* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+    uint32_t t;
+    do {
+	t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+	    ((unsigned) buf[1] << 8 | buf[0]);
+	*(uint32_t *) buf = t;
+	buf += 4;
+    } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    uint32_t t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+	ctx->bits[1]++;		/* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+	unsigned char *p = (unsigned char *) ctx->in + t;
+
+	t = 64 - t;
+	if (len < t) {
+	    memcpy(p, buf, len);
+	    return;
+	}
+	memcpy(p, buf, t);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+	buf += t;
+	len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+	memcpy(ctx->in, buf, 64);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+	buf += 64;
+	len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[MD5LENGTH], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+	/* Two lots of padding:  Pad the first block to 64 bytes */
+	memset(p, 0, count);
+	byteReverse(ctx->in, 16);
+	MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+	/* Now fill the next block with 56 bytes */
+	memset(ctx->in, 0, 56);
+    } else {
+	/* Pad block to 56 bytes */
+	memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform.
+     * Use memcpy to avoid aliasing problems.  On most systems,
+     * this will be optimized away to the same code.
+     */
+    memcpy(&ctx->in[14 * sizeof(uint32_t)], &ctx->bits[0], 4);
+    memcpy(&ctx->in[15 * sizeof(uint32_t)], &ctx->bits[1], 4);
+
+    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, MD5LENGTH);
+    memset(ctx, 0, sizeof(*ctx));	/* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+    register uint32_t a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+
+#endif
+
diff --git a/libblkid/lib/monotonic.c b/libblkid/lib/monotonic.c
new file mode 100644
index 0000000..3d4a443
--- /dev/null
+++ b/libblkid/lib/monotonic.c
@@ -0,0 +1,68 @@
+/*
+ * Please, don't add this file to libcommon because clock_gettime() requires
+ * -lrt on systems with old libc.
+ */
+#include <time.h>
+#include <sys/sysinfo.h>
+#include <sys/time.h>
+
+#include "c.h"
+#include "nls.h"
+#include "monotonic.h"
+
+int get_boot_time(struct timeval *boot_time)
+{
+#ifdef CLOCK_BOOTTIME
+	struct timespec hires_uptime;
+	struct timeval lores_uptime;
+#endif
+	struct timeval now;
+#ifdef HAVE_SYSINFO
+	struct sysinfo info;
+#endif
+
+	if (gettimeofday(&now, NULL) != 0) {
+		warn(_("gettimeofday failed"));
+		return -errno;
+	}
+#ifdef CLOCK_BOOTTIME
+	if (clock_gettime(CLOCK_BOOTTIME, &hires_uptime) == 0) {
+		TIMESPEC_TO_TIMEVAL(&lores_uptime, &hires_uptime);
+		timersub(&now, &lores_uptime, boot_time);
+		return 0;
+	}
+#endif
+#ifdef HAVE_SYSINFO
+	/* fallback */
+	if (sysinfo(&info) != 0)
+		warn(_("sysinfo failed"));
+
+	boot_time->tv_sec = now.tv_sec - info.uptime;
+	boot_time->tv_usec = 0;
+	return 0;
+#else
+	return -ENOSYS;
+#endif
+}
+
+int gettime_monotonic(struct timeval *tv)
+{
+#ifdef CLOCK_MONOTONIC
+	/* Can slew only by ntp and adjtime */
+	int ret;
+	struct timespec ts;
+
+# ifdef CLOCK_MONOTONIC_RAW
+	/* Linux specific, cant slew */
+	if (!(ret = clock_gettime(CLOCK_MONOTONIC_RAW, &ts))) {
+# else
+	if (!(ret = clock_gettime(CLOCK_MONOTONIC, &ts))) {
+# endif
+		tv->tv_sec = ts.tv_sec;
+		tv->tv_usec = ts.tv_nsec / 1000;
+	}
+	return ret;
+#else
+	return gettimeofday(tv, NULL);
+#endif
+}
diff --git a/libblkid/lib/pager.c b/libblkid/lib/pager.c
new file mode 100644
index 0000000..9e09cd5
--- /dev/null
+++ b/libblkid/lib/pager.c
@@ -0,0 +1,210 @@
+/*
+ * Based on linux-perf/git scm
+ *
+ * Some modifications and simplifications for util-linux
+ * by Davidlohr Bueso <dave@xxxxxxx> - March 2012.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "c.h"
+#include "xalloc.h"
+#include "nls.h"
+
+#define NULL_DEVICE	"/dev/null"
+
+void setup_pager(void);
+
+static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
+
+struct child_process {
+	const char **argv;
+	pid_t pid;
+	int in;
+	int out;
+	int err;
+	unsigned no_stdin:1;
+	void (*preexec_cb)(void);
+};
+static struct child_process pager_process;
+
+static inline void close_pair(int fd[2])
+{
+	close(fd[0]);
+	close(fd[1]);
+}
+
+static int start_command(struct child_process *cmd)
+{
+	int need_in;
+	int fdin[2];
+
+	/*
+	 * In case of errors we must keep the promise to close FDs
+	 * that have been passed in via ->in and ->out.
+	 */
+	need_in = !cmd->no_stdin && cmd->in < 0;
+	if (need_in) {
+		if (pipe(fdin) < 0) {
+			if (cmd->out > 0)
+				close(cmd->out);
+			return -1;
+		}
+		cmd->in = fdin[1];
+	}
+
+	fflush(NULL);
+	cmd->pid = fork();
+	if (!cmd->pid) {
+		if (need_in) {
+			dup2(fdin[0], STDIN_FILENO);
+			close_pair(fdin);
+		} else if (cmd->in > 0) {
+			dup2(cmd->in, STDIN_FILENO);
+			close(cmd->in);
+		}
+
+		cmd->preexec_cb();
+		execvp(cmd->argv[0], (char *const*) cmd->argv);
+		exit(127); /* cmd not found */
+	}
+
+	if (cmd->pid < 0) {
+		if (need_in)
+			close_pair(fdin);
+		else if (cmd->in)
+			close(cmd->in);
+		return -1;
+	}
+
+	if (need_in)
+		close(fdin[0]);
+	else if (cmd->in)
+		close(cmd->in);
+	return 0;
+}
+
+static int wait_or_whine(pid_t pid)
+{
+	for (;;) {
+		int status, code;
+		pid_t waiting = waitpid(pid, &status, 0);
+
+		if (waiting < 0) {
+			if (errno == EINTR)
+				continue;
+			err(EXIT_FAILURE, _("waitpid failed (%s)"), strerror(errno));
+		}
+		if (waiting != pid)
+			return -1;
+		if (WIFSIGNALED(status))
+			return -1;
+
+		if (!WIFEXITED(status))
+			return -1;
+		code = WEXITSTATUS(status);
+		switch (code) {
+		case 127:
+			return -1;
+		case 0:
+			return 0;
+		default:
+			return -1;
+		}
+	}
+}
+
+static int finish_command(struct child_process *cmd)
+{
+	return wait_or_whine(cmd->pid);
+}
+
+static void pager_preexec(void)
+{
+	/*
+	 * Work around bug in "less" by not starting it until we
+	 * have real input
+	 */
+	fd_set in;
+
+	FD_ZERO(&in);
+	FD_SET(STDIN_FILENO, &in);
+	select(1, &in, NULL, &in, NULL);
+
+	setenv("LESS", "FRSX", 0);
+}
+
+static void wait_for_pager(void)
+{
+	fflush(stdout);
+	fflush(stderr);
+	/* signal EOF to pager */
+	close(STDOUT_FILENO);
+	close(STDERR_FILENO);
+	finish_command(&pager_process);
+}
+
+static void wait_for_pager_signal(int signo)
+{
+	wait_for_pager();
+	raise(signo);
+}
+
+void setup_pager(void)
+{
+	const char *pager = getenv("PAGER");
+
+	if (!isatty(STDOUT_FILENO))
+		return;
+
+	if (!pager)
+		pager = "less";
+	else if (!*pager || !strcmp(pager, "cat"))
+		return;
+
+	/* spawn the pager */
+	pager_argv[2] = pager;
+	pager_process.argv = pager_argv;
+	pager_process.in = -1;
+	pager_process.preexec_cb = pager_preexec;
+
+	if (start_command(&pager_process))
+		return;
+
+	/* original process continues, but writes to the pipe */
+	dup2(pager_process.in, STDOUT_FILENO);
+	if (isatty(STDERR_FILENO))
+		dup2(pager_process.in, STDERR_FILENO);
+	close(pager_process.in);
+
+	/* this makes sure that the parent terminates after the pager */
+	signal(SIGINT, wait_for_pager_signal);
+	signal(SIGHUP, wait_for_pager_signal);
+	signal(SIGTERM, wait_for_pager_signal);
+	signal(SIGQUIT, wait_for_pager_signal);
+	signal(SIGPIPE, wait_for_pager_signal);
+
+	atexit(wait_for_pager);
+}
+
+#ifdef TEST_PROGRAM
+
+#define MAX 255
+
+int main(int argc __attribute__ ((__unused__)),
+	 char *argv[] __attribute__ ((__unused__)))
+{
+	int i;
+
+	setup_pager();
+	for (i = 0; i < MAX; i++)
+		printf("%d\n", i);
+	return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/lib/path.c b/libblkid/lib/path.c
new file mode 100644
index 0000000..fc90c0a
--- /dev/null
+++ b/libblkid/lib/path.c
@@ -0,0 +1,258 @@
+/*
+ * Simple functions to access files, paths maybe be globally prefixed by a
+ * global prefix to read data from alternative destination (e.g. /proc dump for
+ * regression tests).
+ *
+ * Taken from lscpu.c
+ *
+ * Copyright (C) 2008 Cai Qian <qcai@redhat.com>
+ * Copyright (C) 2008-2012 Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#include "all-io.h"
+#include "path.h"
+#include "nls.h"
+#include "c.h"
+
+static size_t prefixlen;
+static char pathbuf[PATH_MAX];
+
+static const char *
+path_vcreate(const char *path, va_list ap)
+{
+	if (prefixlen)
+		vsnprintf(pathbuf + prefixlen,
+			  sizeof(pathbuf) - prefixlen, path, ap);
+	else
+		vsnprintf(pathbuf, sizeof(pathbuf), path, ap);
+	return pathbuf;
+}
+
+char *
+path_strdup(const char *path, ...)
+{
+	const char *p;
+	va_list ap;
+
+	va_start(ap, path);
+	p = path_vcreate(path, ap);
+	va_end(ap);
+
+	return p ? strdup(p) : NULL;
+}
+
+static FILE *
+path_vfopen(const char *mode, int exit_on_error, const char *path, va_list ap)
+{
+	FILE *f;
+	const char *p = path_vcreate(path, ap);
+
+	f = fopen(p, mode);
+	if (!f && exit_on_error)
+		err(EXIT_FAILURE, _("cannot open %s"), p);
+	return f;
+}
+
+static int
+path_vopen(int flags, const char *path, va_list ap)
+{
+	int fd;
+	const char *p = path_vcreate(path, ap);
+
+	fd = open(p, flags);
+	if (fd == -1)
+		err(EXIT_FAILURE, _("cannot open %s"), p);
+	return fd;
+}
+
+FILE *
+path_fopen(const char *mode, int exit_on_error, const char *path, ...)
+{
+	FILE *fd;
+	va_list ap;
+
+	va_start(ap, path);
+	fd = path_vfopen(mode, exit_on_error, path, ap);
+	va_end(ap);
+
+	return fd;
+}
+
+void
+path_read_str(char *result, size_t len, const char *path, ...)
+{
+	FILE *fd;
+	va_list ap;
+
+	va_start(ap, path);
+	fd = path_vfopen("r", 1, path, ap);
+	va_end(ap);
+
+	if (!fgets(result, len, fd))
+		err(EXIT_FAILURE, _("cannot read %s"), pathbuf);
+	fclose(fd);
+
+	len = strlen(result);
+	if (result[len - 1] == '\n')
+		result[len - 1] = '\0';
+}
+
+int
+path_read_s32(const char *path, ...)
+{
+	FILE *fd;
+	va_list ap;
+	int result;
+
+	va_start(ap, path);
+	fd = path_vfopen("r", 1, path, ap);
+	va_end(ap);
+
+	if (fscanf(fd, "%d", &result) != 1) {
+		if (ferror(fd))
+			err(EXIT_FAILURE, _("cannot read %s"), pathbuf);
+		else
+			errx(EXIT_FAILURE, _("parse error: %s"), pathbuf);
+	}
+	fclose(fd);
+	return result;
+}
+
+uint64_t
+path_read_u64(const char *path, ...)
+{
+	FILE *fd;
+	va_list ap;
+	uint64_t result;
+
+	va_start(ap, path);
+	fd = path_vfopen("r", 1, path, ap);
+	va_end(ap);
+
+	if (fscanf(fd, "%"SCNu64, &result) != 1) {
+		if (ferror(fd))
+			err(EXIT_FAILURE, _("cannot read %s"), pathbuf);
+		else
+			errx(EXIT_FAILURE, _("parse error: %s"), pathbuf);
+	}
+	fclose(fd);
+	return result;
+}
+
+int
+path_write_str(const char *str, const char *path, ...)
+{
+	int fd, result;
+	va_list ap;
+
+	va_start(ap, path);
+	fd = path_vopen(O_WRONLY|O_CLOEXEC, path, ap);
+	va_end(ap);
+	result = write_all(fd, str, strlen(str));
+	close(fd);
+	return result;
+}
+
+int
+path_exist(const char *path, ...)
+{
+	va_list ap;
+	const char *p;
+
+	va_start(ap, path);
+	p = path_vcreate(path, ap);
+	va_end(ap);
+
+	return access(p, F_OK) == 0;
+}
+
+#ifdef HAVE_CPU_SET_T
+
+static cpu_set_t *
+path_cpuparse(int maxcpus, int islist, const char *path, va_list ap)
+{
+	FILE *fd;
+	cpu_set_t *set;
+	size_t setsize, len = maxcpus * 7;
+	char buf[len];
+
+	fd = path_vfopen("r", 1, path, ap);
+
+	if (!fgets(buf, len, fd))
+		err(EXIT_FAILURE, _("cannot read %s"), pathbuf);
+	fclose(fd);
+
+	len = strlen(buf);
+	if (buf[len - 1] == '\n')
+		buf[len - 1] = '\0';
+
+	set = cpuset_alloc(maxcpus, &setsize, NULL);
+	if (!set)
+		err(EXIT_FAILURE, _("failed to callocate cpu set"));
+
+	if (islist) {
+		if (cpulist_parse(buf, set, setsize, 0))
+			errx(EXIT_FAILURE, _("failed to parse CPU list %s"), buf);
+	} else {
+		if (cpumask_parse(buf, set, setsize))
+			errx(EXIT_FAILURE, _("failed to parse CPU mask %s"), buf);
+	}
+	return set;
+}
+
+cpu_set_t *
+path_read_cpuset(int maxcpus, const char *path, ...)
+{
+	va_list ap;
+	cpu_set_t *set;
+
+	va_start(ap, path);
+	set = path_cpuparse(maxcpus, 0, path, ap);
+	va_end(ap);
+
+	return set;
+}
+
+cpu_set_t *
+path_read_cpulist(int maxcpus, const char *path, ...)
+{
+	va_list ap;
+	cpu_set_t *set;
+
+	va_start(ap, path);
+	set = path_cpuparse(maxcpus, 1, path, ap);
+	va_end(ap);
+
+	return set;
+}
+
+#endif /* HAVE_CPU_SET_T */
+
+void
+path_set_prefix(const char *prefix)
+{
+	prefixlen = strlen(prefix);
+	strncpy(pathbuf, prefix, sizeof(pathbuf));
+	pathbuf[sizeof(pathbuf) - 1] = '\0';
+}
diff --git a/libblkid/lib/procutils.c b/libblkid/lib/procutils.c
new file mode 100644
index 0000000..ef96941
--- /dev/null
+++ b/libblkid/lib/procutils.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2011 Davidlohr Bueso <dave@gnu.org>
+ *
+ * procutils.c: General purpose procfs parsing utilities
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include "procutils.h"
+#include "at.h"
+#include "c.h"
+
+/*
+ * @pid: process ID for which we want to obtain the threads group
+ *
+ * Returns: newly allocated tasks structure
+ */
+struct proc_tasks *proc_open_tasks(pid_t pid)
+{
+	struct proc_tasks *tasks;
+	char path[PATH_MAX];
+
+	sprintf(path, "/proc/%d/task/", pid);
+
+	tasks = malloc(sizeof(struct proc_tasks));
+	if (tasks) {
+		tasks->dir = opendir(path);
+		if (tasks->dir)
+			return tasks;
+	}
+
+	free(tasks);
+	return NULL;
+}
+
+/*
+ * @tasks: allocated tasks structure
+ *
+ * Returns: nothing
+ */
+void proc_close_tasks(struct proc_tasks *tasks)
+{
+	if (tasks && tasks->dir)
+		closedir(tasks->dir);
+	free(tasks);
+}
+
+/*
+ * @tasks: allocated task structure
+ * @tid: [output] one of the thread IDs belonging to the thread group
+ *        If when an error occurs, it is set to 0.
+ *
+ * Returns: 0 on success, 1 on end, -1 on failure or no more threads
+ */
+int proc_next_tid(struct proc_tasks *tasks, pid_t *tid)
+{
+	struct dirent *d;
+	char *end;
+
+	if (!tasks || !tid)
+		return -EINVAL;
+
+	*tid = 0;
+	errno = 0;
+
+	do {
+		d = readdir(tasks->dir);
+		if (!d)
+			return errno ? -1 : 1;		/* error or end-of-dir */
+
+		if (!isdigit((unsigned char) *d->d_name))
+			continue;
+		errno = 0;
+		*tid = (pid_t) strtol(d->d_name, &end, 10);
+		if (errno || d->d_name == end || (end && *end))
+			return -1;
+
+	} while (!*tid);
+
+	return 0;
+}
+
+struct proc_processes *proc_open_processes(void)
+{
+	struct proc_processes *ps;
+
+	ps = calloc(1, sizeof(struct proc_processes));
+	if (ps) {
+		ps->dir = opendir("/proc");
+		if (ps->dir)
+			return ps;
+	}
+
+	free(ps);
+	return NULL;
+}
+
+void proc_close_processes(struct proc_processes *ps)
+{
+	if (ps && ps->dir)
+		closedir(ps->dir);
+	free(ps);
+}
+
+void proc_processes_filter_by_name(struct proc_processes *ps, const char *name)
+{
+	ps->fltr_name = name;
+	ps->has_fltr_name = name ? 1 : 0;
+}
+
+void proc_processes_filter_by_uid(struct proc_processes *ps, uid_t uid)
+{
+	ps->fltr_uid = uid;
+	ps->has_fltr_uid = 1;
+}
+
+int proc_next_pid(struct proc_processes *ps, pid_t *pid)
+{
+	struct dirent *d;
+
+	if (!ps || !pid)
+		return -EINVAL;
+
+	*pid = 0;
+	errno = 0;
+
+	do {
+		char buf[BUFSIZ], *p;
+
+		d = readdir(ps->dir);
+		if (!d)
+			return errno ? -1 : 1;		/* error or end-of-dir */
+
+
+		if (!isdigit((unsigned char) *d->d_name))
+			continue;
+
+		/* filter out by UID */
+		if (ps->has_fltr_uid) {
+			struct stat st;
+
+			if (fstat_at(dirfd(ps->dir), "/proc", d->d_name, &st, 0))
+				continue;
+			if (ps->fltr_uid != st.st_uid)
+				continue;
+		}
+
+		/* filter out by NAME */
+		if (ps->has_fltr_name) {
+			char procname[256];
+			FILE *f;
+
+			snprintf(buf, sizeof(buf), "%s/stat", d->d_name);
+			f = fopen_at(dirfd(ps->dir), "/proc", buf,
+						O_CLOEXEC|O_RDONLY, "r");
+			if (!f)
+				continue;
+
+			p = fgets(buf, sizeof(buf), f);
+			fclose(f);
+			if (!p)
+				continue;
+
+			if (sscanf(buf, "%*d (%255[^)])", procname) != 1)
+				continue;
+
+			/* ok, we got the process name. */
+			if (strcmp(procname, ps->fltr_name) != 0)
+				continue;
+		}
+
+		p = NULL;
+		errno = 0;
+		*pid = (pid_t) strtol(d->d_name, &p, 10);
+		if (errno || d->d_name == p || (p && *p))
+			return errno ? -errno : -1;
+
+		return 0;
+	} while (1);
+
+	return 0;
+}
+
+#ifdef TEST_PROGRAM
+
+static int test_tasks(int argc, char *argv[])
+{
+	pid_t tid, pid;
+	struct proc_tasks *ts;
+
+	if (argc != 2)
+		return EXIT_FAILURE;
+
+	pid = strtol(argv[1], (char **) NULL, 10);
+	printf("PID=%d, TIDs:", pid);
+
+	ts = proc_open_tasks(pid);
+	if (!ts)
+		err(EXIT_FAILURE, "open list of tasks failed");
+
+	while (proc_next_tid(ts, &tid) == 0)
+		printf(" %d", tid);
+
+	printf("\n");
+        proc_close_tasks(ts);
+	return EXIT_SUCCESS;
+}
+
+static int test_processes(int argc, char *argv[])
+{
+	pid_t pid;
+	struct proc_processes *ps;
+
+	ps = proc_open_processes();
+	if (!ps)
+		err(EXIT_FAILURE, "open list of processes failed");
+
+	if (argc >= 3 && strcmp(argv[1], "--name") == 0)
+		proc_processes_filter_by_name(ps, argv[2]);
+
+	if (argc >= 3 && strcmp(argv[1], "--uid") == 0)
+		proc_processes_filter_by_uid(ps, (uid_t) atol(argv[2]));
+
+	while (proc_next_pid(ps, &pid) == 0)
+		printf(" %d", pid);
+
+	printf("\n");
+        proc_close_processes(ps);
+	return EXIT_SUCCESS;
+}
+
+int main(int argc, char *argv[])
+{
+	if (argc < 2) {
+		fprintf(stderr, "usage: %1$s --tasks <pid>\n"
+				"       %1$s --processes [---name <name>] [--uid <uid>]\n",
+				program_invocation_short_name);
+		return EXIT_FAILURE;
+	}
+
+	if (strcmp(argv[1], "--tasks") == 0)
+		return test_tasks(argc - 1, argv + 1);
+	if (strcmp(argv[1], "--processes") == 0)
+		return test_processes(argc - 1, argv + 1);
+
+	return EXIT_FAILURE;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/lib/randutils.c b/libblkid/lib/randutils.c
new file mode 100644
index 0000000..684ac0a
--- /dev/null
+++ b/libblkid/lib/randutils.c
@@ -0,0 +1,147 @@
+/*
+ * General purpose random utilities
+ *
+ * Based on libuuid code.
+ *
+ * This file may be redistributed under the terms of the
+ * GNU Lesser General Public License.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <sys/syscall.h>
+
+#include "c.h"
+#include "randutils.h"
+#include "nls.h"
+
+#ifdef HAVE_TLS
+#define THREAD_LOCAL static __thread
+#else
+#define THREAD_LOCAL static
+#endif
+
+#if defined(__linux__) && defined(__NR_gettid) && defined(HAVE_JRAND48)
+#define DO_JRAND_MIX
+THREAD_LOCAL unsigned short ul_jrand_seed[3];
+#endif
+
+int random_get_fd(void)
+{
+	int i, fd;
+	struct timeval	tv;
+
+	gettimeofday(&tv, 0);
+	fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
+	if (fd == -1)
+		fd = open("/dev/random", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
+	if (fd >= 0) {
+		i = fcntl(fd, F_GETFD);
+		if (i >= 0)
+			fcntl(fd, F_SETFD, i | FD_CLOEXEC);
+	}
+	srand((getpid() << 16) ^ getuid() ^ tv.tv_sec ^ tv.tv_usec);
+
+#ifdef DO_JRAND_MIX
+	ul_jrand_seed[0] = getpid() ^ (tv.tv_sec & 0xFFFF);
+	ul_jrand_seed[1] = getppid() ^ (tv.tv_usec & 0xFFFF);
+	ul_jrand_seed[2] = (tv.tv_sec ^ tv.tv_usec) >> 16;
+#endif
+	/* Crank the random number generator a few times */
+	gettimeofday(&tv, 0);
+	for (i = (tv.tv_sec ^ tv.tv_usec) & 0x1F; i > 0; i--)
+		rand();
+	return fd;
+}
+
+
+/*
+ * Generate a stream of random nbytes into buf.
+ * Use /dev/urandom if possible, and if not,
+ * use glibc pseudo-random functions.
+ */
+void random_get_bytes(void *buf, size_t nbytes)
+{
+	size_t i, n = nbytes;
+	int fd = random_get_fd();
+	int lose_counter = 0;
+	unsigned char *cp = (unsigned char *) buf;
+
+	if (fd >= 0) {
+		while (n > 0) {
+			ssize_t x = read(fd, cp, n);
+			if (x <= 0) {
+				if (lose_counter++ > 16)
+					break;
+				continue;
+			}
+			n -= x;
+			cp += x;
+			lose_counter = 0;
+		}
+
+		close(fd);
+	}
+
+	/*
+	 * We do this all the time, but this is the only source of
+	 * randomness if /dev/random/urandom is out to lunch.
+	 */
+	for (cp = buf, i = 0; i < nbytes; i++)
+		*cp++ ^= (rand() >> 7) & 0xFF;
+
+#ifdef DO_JRAND_MIX
+	{
+		unsigned short tmp_seed[3];
+
+		memcpy(tmp_seed, ul_jrand_seed, sizeof(tmp_seed));
+		ul_jrand_seed[2] = ul_jrand_seed[2] ^ syscall(__NR_gettid);
+		for (cp = buf, i = 0; i < nbytes; i++)
+			*cp++ ^= (jrand48(tmp_seed) >> 7) & 0xFF;
+		memcpy(ul_jrand_seed, tmp_seed,
+		       sizeof(ul_jrand_seed)-sizeof(unsigned short));
+	}
+#endif
+
+	return;
+}
+
+
+/*
+ * Tell source of randomness.
+ */
+const char *random_tell_source(void)
+{
+	size_t i;
+	static const char *random_sources[] = {
+		"/dev/urandom",
+		"/dev/random"
+	};
+
+	for (i = 0; i < ARRAY_SIZE(random_sources); i++) {
+		if (!access(random_sources[i], R_OK))
+			return random_sources[i];
+	}
+
+	return _("libc pseudo-random functions");
+}
+
+#ifdef TEST_PROGRAM
+int main(int argc __attribute__ ((__unused__)),
+         char *argv[] __attribute__ ((__unused__)))
+{
+	unsigned int v, i;
+
+	/* generate and print 10 random numbers */
+	for (i = 0; i < 10; i++) {
+		random_get_bytes(&v, sizeof(v));
+		printf("%d\n", v);
+	}
+
+	return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/lib/readutmp.c b/libblkid/lib/readutmp.c
new file mode 100644
index 0000000..b11e9a4
--- /dev/null
+++ b/libblkid/lib/readutmp.c
@@ -0,0 +1,78 @@
+/* GNU's read utmp module.
+
+	 Copyright (C) 1992-2001, 2003-2006, 2009-2014 Free Software Foundation, Inc.
+
+	 This program is free software: you can redistribute it and/or modify
+	 it under the terms of the GNU General Public License as published by
+	 the Free Software Foundation; either version 3 of the License, or
+	 (at your option) any later version.
+
+	 This program is distributed in the hope that it will be useful,
+	 but WITHOUT ANY WARRANTY; without even the implied warranty of
+	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+	 GNU General Public License for more details.
+
+	 You should have received a copy of the GNU General Public License
+	 along with this program.	If not, see <http://www.gnu.org/licenses/>.	*/
+
+/* Written by jla; revised by djm */
+/* extracted for util-linux by ooprala */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "xalloc.h"
+#include "readutmp.h"
+
+/* Read the utmp entries corresponding to file FILE into freshly-
+	 malloc'd storage, set *UTMP_BUF to that pointer, set *N_ENTRIES to
+	 the number of entries, and return zero.	If there is any error,
+	 return -1, setting errno, and don't modify the parameters.
+	 If OPTIONS & READ_UTMP_CHECK_PIDS is nonzero, omit entries whose
+	 process-IDs do not currently exist.	*/
+int
+read_utmp (char const *file, size_t *n_entries, struct utmp **utmp_buf)
+{
+	size_t n_read = 0;
+	size_t n_alloc = 0;
+	struct utmp *utmp = NULL;
+	struct utmp *u;
+
+	/* Ignore the return value for now.
+		 Solaris' utmpname returns 1 upon success -- which is contrary
+		 to what the GNU libc version does.	In addition, older GNU libc
+		 versions are actually void.	 */
+	utmpname(file);
+
+	setutent();
+
+	errno = 0;
+	while ((u = getutent()) != NULL) {
+		if (n_read == n_alloc) {
+			n_alloc += 32;
+			utmp = xrealloc(utmp, n_alloc * sizeof (struct utmp));
+			if (!utmp)
+				return -1;
+		}
+		utmp[n_read++] = *u;
+	}
+	if (!u && errno) {
+		free(utmp);
+		return -1;
+	}
+
+	endutent();
+
+	*n_entries = n_read;
+	*utmp_buf = utmp;
+
+	return 0;
+}
diff --git a/libblkid/lib/setproctitle.c b/libblkid/lib/setproctitle.c
new file mode 100644
index 0000000..4bcf8c8
--- /dev/null
+++ b/libblkid/lib/setproctitle.c
@@ -0,0 +1,74 @@
+/*
+ *  set process title for ps (from sendmail)
+ *
+ *  Clobbers argv of our main procedure so ps(1) will display the title.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "setproctitle.h"
+
+#ifndef SPT_BUFSIZE
+# define SPT_BUFSIZE     2048
+#endif
+
+extern char **environ;
+
+static char **argv0;
+static int argv_lth;
+
+void initproctitle (int argc, char **argv)
+{
+	int i;
+	char **envp = environ;
+
+	/*
+	 * Move the environment so we can reuse the memory.
+	 * (Code borrowed from sendmail.)
+	 * WARNING: ugly assumptions on memory layout here;
+	 *          if this ever causes problems, #undef DO_PS_FIDDLING
+	 */
+	for (i = 0; envp[i] != NULL; i++)
+		continue;
+
+	environ = (char **) malloc(sizeof(char *) * (i + 1));
+	if (environ == NULL)
+		return;
+
+	for (i = 0; envp[i] != NULL; i++)
+		if ((environ[i] = strdup(envp[i])) == NULL)
+			return;
+	environ[i] = NULL;
+
+	argv0 = argv;
+	if (i > 0)
+		argv_lth = envp[i-1] + strlen(envp[i-1]) - argv0[0];
+	else
+		argv_lth = argv0[argc-1] + strlen(argv0[argc-1]) - argv0[0];
+}
+
+void setproctitle (const char *prog, const char *txt)
+{
+        int i;
+        char buf[SPT_BUFSIZE];
+
+        if (!argv0)
+                return;
+
+	if (strlen(prog) + strlen(txt) + 5 > SPT_BUFSIZE)
+		return;
+
+	sprintf(buf, "%s -- %s", prog, txt);
+
+        i = strlen(buf);
+        if (i > argv_lth - 2) {
+                i = argv_lth - 2;
+                buf[i] = '\0';
+        }
+	memset(argv0[0], '\0', argv_lth);       /* clear the memory area */
+        strcpy(argv0[0], buf);
+
+        argv0[1] = NULL;
+}
diff --git a/libblkid/lib/strutils.c b/libblkid/lib/strutils.c
new file mode 100644
index 0000000..9fe9481
--- /dev/null
+++ b/libblkid/lib/strutils.c
@@ -0,0 +1,763 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <assert.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "bitops.h"
+
+static int do_scale_by_power (uintmax_t *x, int base, int power)
+{
+	while (power--) {
+		if (UINTMAX_MAX / base < *x)
+			return -ERANGE;
+		*x *= base;
+	}
+	return 0;
+}
+
+/*
+ * strtosize() - convert string to size (uintmax_t).
+ *
+ * Supported suffixes:
+ *
+ * XiB or X for 2^N
+ *     where X = {K,M,G,T,P,E,Z,Y}
+ *        or X = {k,m,g,t,p,e}  (undocumented for backward compatibility only)
+ * for example:
+ *		10KiB	= 10240
+ *		10K	= 10240
+ *
+ * XB for 10^N
+ *     where X = {K,M,G,T,P,E,Z,Y}
+ * for example:
+ *		10KB	= 10000
+ *
+ * The optinal 'power' variable returns number associated with used suffix
+ * {K,M,G,T,P,E,Z,Y}  = {1,2,3,4,5,6,7,8}.
+ *
+ * The function also supports decimal point, for example:
+ *              0.5MB   = 500000
+ *              0.5MiB  = 512000
+ *
+ * Note that the function does not accept numbers with '-' (negative sign)
+ * prefix.
+ */
+int parse_size(const char *str, uintmax_t *res, int *power)
+{
+	char *p;
+	uintmax_t x, frac = 0;
+	int base = 1024, rc = 0, pwr = 0, frac_zeros = 0;
+
+	static const char *suf  = "KMGTPEYZ";
+	static const char *suf2 = "kmgtpeyz";
+	const char *sp;
+
+	*res = 0;
+
+	if (!str || !*str) {
+		rc = -EINVAL;
+		goto err;
+	}
+
+	/* Only positive numbers are acceptable
+	 *
+	 * Note that this check is not perfect, it would be better to
+	 * use lconv->negative_sign. But coreutils use the same solution,
+	 * so it's probably good enough...
+	 */
+	p = (char *) str;
+	while (isspace((unsigned char) *p))
+		p++;
+	if (*p == '-') {
+		rc = -EINVAL;
+		goto err;
+	}
+	p = NULL;
+
+	errno = 0;
+	x = strtoumax(str, &p, 0);
+
+	if (p == str ||
+	    (errno != 0 && (x == UINTMAX_MAX || x == 0))) {
+		rc = errno ? -errno : -1;
+		goto err;
+	}
+	if (!p || !*p)
+		goto done;			/* without suffix */
+
+	/*
+	 * Check size suffixes
+	 */
+check_suffix:
+	if (*(p + 1) == 'i' && *(p + 2) == 'B' && !*(p + 3))
+		base = 1024;			/* XiB, 2^N */
+	else if (*(p + 1) == 'B' && !*(p + 2))
+		base = 1000;			/* XB, 10^N */
+	else if (*(p + 1)) {
+		struct lconv const *l = localeconv();
+		char *dp = l ? l->decimal_point : NULL;
+		size_t dpsz = dp ? strlen(dp) : 0;
+
+		if (frac == 0 && *p && dp && strncmp(dp, p, dpsz) == 0) {
+			char *fstr = p + dpsz;
+
+			for (p = fstr; *p && *p == '0'; p++)
+				frac_zeros++;
+			errno = 0, p = NULL;
+			frac = strtoumax(fstr, &p, 0);
+			if (p == fstr ||
+			    (errno != 0 && (frac == UINTMAX_MAX || frac == 0))) {
+				rc = errno ? -errno : -1;
+				goto err;
+			}
+			if (frac && (!p  || !*p)) {
+				rc = -EINVAL;
+				goto err;		/* without suffix, but with frac */
+			}
+			goto check_suffix;
+		}
+		rc = -EINVAL;
+		goto err;			/* unexpected suffix */
+	}
+
+	sp = strchr(suf, *p);
+	if (sp)
+		pwr = (sp - suf) + 1;
+	else {
+		sp = strchr(suf2, *p);
+		if (sp)
+			pwr = (sp - suf2) + 1;
+		else {
+			rc = -EINVAL;
+			goto err;
+		}
+	}
+
+	rc = do_scale_by_power(&x, base, pwr);
+	if (power)
+		*power = pwr;
+	if (frac && pwr) {
+		int zeros_in_pwr = frac_zeros % 3;
+		int frac_pwr = pwr - (frac_zeros / 3) - 1;
+		uintmax_t y = frac * (zeros_in_pwr == 0 ? 100 :
+				      zeros_in_pwr == 1 ?  10 : 1);
+
+		if (frac_pwr < 0) {
+			rc = -EINVAL;
+			goto err;
+		}
+		do_scale_by_power(&y, base, frac_pwr);
+		x += y;
+	}
+done:
+	*res = x;
+err:
+	return rc;
+}
+
+int strtosize(const char *str, uintmax_t *res)
+{
+	return parse_size(str, res, NULL);
+}
+
+int isdigit_string(const char *str)
+{
+	const char *p;
+
+	for (p = str; p && *p && isdigit((unsigned char) *p); p++);
+
+	return p && p > str && !*p;
+}
+
+
+#ifndef HAVE_MEMPCPY
+void *mempcpy(void *restrict dest, const void *restrict src, size_t n)
+{
+    return ((char *)memcpy(dest, src, n)) + n;
+}
+#endif
+
+#ifndef HAVE_STRNLEN
+size_t strnlen(const char *s, size_t maxlen)
+{
+        int i;
+
+        for (i = 0; i < maxlen; i++) {
+                if (s[i] == '\0')
+                        return i + 1;
+        }
+        return maxlen;
+}
+#endif
+
+#ifndef HAVE_STRNCHR
+char *strnchr(const char *s, size_t maxlen, int c)
+{
+	for (; maxlen-- && *s != '\0'; ++s)
+		if (*s == (char)c)
+			return (char *)s;
+	return NULL;
+}
+#endif
+
+#ifndef HAVE_STRNDUP
+char *strndup(const char *s, size_t n)
+{
+	size_t len = strnlen(s, n);
+	char *new = (char *) malloc((len + 1) * sizeof(char));
+	if (!new)
+		return NULL;
+	new[len] = '\0';
+	return (char *) memcpy(new, s, len);
+}
+#endif
+
+int16_t strtos16_or_err(const char *str, const char *errmesg)
+{
+	int32_t num = strtos32_or_err(str, errmesg);
+
+	if (num < INT16_MIN || num > INT16_MAX)
+		errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	return num;
+}
+
+uint16_t strtou16_or_err(const char *str, const char *errmesg)
+{
+	uint32_t num = strtou32_or_err(str, errmesg);
+
+	if (num > UINT16_MAX)
+		errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	return num;
+}
+
+int32_t strtos32_or_err(const char *str, const char *errmesg)
+{
+	int64_t num = strtos64_or_err(str, errmesg);
+
+	if (num < INT32_MIN || num > INT32_MAX)
+		errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	return num;
+}
+
+uint32_t strtou32_or_err(const char *str, const char *errmesg)
+{
+	uint64_t num = strtou64_or_err(str, errmesg);
+
+	if (num > UINT32_MAX)
+		errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	return num;
+}
+
+int64_t strtos64_or_err(const char *str, const char *errmesg)
+{
+	int64_t num;
+	char *end = NULL;
+
+	if (str == NULL || *str == '\0')
+		goto err;
+	errno = 0;
+	num = strtoimax(str, &end, 10);
+
+	if (errno || str == end || (end && *end))
+		goto err;
+
+	return num;
+err:
+	if (errno)
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+uint64_t strtou64_or_err(const char *str, const char *errmesg)
+{
+	uintmax_t num;
+	char *end = NULL;
+
+	if (str == NULL || *str == '\0')
+		goto err;
+	errno = 0;
+	num = strtoumax(str, &end, 10);
+
+	if (errno || str == end || (end && *end))
+		goto err;
+
+	return num;
+err:
+	if (errno)
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+
+double strtod_or_err(const char *str, const char *errmesg)
+{
+	double num;
+	char *end = NULL;
+
+	if (str == NULL || *str == '\0')
+		goto err;
+	errno = 0;
+	num = strtod(str, &end);
+
+	if (errno || str == end || (end && *end))
+		goto err;
+
+	return num;
+err:
+	if (errno)
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+long strtol_or_err(const char *str, const char *errmesg)
+{
+	long num;
+	char *end = NULL;
+
+	if (str == NULL || *str == '\0')
+		goto err;
+	errno = 0;
+	num = strtol(str, &end, 10);
+
+	if (errno || str == end || (end && *end))
+		goto err;
+
+	return num;
+err:
+	if (errno)
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+unsigned long strtoul_or_err(const char *str, const char *errmesg)
+{
+	unsigned long num;
+	char *end = NULL;
+
+	if (str == NULL || *str == '\0')
+		goto err;
+	errno = 0;
+	num = strtoul(str, &end, 10);
+
+	if (errno || str == end || (end && *end))
+		goto err;
+
+	return num;
+err:
+	if (errno)
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+uintmax_t strtosize_or_err(const char *str, const char *errmesg)
+{
+	uintmax_t num;
+
+	if (strtosize(str, &num) == 0)
+		return num;
+
+	if (errno)
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+}
+
+
+void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmesg)
+{
+	double user_input;
+
+	user_input = strtod_or_err(str, errmesg);
+	tv->tv_sec = (time_t) user_input;
+	tv->tv_usec = (long)((user_input - tv->tv_sec) * 1000000);
+}
+
+/*
+ * Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
+ * be 11 bytes.
+ */
+void strmode(mode_t mode, char *str)
+{
+	if (S_ISDIR(mode))
+		str[0] = 'd';
+	else if (S_ISLNK(mode))
+		str[0] = 'l';
+	else if (S_ISCHR(mode))
+		str[0] = 'c';
+	else if (S_ISBLK(mode))
+		str[0] = 'b';
+	else if (S_ISSOCK(mode))
+		str[0] = 's';
+	else if (S_ISFIFO(mode))
+		str[0] = 'p';
+	else if (S_ISREG(mode))
+		str[0] = '-';
+
+	str[1] = mode & S_IRUSR ? 'r' : '-';
+	str[2] = mode & S_IWUSR ? 'w' : '-';
+	str[3] = (mode & S_ISUID
+		? (mode & S_IXUSR ? 's' : 'S')
+		: (mode & S_IXUSR ? 'x' : '-'));
+	str[4] = mode & S_IRGRP ? 'r' : '-';
+	str[5] = mode & S_IWGRP ? 'w' : '-';
+	str[6] = (mode & S_ISGID
+		? (mode & S_IXGRP ? 's' : 'S')
+		: (mode & S_IXGRP ? 'x' : '-'));
+	str[7] = mode & S_IROTH ? 'r' : '-';
+	str[8] = mode & S_IWOTH ? 'w' : '-';
+	str[9] = (mode & S_ISVTX
+		? (mode & S_IXOTH ? 't' : 'T')
+		: (mode & S_IXOTH ? 'x' : '-'));
+	str[10] = '\0';
+}
+
+/*
+ * returns exponent (2^x=n) in range KiB..PiB
+ */
+static int get_exp(uint64_t n)
+{
+	int shft;
+
+	for (shft = 10; shft <= 60; shft += 10) {
+		if (n < (1ULL << shft))
+			break;
+	}
+	return shft - 10;
+}
+
+char *size_to_human_string(int options, uint64_t bytes)
+{
+	char buf[32];
+	int dec, exp;
+	uint64_t frac;
+	const char *letters = "BKMGTPE";
+	char suffix[sizeof(" KiB")], *psuf = suffix;
+	char c;
+
+	if (options & SIZE_SUFFIX_SPACE)
+		*psuf++ = ' ';
+
+	exp  = get_exp(bytes);
+	c    = *(letters + (exp ? exp / 10 : 0));
+	dec  = exp ? bytes / (1ULL << exp) : bytes;
+	frac = exp ? bytes % (1ULL << exp) : 0;
+
+	*psuf++ = c;
+
+	if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) {
+		*psuf++ = 'i';
+		*psuf++ = 'B';
+	}
+
+	*psuf = '\0';
+
+	/* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
+	 *                 exp, suffix[0], dec, frac);
+	 */
+
+	if (frac) {
+		/* round */
+		frac = (frac / (1ULL << (exp - 10)) + 50) / 100;
+		if (frac == 10)
+			dec++, frac = 0;
+	}
+
+	if (frac) {
+		struct lconv const *l = localeconv();
+		char *dp = l ? l->decimal_point : NULL;
+
+		if (!dp || !*dp)
+			dp = ".";
+		snprintf(buf, sizeof(buf), "%d%s%jd%s", dec, dp, frac, suffix);
+	} else
+		snprintf(buf, sizeof(buf), "%d%s", dec, suffix);
+
+	return strdup(buf);
+}
+
+/*
+ * Parses comma delimited list to array with IDs, for example:
+ *
+ * "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
+ *                   ary[1] = FOO_BBB;
+ *                   ary[3] = FOO_CCC;
+ *
+ * The function name2id() provides conversion from string to ID.
+ *
+ * Returns: >= 0  : number of items added to ary[]
+ *            -1  : parse error or unknown item
+ *            -2  : arysz reached
+ */
+int string_to_idarray(const char *list, int ary[], size_t arysz,
+			int (name2id)(const char *, size_t))
+{
+	const char *begin = NULL, *p;
+	size_t n = 0;
+
+	if (!list || !*list || !ary || !arysz || !name2id)
+		return -1;
+
+	for (p = list; p && *p; p++) {
+		const char *end = NULL;
+		int id;
+
+		if (n >= arysz)
+			return -2;
+		if (!begin)
+			begin = p;		/* begin of the column name */
+		if (*p == ',')
+			end = p;		/* terminate the name */
+		if (*(p + 1) == '\0')
+			end = p + 1;		/* end of string */
+		if (!begin || !end)
+			continue;
+		if (end <= begin)
+			return -1;
+
+		id = name2id(begin, end - begin);
+		if (id == -1)
+			return -1;
+		ary[ n++ ] = id;
+		begin = NULL;
+		if (end && !*end)
+			break;
+	}
+	return n;
+}
+
+/*
+ * Parses the array like string_to_idarray but if format is "+aaa,bbb"
+ * it adds fields to array instead of replacing them.
+ */
+int string_add_to_idarray(const char *list, int ary[], size_t arysz,
+			int *ary_pos, int (name2id)(const char *, size_t))
+{
+	const char *list_add;
+	int r;
+
+	if (!list || !*list || !ary_pos ||
+	    *ary_pos < 0 || (size_t) *ary_pos > arysz)
+		return -1;
+
+	if (list[0] == '+')
+		list_add = &list[1];
+	else {
+		list_add = list;
+		*ary_pos = 0;
+	}
+
+	r = string_to_idarray(list_add, &ary[*ary_pos], arysz - *ary_pos, name2id);
+	if (r > 0)
+		*ary_pos += r;
+	return r;
+}
+
+/*
+ * LIST ::= <item> [, <item>]
+ *
+ * The <item> is translated to 'id' by name2id() function and the 'id' is used
+ * as a position in the 'ary' bit array. It means that the 'id' has to be in
+ * range <0..N> where N < sizeof(ary) * NBBY.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int string_to_bitarray(const char *list,
+		     char *ary,
+		     int (*name2bit)(const char *, size_t))
+{
+	const char *begin = NULL, *p;
+
+	if (!list || !name2bit || !ary)
+		return -EINVAL;
+
+	for (p = list; p && *p; p++) {
+		const char *end = NULL;
+		int bit;
+
+		if (!begin)
+			begin = p;		/* begin of the level name */
+		if (*p == ',')
+			end = p;		/* terminate the name */
+		if (*(p + 1) == '\0')
+			end = p + 1;		/* end of string */
+		if (!begin || !end)
+			continue;
+		if (end <= begin)
+			return -1;
+
+		bit = name2bit(begin, end - begin);
+		if (bit < 0)
+			return bit;
+		setbit(ary, bit);
+		begin = NULL;
+		if (end && !*end)
+			break;
+	}
+	return 0;
+}
+
+/*
+ * LIST ::= <item> [, <item>]
+ *
+ * The <item> is translated to 'id' by name2flag() function and the flags is
+ * set to the 'mask'
+*
+ * Returns: 0 on success, <0 on error.
+ */
+int string_to_bitmask(const char *list,
+		     unsigned long *mask,
+		     long (*name2flag)(const char *, size_t))
+{
+	const char *begin = NULL, *p;
+
+	if (!list || !name2flag || !mask)
+		return -EINVAL;
+
+	for (p = list; p && *p; p++) {
+		const char *end = NULL;
+		long flag;
+
+		if (!begin)
+			begin = p;		/* begin of the level name */
+		if (*p == ',')
+			end = p;		/* terminate the name */
+		if (*(p + 1) == '\0')
+			end = p + 1;		/* end of string */
+		if (!begin || !end)
+			continue;
+		if (end <= begin)
+			return -1;
+
+		flag = name2flag(begin, end - begin);
+		if (flag < 0)
+			return flag;	/* error */
+		*mask |= flag;
+		begin = NULL;
+		if (end && !*end)
+			break;
+	}
+	return 0;
+}
+
+/*
+ * Parse the lower and higher values in a string containing
+ * "lower:higher" or "lower-higher" format. Note that either
+ * the lower or the higher values may be missing, and the def
+ * value will be assigned to it by default.
+ *
+ * Returns: 0 on success, <0 on error.
+ */
+int parse_range(const char *str, int *lower, int *upper, int def)
+{
+	char *end = NULL;
+
+	if (!str)
+		return 0;
+
+	*upper = *lower = def;
+	errno = 0;
+
+	if (*str == ':') {				/* <:N> */
+		str++;
+		*upper = strtol(str, &end, 10);
+		if (errno || !end || *end || end == str)
+			return -1;
+	} else {
+		*upper = *lower = strtol(str, &end, 10);
+		if (errno || !end || end == str)
+			return -1;
+
+		if (*end == ':' && !*(end + 1))		/* <M:> */
+			*upper = 0;
+		else if (*end == '-' || *end == ':') {	/* <M:N> <M-N> */
+			str = end + 1;
+			end = NULL;
+			errno = 0;
+			*upper = strtol(str, &end, 10);
+
+			if (errno || !end || *end || end == str)
+				return -1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Compare two strings for equality, ignoring at most one trailing
+ * slash.
+ */
+int streq_except_trailing_slash(const char *s1, const char *s2)
+{
+	int equal;
+
+	if (!s1 && !s2)
+		return 1;
+	if (!s1 || !s2)
+		return 0;
+
+	equal = !strcmp(s1, s2);
+
+	if (!equal) {
+		size_t len1 = strlen(s1);
+		size_t len2 = strlen(s2);
+
+		if (len1 && *(s1 + len1 - 1) == '/')
+			len1--;
+		if (len2 && *(s2 + len2 - 1) == '/')
+			len2--;
+		if (len1 != len2)
+			return 0;
+
+		equal = !strncmp(s1, s2, len1);
+	}
+
+	return equal;
+}
+
+
+#ifdef TEST_PROGRAM
+
+int main(int argc, char *argv[])
+{
+	uintmax_t size = 0;
+	char *hum, *hum2;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s <number>[suffix]\n",	argv[0]);
+		exit(EXIT_FAILURE);
+	}
+
+	if (strtosize(argv[1], &size))
+		errx(EXIT_FAILURE, "invalid size '%s' value", argv[1]);
+
+	hum = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
+	hum2 = size_to_human_string(SIZE_SUFFIX_3LETTER |
+				    SIZE_SUFFIX_SPACE, size);
+
+	printf("%25s : %20ju : %8s : %12s\n", argv[1], size, hum, hum2);
+	free(hum);
+	free(hum2);
+
+	return EXIT_SUCCESS;
+}
+#endif /* TEST_PROGRAM */
diff --git a/libblkid/lib/swapprober.c b/libblkid/lib/swapprober.c
new file mode 100644
index 0000000..5a4b112
--- /dev/null
+++ b/libblkid/lib/swapprober.c
@@ -0,0 +1,49 @@
+
+#include "c.h"
+#include "nls.h"
+
+#include "swapheader.h"
+#include "swapprober.h"
+
+blkid_probe get_swap_prober(const char *devname)
+{
+	blkid_probe pr;
+	int rc;
+	const char *version = NULL;
+	char *swap_filter[] = { "swap", NULL };
+
+	pr = blkid_new_probe_from_filename(devname);
+	if (!pr) {
+		warn(_("%s: unable to probe device"), devname);
+		return NULL;
+	}
+
+	blkid_probe_enable_superblocks(pr, TRUE);
+	blkid_probe_set_superblocks_flags(pr,
+			BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
+			BLKID_SUBLKS_VERSION);
+
+	blkid_probe_filter_superblocks_type(pr, BLKID_FLTR_ONLYIN, swap_filter);
+
+	rc = blkid_do_safeprobe(pr);
+	if (rc == -1)
+		warn(_("%s: unable to probe device"), devname);
+	else if (rc == -2)
+		warnx(_("%s: ambiguous probing result; use wipefs(8)"), devname);
+	else if (rc == 1)
+		warnx(_("%s: not a valid swap partition"), devname);
+
+	if (rc == 0) {
+		/* Only the SWAPSPACE2 is supported. */
+		if (blkid_probe_lookup_value(pr, "VERSION", &version, NULL) == 0
+		    && version
+		    && strcmp(version, stringify_value(SWAP_VERSION)))
+			warnx(_("%s: unsupported swap version '%s'"),
+						devname, version);
+		else
+			return pr;
+	}
+
+	blkid_free_probe(pr);
+	return NULL;
+}
diff --git a/libblkid/lib/sysfs.c b/libblkid/lib/sysfs.c
new file mode 100644
index 0000000..63a90dc
--- /dev/null
+++ b/libblkid/lib/sysfs.c
@@ -0,0 +1,1069 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <ctype.h>
+
+#include "c.h"
+#include "at.h"
+#include "pathnames.h"
+#include "sysfs.h"
+#include "fileutils.h"
+#include "all-io.h"
+
+char *sysfs_devno_attribute_path(dev_t devno, char *buf,
+				 size_t bufsiz, const char *attr)
+{
+	int len;
+
+	if (attr)
+		len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d/%s",
+			major(devno), minor(devno), attr);
+	else
+		len = snprintf(buf, bufsiz, _PATH_SYS_DEVBLOCK "/%d:%d",
+			major(devno), minor(devno));
+
+	return (len < 0 || (size_t) len + 1 > bufsiz) ? NULL : buf;
+}
+
+int sysfs_devno_has_attribute(dev_t devno, const char *attr)
+{
+	char path[PATH_MAX];
+	struct stat info;
+
+	if (!sysfs_devno_attribute_path(devno, path, sizeof(path), attr))
+		return 0;
+	if (stat(path, &info) == 0)
+		return 1;
+	return 0;
+}
+
+char *sysfs_devno_path(dev_t devno, char *buf, size_t bufsiz)
+{
+	return sysfs_devno_attribute_path(devno, buf, bufsiz, NULL);
+}
+
+dev_t sysfs_devname_to_devno(const char *name, const char *parent)
+{
+	char buf[PATH_MAX], *path = NULL;
+	dev_t dev = 0;
+
+	if (strncmp("/dev/", name, 5) == 0) {
+		/*
+		 * Read from /dev
+		 */
+		struct stat st;
+
+		if (stat(name, &st) == 0)
+			dev = st.st_rdev;
+		else
+			name += 5;	/* unaccesible, or not node in /dev */
+	}
+
+	if (!dev && parent && strncmp("dm-", name, 3)) {
+		/*
+		 * Create path to /sys/block/<parent>/<name>/dev
+		 */
+		int len = snprintf(buf, sizeof(buf),
+				_PATH_SYS_BLOCK "/%s/%s/dev", parent, name);
+		if (len < 0 || (size_t) len + 1 > sizeof(buf))
+			return 0;
+		path = buf;
+
+	} else if (!dev) {
+		/*
+		 * Create path to /sys/block/<name>/dev
+		 */
+		int len = snprintf(buf, sizeof(buf),
+				_PATH_SYS_BLOCK "/%s/dev", name);
+		if (len < 0 || (size_t) len + 1 > sizeof(buf))
+			return 0;
+		path = buf;
+	}
+
+	if (path) {
+		/*
+		 * read devno from sysfs
+		 */
+		FILE *f;
+		int maj = 0, min = 0;
+
+		f = fopen(path, "r" UL_CLOEXECSTR);
+		if (!f)
+			return 0;
+
+		if (fscanf(f, "%d:%d", &maj, &min) == 2)
+			dev = makedev(maj, min);
+		fclose(f);
+	}
+	return dev;
+}
+
+/*
+ * Returns devname (e.g. "/dev/sda1") for the given devno.
+ *
+ * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
+ * symlinks.
+ *
+ * Please, use more robust blkid_devno_to_devname() in your applications.
+ */
+char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz)
+{
+	struct sysfs_cxt cxt;
+	char *name;
+	size_t sz;
+	struct stat st;
+
+	if (sysfs_init(&cxt, devno, NULL))
+		return NULL;
+
+	name = sysfs_get_devname(&cxt, buf, bufsiz);
+	sysfs_deinit(&cxt);
+
+	if (!name)
+		return NULL;
+
+	sz = strlen(name);
+
+	if (sz + sizeof("/dev/") > bufsiz)
+		return NULL;
+
+	/* create the final "/dev/<name>" string */
+	memmove(buf + 5, name, sz + 1);
+	memcpy(buf, "/dev/", 5);
+
+	if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == devno)
+		return buf;
+
+	return NULL;
+}
+
+int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent)
+{
+	char path[PATH_MAX];
+	int fd, rc;
+
+	memset(cxt, 0, sizeof(*cxt));
+	cxt->dir_fd = -1;
+
+	if (!sysfs_devno_path(devno, path, sizeof(path)))
+		goto err;
+
+	fd = open(path, O_RDONLY|O_CLOEXEC);
+	if (fd < 0)
+		goto err;
+	cxt->dir_fd = fd;
+
+	cxt->dir_path = strdup(path);
+	if (!cxt->dir_path)
+		goto err;
+	cxt->devno = devno;
+	cxt->parent = parent;
+	return 0;
+err:
+	rc = errno > 0 ? -errno : -1;
+	sysfs_deinit(cxt);
+	return rc;
+}
+
+void sysfs_deinit(struct sysfs_cxt *cxt)
+{
+	if (!cxt)
+		return;
+
+	if (cxt->dir_fd >= 0)
+	       close(cxt->dir_fd);
+	free(cxt->dir_path);
+
+	memset(cxt, 0, sizeof(*cxt));
+
+	cxt->dir_fd = -1;
+}
+
+int sysfs_stat(struct sysfs_cxt *cxt, const char *attr, struct stat *st)
+{
+	int rc = fstat_at(cxt->dir_fd, cxt->dir_path, attr, st, 0);
+
+	if (rc != 0 && errno == ENOENT &&
+	    strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
+
+		/* Exception for "queue/<attr>". These attributes are available
+		 * for parental devices only
+		 */
+		return fstat_at(cxt->parent->dir_fd,
+				cxt->parent->dir_path, attr, st, 0);
+	}
+	return rc;
+}
+
+int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr)
+{
+	struct stat st;
+
+	return sysfs_stat(cxt, attr, &st) == 0;
+}
+
+static int sysfs_open(struct sysfs_cxt *cxt, const char *attr, int flags)
+{
+	int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, flags);
+
+	if (fd == -1 && errno == ENOENT &&
+	    strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
+
+		/* Exception for "queue/<attr>". These attributes are available
+		 * for parental devices only
+		 */
+		fd = open_at(cxt->parent->dir_fd, cxt->dir_path, attr, flags);
+	}
+	return fd;
+}
+
+ssize_t sysfs_readlink(struct sysfs_cxt *cxt, const char *attr,
+		   char *buf, size_t bufsiz)
+{
+	if (!cxt->dir_path)
+		return -1;
+
+	if (attr)
+		return readlink_at(cxt->dir_fd, cxt->dir_path, attr, buf, bufsiz);
+
+	/* read /sys/dev/block/<maj:min> link */
+	return readlink(cxt->dir_path, buf, bufsiz);
+}
+
+DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr)
+{
+	DIR *dir;
+	int fd = -1;
+
+	if (attr)
+		fd = sysfs_open(cxt, attr, O_RDONLY|O_CLOEXEC);
+
+	else if (cxt->dir_fd >= 0)
+		/* request to open root of device in sysfs (/sys/block/<dev>)
+		 * -- we cannot use cxt->sysfs_fd directly, because closedir()
+		 * will close this our persistent file descriptor.
+		 */
+		fd = dup(cxt->dir_fd);
+
+	if (fd < 0)
+		return NULL;
+
+	dir = fdopendir(fd);
+	if (!dir) {
+		close(fd);
+		return NULL;
+	}
+	if (!attr)
+		 rewinddir(dir);
+	return dir;
+}
+
+
+static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr)
+{
+	int fd = sysfs_open(cxt, attr, O_RDONLY|O_CLOEXEC);
+
+	return fd < 0 ? NULL : fdopen(fd, "r" UL_CLOEXECSTR);
+}
+
+
+static struct dirent *xreaddir(DIR *dp)
+{
+	struct dirent *d;
+
+	while ((d = readdir(dp))) {
+		if (!strcmp(d->d_name, ".") ||
+		    !strcmp(d->d_name, ".."))
+			continue;
+
+		/* blacklist here? */
+		break;
+	}
+	return d;
+}
+
+int sysfs_is_partition_dirent(DIR *dir, struct dirent *d, const char *parent_name)
+{
+	char path[256];
+
+#ifdef _DIRENT_HAVE_D_TYPE
+	if (d->d_type != DT_DIR &&
+	    d->d_type != DT_LNK &&
+	    d->d_type != DT_UNKNOWN)
+		return 0;
+#endif
+	if (parent_name) {
+		const char *p = parent_name;
+		size_t len;
+
+		/* /dev/sda --> "sda" */
+		if (*parent_name == '/') {
+			p = strrchr(parent_name, '/');
+			if (!p)
+				return 0;
+			p++;
+		}
+
+		len = strlen(p);
+		if (strlen(d->d_name) <= len)
+			return 0;
+
+		/* partitions subdir name is
+		 *	"<parent>[:digit:]" or "<parent>p[:digit:]"
+		 */
+		return strncmp(p, d->d_name, len) == 0 &&
+		       ((*(d->d_name + len) == 'p' && isdigit(*(d->d_name + len + 1)))
+			|| isdigit(*(d->d_name + len)));
+	}
+
+	/* Cannot use /partition file, not supported on old sysfs */
+	snprintf(path, sizeof(path), "%s/start", d->d_name);
+
+	return faccessat(dirfd(dir), path, R_OK, 0) == 0;
+}
+
+/*
+ * Converts @partno (partition number) to devno of the partition.
+ * The @cxt handles wholedisk device.
+ *
+ * Note that this code does not expect any special format of the
+ * partitions devnames.
+ */
+dev_t sysfs_partno_to_devno(struct sysfs_cxt *cxt, int partno)
+{
+	DIR *dir;
+	struct dirent *d;
+	char path[256];
+	dev_t devno = 0;
+
+	dir = sysfs_opendir(cxt, NULL);
+	if (!dir)
+		return 0;
+
+	while ((d = xreaddir(dir))) {
+		int n, maj, min;
+
+		if (!sysfs_is_partition_dirent(dir, d, NULL))
+			continue;
+
+		snprintf(path, sizeof(path), "%s/partition", d->d_name);
+		if (sysfs_read_int(cxt, path, &n))
+			continue;
+
+		if (n == partno) {
+			snprintf(path, sizeof(path), "%s/dev", d->d_name);
+			if (sysfs_scanf(cxt, path, "%d:%d", &maj, &min) == 2)
+				devno = makedev(maj, min);
+			break;
+		}
+	}
+
+	closedir(dir);
+	return devno;
+}
+
+
+int sysfs_scanf(struct sysfs_cxt *cxt,  const char *attr, const char *fmt, ...)
+{
+	FILE *f = sysfs_fopen(cxt, attr);
+	va_list ap;
+	int rc;
+
+	if (!f)
+		return -EINVAL;
+	va_start(ap, fmt);
+	rc = vfscanf(f, fmt, ap);
+	va_end(ap);
+
+	fclose(f);
+	return rc;
+}
+
+
+int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res)
+{
+	int64_t x = 0;
+
+	if (sysfs_scanf(cxt, attr, "%"SCNd64, &x) == 1) {
+		if (res)
+			*res = x;
+		return 0;
+	}
+	return -1;
+}
+
+int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res)
+{
+	uint64_t x = 0;
+
+	if (sysfs_scanf(cxt, attr, "%"SCNu64, &x) == 1) {
+		if (res)
+			*res = x;
+		return 0;
+	}
+	return -1;
+}
+
+int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res)
+{
+	int x = 0;
+
+	if (sysfs_scanf(cxt, attr, "%d", &x) == 1) {
+		if (res)
+			*res = x;
+		return 0;
+	}
+	return -1;
+}
+
+int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str)
+{
+	int fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC);
+	int rc, errsv;
+
+	if (fd < 0)
+		return -errno;
+	rc = write_all(fd, str, strlen(str));
+
+	errsv = errno;
+	close(fd);
+	errno = errsv;
+	return rc;
+}
+
+int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num)
+{
+	char buf[sizeof(stringify_value(ULLONG_MAX))];
+	int fd, rc = 0, len, errsv;
+
+	fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC);
+	if (fd < 0)
+		return -errno;
+
+	len = snprintf(buf, sizeof(buf), "%ju", num);
+	if (len < 0 || (size_t) len + 1 > sizeof(buf))
+		rc = -errno;
+	else
+		rc = write_all(fd, buf, len);
+
+	errsv = errno;
+	close(fd);
+	errno = errsv;
+	return rc;
+}
+
+char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr)
+{
+	char buf[1024];
+	return sysfs_scanf(cxt, attr, "%1023[^\n]", buf) == 1 ?
+						strdup(buf) : NULL;
+}
+
+int sysfs_count_dirents(struct sysfs_cxt *cxt, const char *attr)
+{
+	DIR *dir;
+	int r = 0;
+
+	if (!(dir = sysfs_opendir(cxt, attr)))
+		return 0;
+
+	while (xreaddir(dir)) r++;
+
+	closedir(dir);
+	return r;
+}
+
+int sysfs_count_partitions(struct sysfs_cxt *cxt, const char *devname)
+{
+	DIR *dir;
+	struct dirent *d;
+	int r = 0;
+
+	if (!(dir = sysfs_opendir(cxt, NULL)))
+		return 0;
+
+	while ((d = xreaddir(dir))) {
+		if (sysfs_is_partition_dirent(dir, d, devname))
+			r++;
+	}
+
+	closedir(dir);
+	return r;
+}
+
+/*
+ * Returns slave name if there is only one slave, otherwise returns NULL.
+ * The result should be deallocated by free().
+ */
+char *sysfs_get_slave(struct sysfs_cxt *cxt)
+{
+	DIR *dir;
+	struct dirent *d;
+	char *name = NULL;
+
+	if (!(dir = sysfs_opendir(cxt, "slaves")))
+		return NULL;
+
+	while ((d = xreaddir(dir))) {
+		if (name)
+			goto err;	/* more slaves */
+
+		name = strdup(d->d_name);
+	}
+
+	closedir(dir);
+	return name;
+err:
+	free(name);
+	closedir(dir);
+	return NULL;
+}
+
+/*
+ * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min>
+ * symlinks.
+ */
+char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz)
+{
+	char *name = NULL;
+	ssize_t sz;
+
+	sz = sysfs_readlink(cxt, NULL, buf, bufsiz - 1);
+	if (sz < 0)
+		return NULL;
+
+	buf[sz] = '\0';
+	name = strrchr(buf, '/');
+	if (!name)
+		return NULL;
+
+	name++;
+	sz = strlen(name);
+
+	memmove(buf, name, sz + 1);
+	return buf;
+}
+
+#define SUBSYSTEM_LINKNAME	"/subsystem"
+
+/*
+ * For example:
+ *
+ * chain: /sys/dev/block/../../devices/pci0000:00/0000:00:1a.0/usb1/1-1/1-1.2/ \
+ *                           1-1.2:1.0/host65/target65:0:0/65:0:0:0/block/sdb
+ *
+ * The function check if <chain>/subsystem symlink exists, if yes then returns
+ * basename of the readlink result, and remove the last subdirectory from the
+ * <chain> path.
+ */
+static char *get_subsystem(char *chain, char *buf, size_t bufsz)
+{
+	size_t len;
+	char *p;
+
+	if (!chain || !*chain)
+		return NULL;
+
+	len = strlen(chain);
+	if (len + sizeof(SUBSYSTEM_LINKNAME) > PATH_MAX)
+		return NULL;
+
+	do {
+		ssize_t sz;
+
+		/* append "/subsystem" to the path */
+		memcpy(chain + len, SUBSYSTEM_LINKNAME, sizeof(SUBSYSTEM_LINKNAME));
+
+		/* try if subsystem symlink exists */
+		sz = readlink(chain, buf, bufsz - 1);
+
+		/* remove last subsystem from chain */
+		chain[len] = '\0';
+		p = strrchr(chain, '/');
+		if (p) {
+			*p = '\0';
+			len = p - chain;
+		}
+
+		if (sz > 0) {
+			/* we found symlink to subsystem, return basename */
+			buf[sz] = '\0';
+			return basename(buf);
+		}
+
+	} while (p);
+
+	return NULL;
+}
+
+/*
+ * Returns complete path to the device, the patch contains all all sybsystems
+ * used for the device.
+ */
+char *sysfs_get_devchain(struct sysfs_cxt *cxt, char *buf, size_t bufsz)
+{
+	/* read /sys/dev/block/<maj>:<min> symlink */
+	size_t sz = sysfs_readlink(cxt, NULL, buf, bufsz);
+	if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > bufsz)
+		return NULL;
+
+	buf[sz++] = '\0';
+
+	/* create absolute patch from the link */
+	memmove(buf + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, buf, sz);
+	memcpy(buf, _PATH_SYS_DEVBLOCK "/", sizeof(_PATH_SYS_DEVBLOCK "/") - 1);
+
+	return buf;
+}
+
+/*
+ * The @subsys returns the next subsystem in the chain. Function modifies
+ * @devchain string.
+ *
+ * Returns: 0 in success, <0 on error, 1 on end of chain
+ */
+int sysfs_next_subsystem(struct sysfs_cxt *cxt __attribute__((unused)),
+			 char *devchain, char **subsys)
+{
+	char subbuf[PATH_MAX];
+	char *sub;
+
+	if (!subsys || !devchain)
+		return -EINVAL;
+
+	while ((sub = get_subsystem(devchain, subbuf, sizeof(subbuf)))) {
+		*subsys = strdup(sub);
+		if (!*subsys)
+			return -ENOMEM;
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static int is_hotpluggable_subsystem(const char *name)
+{
+	static const char * const hotplug_subsystems[] = {
+		"usb",
+		"ieee1394",
+		"pcmcia",
+		"mmc",
+		"ccw"
+	};
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(hotplug_subsystems); i++)
+		if (strcmp(name, hotplug_subsystems[i]) == 0)
+			return 1;
+
+	return 0;
+}
+
+int sysfs_is_hotpluggable(struct sysfs_cxt *cxt)
+{
+	char buf[PATH_MAX], *chain, *sub;
+	int rc = 0;
+
+
+	/* check /sys/dev/block/<maj>:<min>/removable attribute */
+	if (sysfs_read_int(cxt, "removable", &rc) == 0 && rc == 1)
+		return 1;
+
+	chain = sysfs_get_devchain(cxt, buf, sizeof(buf));
+
+	while (chain && sysfs_next_subsystem(cxt, chain, &sub) == 0) {
+		rc = is_hotpluggable_subsystem(sub);
+		if (rc) {
+			free(sub);
+			break;
+		}
+		free(sub);
+	}
+
+	return rc;
+}
+
+static int get_dm_wholedisk(struct sysfs_cxt *cxt, char *diskname,
+                size_t len, dev_t *diskdevno)
+{
+    int rc = 0;
+    char *name;
+
+    /* Note, sysfs_get_slave() returns the first slave only,
+     * if there is more slaves, then return NULL
+     */
+    name = sysfs_get_slave(cxt);
+    if (!name)
+        return -1;
+
+    if (diskname && len) {
+        strncpy(diskname, name, len);
+        diskname[len - 1] = '\0';
+    }
+
+    if (diskdevno) {
+        *diskdevno = sysfs_devname_to_devno(name, NULL);
+        if (!*diskdevno)
+            rc = -1;
+    }
+
+    free(name);
+    return rc;
+}
+
+/*
+ * Returns by @diskdevno whole disk device devno and (optionaly) by
+ * @diskname the whole disk device name.
+ */
+int sysfs_devno_to_wholedisk(dev_t dev, char *diskname,
+            size_t len, dev_t *diskdevno)
+{
+    struct sysfs_cxt cxt;
+    int is_part = 0;
+
+    if (!dev || sysfs_init(&cxt, dev, NULL) != 0)
+        return -1;
+
+    is_part = sysfs_has_attribute(&cxt, "partition");
+    if (!is_part) {
+        /*
+         * Extra case for partitions mapped by device-mapper.
+         *
+         * All regualar partitions (added by BLKPG ioctl or kernel PT
+         * parser) have the /sys/.../partition file. The partitions
+         * mapped by DM don't have such file, but they have "part"
+         * prefix in DM UUID.
+         */
+        char *uuid = sysfs_strdup(&cxt, "dm/uuid");
+        char *tmp = uuid;
+        char *prefix = uuid ? strsep(&tmp, "-") : NULL;
+
+        if (prefix && strncasecmp(prefix, "part", 4) == 0)
+            is_part = 1;
+        free(uuid);
+
+        if (is_part &&
+            get_dm_wholedisk(&cxt, diskname, len, diskdevno) == 0)
+            /*
+             * partitioned device, mapped by DM
+             */
+            goto done;
+
+        is_part = 0;
+    }
+
+    if (!is_part) {
+        /*
+         * unpartitioned device
+         */
+        if (diskname && len) {
+            if (!sysfs_get_devname(&cxt, diskname, len))
+                goto err;
+        }
+        if (diskdevno)
+            *diskdevno = dev;
+
+    } else {
+        /*
+         * partitioned device
+         *  - readlink /sys/dev/block/8:1   = ../../block/sda/sda1
+         *  - dirname  ../../block/sda/sda1 = ../../block/sda
+         *  - basename ../../block/sda      = sda
+         */
+        char linkpath[PATH_MAX];
+        char *name;
+        int linklen;
+
+        linklen = sysfs_readlink(&cxt, NULL,
+                linkpath, sizeof(linkpath) - 1);
+        if (linklen < 0)
+            goto err;
+        linkpath[linklen] = '\0';
+
+        stripoff_last_component(linkpath);      /* dirname */
+        name = stripoff_last_component(linkpath);   /* basename */
+        if (!name)
+            goto err;
+
+        if (diskname && len) {
+            strncpy(diskname, name, len);
+            diskname[len - 1] = '\0';
+        }
+
+        if (diskdevno) {
+            *diskdevno = sysfs_devname_to_devno(name, NULL);
+            if (!*diskdevno)
+                goto err;
+        }
+    }
+
+done:
+    sysfs_deinit(&cxt);
+    return 0;
+err:
+    sysfs_deinit(&cxt);
+    return -1;
+}
+
+/*
+ * Returns 1 if the device is private LVM device.
+ */
+int sysfs_devno_is_lvm_private(dev_t devno)
+{
+	struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
+	char *uuid = NULL;
+	int rc = 0;
+
+	if (sysfs_init(&cxt, devno, NULL) != 0)
+		return 0;
+
+	uuid = sysfs_strdup(&cxt, "dm/uuid");
+
+	/* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important
+	 * is the "LVM" prefix and "-<name>" postfix).
+	 */
+	if (uuid && strncmp(uuid, "LVM-", 4) == 0) {
+		char *p = strrchr(uuid + 4, '-');
+
+		if (p && *(p + 1))
+			rc = 1;
+	}
+
+	sysfs_deinit(&cxt);
+	free(uuid);
+	return rc;
+}
+
+/*
+ * Return 0 or 1, or < 0 in case of error
+ */
+int sysfs_devno_is_wholedisk(dev_t devno)
+{
+	dev_t disk;
+
+	if (sysfs_devno_to_wholedisk(devno, NULL, 0, &disk) != 0)
+		return -1;
+
+	return devno == disk;
+}
+
+
+int sysfs_scsi_get_hctl(struct sysfs_cxt *cxt, int *h, int *c, int *t, int *l)
+{
+	char buf[PATH_MAX], *hctl;
+	ssize_t len;
+
+	if (!cxt)
+		return -EINVAL;
+	if (cxt->has_hctl)
+		goto done;
+
+	len = sysfs_readlink(cxt, "device", buf, sizeof(buf) - 1);
+	if (len < 0)
+		return len;
+
+	buf[len] = '\0';
+	hctl = strrchr(buf, '/');
+	if (!hctl)
+		return -1;
+	hctl++;
+
+	if (sscanf(hctl, "%u:%u:%u:%u", &cxt->scsi_host, &cxt->scsi_channel,
+				&cxt->scsi_target, &cxt->scsi_lun) != 4)
+		return -1;
+
+	cxt->has_hctl = 1;
+done:
+	if (h)
+		*h = cxt->scsi_host;
+	if (c)
+		*c = cxt->scsi_channel;
+	if (t)
+		*t = cxt->scsi_target;
+	if (l)
+		*l = cxt->scsi_lun;
+	return 0;
+}
+
+
+static char *sysfs_scsi_host_attribute_path(struct sysfs_cxt *cxt,
+		const char *type, char *buf, size_t bufsz, const char *attr)
+{
+	int len;
+	int host;
+
+	if (sysfs_scsi_get_hctl(cxt, &host, NULL, NULL, NULL))
+		return NULL;
+
+	if (attr)
+		len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d/%s",
+				type, host, attr);
+	else
+		len = snprintf(buf, bufsz, _PATH_SYS_CLASS "/%s_host/host%d",
+				type, host);
+
+	return (len < 0 || (size_t) len + 1 > bufsz) ? NULL : buf;
+}
+
+char *sysfs_scsi_host_strdup_attribute(struct sysfs_cxt *cxt,
+		const char *type, const char *attr)
+{
+	char buf[1024];
+	int rc;
+	FILE *f;
+
+	if (!attr || !type ||
+	    !sysfs_scsi_host_attribute_path(cxt, type, buf, sizeof(buf), attr))
+		return NULL;
+
+	if (!(f = fopen(buf, "r" UL_CLOEXECSTR)))
+                return NULL;
+
+	rc = fscanf(f, "%1023[^\n]", buf);
+	fclose(f);
+
+	return rc == 1 ? strdup(buf) : NULL;
+}
+
+int sysfs_scsi_host_is(struct sysfs_cxt *cxt, const char *type)
+{
+	char buf[PATH_MAX];
+	struct stat st;
+
+	if (!type || !sysfs_scsi_host_attribute_path(cxt, type,
+				buf, sizeof(buf), NULL))
+		return 0;
+
+	return stat(buf, &st) == 0 && S_ISDIR(st.st_mode);
+}
+
+static char *sysfs_scsi_attribute_path(struct sysfs_cxt *cxt,
+		char *buf, size_t bufsz, const char *attr)
+{
+	int len, h, c, t, l;
+
+	if (sysfs_scsi_get_hctl(cxt, &h, &c, &t, &l) != 0)
+		return NULL;
+
+	if (attr)
+		len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d/%s",
+				h,c,t,l, attr);
+	else
+		len = snprintf(buf, bufsz, _PATH_SYS_SCSI "/devices/%d:%d:%d:%d",
+				h,c,t,l);
+	return (len < 0 || (size_t) len + 1 > bufsz) ? NULL : buf;
+}
+
+int sysfs_scsi_has_attribute(struct sysfs_cxt *cxt, const char *attr)
+{
+	char path[PATH_MAX];
+	struct stat st;
+
+	if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), attr))
+		return 0;
+
+	return stat(path, &st) == 0;
+}
+
+int sysfs_scsi_path_contains(struct sysfs_cxt *cxt, const char *pattern)
+{
+	char path[PATH_MAX], linkc[PATH_MAX];
+	struct stat st;
+	ssize_t len;
+
+	if (!sysfs_scsi_attribute_path(cxt, path, sizeof(path), NULL))
+		return 0;
+
+	if (stat(path, &st) != 0)
+		return 0;
+
+	len = readlink(path, linkc, sizeof(linkc) - 1);
+	if (len < 0)
+		return 0;
+
+	linkc[len] = '\0';
+	return strstr(linkc, pattern) != NULL;
+}
+
+#ifdef TEST_PROGRAM_SYSFS
+#include <errno.h>
+#include <err.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+	struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY;
+	char *devname;
+	dev_t devno;
+	char path[PATH_MAX], *sub, *chain;
+	int i, is_part;
+	uint64_t u64;
+	ssize_t len;
+
+	if (argc != 2)
+		errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]);
+
+	devname = argv[1];
+	devno = sysfs_devname_to_devno(devname, NULL);
+
+	if (!devno)
+		err(EXIT_FAILURE, "failed to read devno");
+
+	is_part = sysfs_devno_has_attribute(devno, "partition");
+
+	printf("NAME: %s\n", devname);
+	printf("DEVNO: %u (%d:%d)\n", (unsigned int) devno, major(devno), minor(devno));
+	printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path)));
+	printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path)));
+	printf("PARTITION: %s\n", is_part ? "YES" : "NOT");
+
+	if (sysfs_init(&cxt, devno, NULL))
+		return EXIT_FAILURE;
+
+	len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1);
+	if (len > 0) {
+		path[len] = '\0';
+		printf("DEVNOLINK: %s\n", path);
+	}
+
+	if (!is_part) {
+		printf("First 5 partitions:\n");
+		for (i = 1; i <= 5; i++) {
+			dev_t dev = sysfs_partno_to_devno(&cxt, i);
+			if (dev)
+				printf("\t#%d %d:%d\n", i, major(dev), minor(dev));
+		}
+	}
+
+	printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves"));
+
+	if (sysfs_read_u64(&cxt, "size", &u64))
+		printf("read SIZE failed\n");
+	else
+		printf("SIZE: %jd\n", u64);
+
+	if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i))
+		printf("read SECTOR failed\n");
+	else
+		printf("SECTOR: %d\n", i);
+
+	printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path)));
+	printf("HOTPLUG: %s\n", sysfs_is_hotpluggable(&cxt) ? "yes" : "no");
+
+	chain = sysfs_get_devchain(&cxt, path, sizeof(path));
+	printf("SUBSUSTEMS:\n");
+
+	while (chain && sysfs_next_subsystem(&cxt, chain, &sub) == 0) {
+		printf("\t%s\n", sub);
+		free(sub);
+	}
+
+
+	sysfs_deinit(&cxt);
+	return EXIT_SUCCESS;
+}
+#endif
diff --git a/libblkid/lib/terminal-colors.d.5 b/libblkid/lib/terminal-colors.d.5
new file mode 100644
index 0000000..66ecf2c
--- /dev/null
+++ b/libblkid/lib/terminal-colors.d.5
@@ -0,0 +1,190 @@
+.\" terminal-colors.d.5 --
+.\" Copyright 2014 Ondrej Oprala <ooprala@redhat.com>
+.\" Copyright (C) 2014 Karel Zak <kzak@redhat.com>
+.\" Copyright 2014 Red Hat, Inc.
+.\" May be distributed under the GNU General Public License
+.TH "TERMINAL_COLORS.D" "5" "January 2014" "util-linux" "terminal-colors.d"
+.SH "NAME"
+terminal-colors.d \- Configure output colorization for various utilities
+.SH "SYNOPSIS"
+/etc/terminal-colors\&.d/[[\fIname\fR][@\fIterm\fR]\&.][\fItype\fR]
+.SH "DESCRIPTION"
+Files in this directory determine the default behavior for utilities
+when coloring output.
+
+The
+.I name
+is a utility name.  The name is optional and when none is specified then the
+file is used for all unspecified utilities.
+
+The
+.I term
+is a terminal identifier (the TERM environment variable).
+The terminal identifier is optional and when none is specified then the file
+is used for all unspecified terminals.
+
+The
+.I type
+is a file type.  Supported file types are:
+.TP
+.B disable
+Turns off output colorization for all compatible utilities.
+.TP
+.B enable
+Turns on output colorization; any matching
+.B disable
+files are ignored.
+.TP
+.B scheme
+Specifies colors used for output.  The file format may be specific to the utility,
+the default format is described below.
+.PP
+If there are more files that match for a utility, then the file with the more
+specific filename wins.  For example, the filename "@xterm.scheme" has less
+priority than "dmesg@xterm.scheme".  The lowest priority are those files without a
+utility name and terminal identifier (e.g. "disable").
+
+The user-specific
+.I $XDG_CONFIG_HOME/terminal-colors.d
+or
+.I $HOME/.config/terminal-colors.d
+overrides the global setting.
+
+.SH EXAMPLES
+Disable colors for all compatible utilities:
+.RS
+.br
+.B "touch /etc/terminal-colors.d/disable"
+.br
+.RE
+
+Disable colors for all compatible utils on a vt100 terminal:
+.RS
+.br
+.B "touch /etc/terminal-colors.d/@vt100.disable"
+.br
+.RE
+
+Disable colors for all compatible utils except dmesg(1):
+.RS
+.br
+.B "touch /etc/terminal-colors.d/disable"
+.sp
+.B "touch /etc/terminal-colors.d/dmesg.enable"
+.br
+.RE
+
+.SH DEFAULT SCHEME FILES FORMAT
+The following statement is recognized:
+
+.RS
+.br
+.B "name color-sequence"
+.br
+.RE
+
+The
+.B name
+is a logical name of color sequence (for example "error").  The names are
+specific to the utilities.  For more details always see the COLORS section
+in the man page for the utility.
+
+The
+.B color-sequence
+is a color name, ASCII color sequences or escape sequences.
+
+.SS Color names
+black, blue, brown, cyan, darkgray, gray, green, lightblue, lightcyan
+lightgray, lightgreen, lightmagenta, lightred, magenta, red and yellow
+.SS ANSI color sequences
+The color sequences are composed of sequences of numbers
+separated by semicolons.  The most common codes are:
+.sp
+.RS
+.TS
+l l.
+ 0	to restore default color
+ 1	for brighter colors
+ 4	for underlined text
+ 5	for flashing text
+30	for black foreground
+31	for red foreground
+32	for green foreground
+33	for yellow (or brown) foreground
+34	for blue foreground
+35	for purple foreground
+36	for cyan foreground
+37	for white (or gray) foreground
+40	for black background
+41	for red background
+42	for green background
+43	for yellow (or brown) background
+44	for blue background
+45	for purple background
+46	for cyan background
+47	for white (or gray) background
+.TE
+.RE
+.SS Escape sequences
+To specify control or blank characters in the color sequences,
+C-style \e-escaped notation can be used:
+.sp
+.RS
+.TS
+lb l.
+\ea	Bell (ASCII 7)
+\eb	Backspace (ASCII 8)
+\ee	Escape (ASCII 27)
+\ef	Form feed (ASCII 12)
+\en	Newline (ASCII 10)
+\er	Carriage Return (ASCII 13)
+\et	Tab (ASCII 9)
+\ev	Vertical Tab (ASCII 11)
+\e?	Delete (ASCII 127)
+\e_	Space
+\e\e	Backslash (\e)
+\e^	Caret (^)
+\e#	Hash mark (#)
+.TE
+.RE
+.sp
+Please note that escapes are necessary to enter a space, backslash,
+caret, or any control character anywhere in the string, as well as a
+hash mark as the first character.
+
+For example, to use a red background for alert messages in the output of
+.BR dmesg (1),
+use:
+
+.RS
+.br
+.B "echo 'alert 37;41' >> /etc/terminal-colors.d/dmesg.scheme"
+.br
+.RE
+
+.SS Comments
+Lines where the first non-blank character is a # (hash) are ignored.
+Any other use of the hash character is not interpreted as introducing
+a comment.
+
+.SH FILES
+.B $XDG_CONFIG_HOME/terminal-colors.d
+.br
+.B $HOME/.config/terminal-colors.d
+.br
+.B /etc/terminal-colors.d
+
+.SH ENVIRONMENT
+.IP TERMINAL_COLORS_DEBUG=all
+enables debug output.
+
+.SH COMPATIBILITY
+The terminal-colors.d functionality is currently supported by all util-linux
+utilities which provides colorized output.  For more details always see the
+COLORS section in the man page for the utility.
+
+.SH AVAILABILITY
+terminal-colors.d is part of the util-linux package and is available from
+.UR ftp://\:ftp.kernel.org\:/pub\:/linux\:/utils\:/util-linux/
+Linux Kernel Archive
+.UE .
diff --git a/libblkid/lib/timeutils.c b/libblkid/lib/timeutils.c
new file mode 100644
index 0000000..b811041
--- /dev/null
+++ b/libblkid/lib/timeutils.c
@@ -0,0 +1,342 @@
+/***
+  First set of functions in this file are part of systemd, and were
+  copied to util-linux at August 2013.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with util-linux; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/sysinfo.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "c.h"
+#include "nls.h"
+#include "strutils.h"
+#include "timeutils.h"
+
+#define WHITESPACE " \t\n\r"
+
+#define streq(a,b) (strcmp((a),(b)) == 0)
+
+static int parse_sec(const char *t, usec_t *usec)
+{
+	 static const struct {
+		const char *suffix;
+		usec_t usec;
+	 } table[] = {
+		{ "seconds",	USEC_PER_SEC },
+		{ "second",	USEC_PER_SEC },
+		{ "sec",	USEC_PER_SEC },
+		{ "s",		USEC_PER_SEC },
+		{ "minutes",	USEC_PER_MINUTE },
+		{ "minute",	USEC_PER_MINUTE },
+		{ "min",	USEC_PER_MINUTE },
+		{ "months",	USEC_PER_MONTH },
+		{ "month",	USEC_PER_MONTH },
+		{ "msec",	USEC_PER_MSEC },
+		{ "ms",		USEC_PER_MSEC },
+		{ "m",		USEC_PER_MINUTE },
+		{ "hours",	USEC_PER_HOUR },
+		{ "hour",	USEC_PER_HOUR },
+		{ "hr",		USEC_PER_HOUR },
+		{ "h",		USEC_PER_HOUR },
+		{ "days",	USEC_PER_DAY },
+		{ "day",	USEC_PER_DAY },
+		{ "d",		USEC_PER_DAY },
+		{ "weeks",	USEC_PER_WEEK },
+		{ "week",	USEC_PER_WEEK },
+		{ "w",		USEC_PER_WEEK },
+		{ "years",	USEC_PER_YEAR },
+		{ "year",	USEC_PER_YEAR },
+		{ "y",		USEC_PER_YEAR },
+		{ "usec",	1ULL },
+		{ "us",		1ULL },
+		{ "",		USEC_PER_SEC },	/* default is sec */
+	 };
+
+	const char *p;
+	usec_t r = 0;
+	int something = FALSE;
+
+	assert(t);
+	assert(usec);
+
+	p = t;
+	for (;;) {
+		long long l, z = 0;
+		char *e;
+		unsigned i, n = 0;
+
+		p += strspn(p, WHITESPACE);
+
+		if (*p == 0) {
+			if (!something)
+				return -EINVAL;
+
+			break;
+		}
+
+		errno = 0;
+		l = strtoll(p, &e, 10);
+
+		if (errno > 0)
+			return -errno;
+
+		if (l < 0)
+			return -ERANGE;
+
+		if (*e == '.') {
+			char *b = e + 1;
+
+			errno = 0;
+			z = strtoll(b, &e, 10);
+			if (errno > 0)
+				return -errno;
+
+			if (z < 0)
+				return -ERANGE;
+
+			if (e == b)
+				return -EINVAL;
+
+			n = e - b;
+
+		} else if (e == p)
+			return -EINVAL;
+
+		e += strspn(e, WHITESPACE);
+
+		for (i = 0; i < ARRAY_SIZE(table); i++)
+			if (startswith(e, table[i].suffix)) {
+				usec_t k = (usec_t) z * table[i].usec;
+
+				for (; n > 0; n--)
+					k /= 10;
+
+				r += (usec_t) l *table[i].usec + k;
+				p = e + strlen(table[i].suffix);
+
+				something = TRUE;
+				break;
+			}
+
+		if (i >= ARRAY_SIZE(table))
+			return -EINVAL;
+
+	}
+
+	*usec = r;
+
+	return 0;
+}
+
+int parse_timestamp(const char *t, usec_t *usec)
+{
+	 static const struct {
+		const char *name;
+		const int nr;
+	 } day_nr[] = {
+		{ "Sunday",	0 },
+		{ "Sun",	0 },
+		{ "Monday",	1 },
+		{ "Mon",	1 },
+		{ "Tuesday",	2 },
+		{ "Tue",	2 },
+		{ "Wednesday",	3 },
+		{ "Wed",	3 },
+		{ "Thursday",	4 },
+		{ "Thu",	4 },
+		{ "Friday",	5 },
+		{ "Fri",	5 },
+		{ "Saturday",	6 },
+		{ "Sat",	6 },
+	 };
+
+	const char *k;
+	struct tm tm, copy;
+	time_t x;
+	usec_t plus = 0, minus = 0, ret;
+	int r, weekday = -1;
+	unsigned i;
+
+	/*
+	 * Allowed syntaxes:
+	 *
+	 *   2012-09-22 16:34:22
+	 *   2012-09-22 16:34	  (seconds will be set to 0)
+	 *   2012-09-22		  (time will be set to 00:00:00)
+	 *   16:34:22		  (date will be set to today)
+	 *   16:34		  (date will be set to today, seconds to 0)
+	 *   now
+	 *   yesterday		  (time is set to 00:00:00)
+	 *   today		  (time is set to 00:00:00)
+	 *   tomorrow		  (time is set to 00:00:00)
+	 *   +5min
+	 *   -5days
+	 *
+	 */
+
+	assert(t);
+	assert(usec);
+
+	x = time(NULL);
+	localtime_r(&x, &tm);
+	tm.tm_isdst = -1;
+
+	if (streq(t, "now"))
+		goto finish;
+
+	else if (streq(t, "today")) {
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+
+	} else if (streq(t, "yesterday")) {
+		tm.tm_mday--;
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+
+	} else if (streq(t, "tomorrow")) {
+		tm.tm_mday++;
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+
+	} else if (t[0] == '+') {
+
+		r = parse_sec(t + 1, &plus);
+		if (r < 0)
+			return r;
+
+		goto finish;
+	} else if (t[0] == '-') {
+
+		r = parse_sec(t + 1, &minus);
+		if (r < 0)
+			return r;
+
+		goto finish;
+
+	} else if (endswith(t, " ago")) {
+		char *z;
+
+		z = strndup(t, strlen(t) - 4);
+		if (!z)
+			return -ENOMEM;
+
+		r = parse_sec(z, &minus);
+		free(z);
+		if (r < 0)
+			return r;
+
+		goto finish;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(day_nr); i++) {
+		size_t skip;
+
+		if (!startswith_no_case(t, day_nr[i].name))
+			continue;
+
+		skip = strlen(day_nr[i].name);
+		if (t[skip] != ' ')
+			continue;
+
+		weekday = day_nr[i].nr;
+		t += skip + 1;
+		break;
+	}
+
+	copy = tm;
+	k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
+	if (k && *k == 0)
+		goto finish;
+
+	tm = copy;
+	k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
+	if (k && *k == 0)
+		goto finish;
+
+	tm = copy;
+	k = strptime(t, "%y-%m-%d %H:%M", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%Y-%m-%d %H:%M", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%y-%m-%d", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%Y-%m-%d", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%H:%M:%S", &tm);
+	if (k && *k == 0)
+		goto finish;
+
+	tm = copy;
+	k = strptime(t, "%H:%M", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = 0;
+		goto finish;
+	}
+
+	tm = copy;
+	k = strptime(t, "%Y%m%d%H%M%S", &tm);
+	if (k && *k == 0) {
+		tm.tm_sec = 0;
+		goto finish;
+	}
+
+	return -EINVAL;
+
+ finish:
+	x = mktime(&tm);
+	if (x == (time_t)-1)
+		return -EINVAL;
+
+	if (weekday >= 0 && tm.tm_wday != weekday)
+		return -EINVAL;
+
+	ret = (usec_t) x *USEC_PER_SEC;
+
+	ret += plus;
+	if (ret > minus)
+		ret -= minus;
+	else
+		ret = 0;
+
+	*usec = ret;
+
+	return 0;
+}
diff --git a/libblkid/lib/ttyutils.c b/libblkid/lib/ttyutils.c
new file mode 100644
index 0000000..ea551e2
--- /dev/null
+++ b/libblkid/lib/ttyutils.c
@@ -0,0 +1,95 @@
+/*
+ * No copyright is claimed.  This code is in the public domain; do with
+ * it what you wish.
+ *
+ * Written by Karel Zak <kzak@redhat.com>
+ */
+#include <ctype.h>
+
+#include "c.h"
+#include "ttyutils.h"
+
+int get_terminal_width(void)
+{
+#ifdef TIOCGSIZE
+	struct ttysize	t_win;
+#endif
+#ifdef TIOCGWINSZ
+	struct winsize	w_win;
+#endif
+        const char	*cp;
+
+#ifdef TIOCGSIZE
+	if (ioctl (STDIN_FILENO, TIOCGSIZE, &t_win) == 0)
+		return t_win.ts_cols;
+#endif
+#ifdef TIOCGWINSZ
+	if (ioctl (STDIN_FILENO, TIOCGWINSZ, &w_win) == 0)
+		return w_win.ws_col;
+#endif
+        cp = getenv("COLUMNS");
+	if (cp) {
+		char *end = NULL;
+		long c;
+
+		errno = 0;
+		c = strtol(cp, &end, 10);
+
+		if (errno == 0 && end && *end == '\0' && end > cp &&
+		    c > 0 && c <= INT_MAX)
+			return c;
+	}
+	return 0;
+}
+
+int get_terminal_name(int fd,
+		      const char **path,
+		      const char **name,
+		      const char **number)
+{
+	const char *tty;
+	const char *p;
+
+	if (name)
+		*name = NULL;
+	if (path)
+		*path = NULL;
+	if (number)
+		*number = NULL;
+
+	tty = ttyname(fd);
+	if (!tty)
+		return -1;
+	if (path)
+		*path = tty;
+	tty = strncmp(tty, "/dev/", 5) == 0 ? tty + 5 : tty;
+	if (name)
+		*name = tty;
+	if (number) {
+		for (p = tty; p && *p; p++) {
+			if (isdigit(*p)) {
+				*number = p;
+				break;
+			}
+		}
+	}
+	return 0;
+}
+
+
+#ifdef TEST_PROGRAM
+# include <stdlib.h>
+int main(void)
+{
+	const char *path, *name, *num;
+
+	if (get_terminal_name(STDERR_FILENO, &path, &name, &num) == 0) {
+		fprintf(stderr, "tty path:   %s\n", path);
+		fprintf(stderr, "tty name:   %s\n", name);
+		fprintf(stderr, "tty number: %s\n", num);
+	}
+	fprintf(stderr,         "tty width:  %d\n", get_terminal_width());
+
+	return EXIT_SUCCESS;
+}
+#endif