diff --git a/SOURCES/0060-build-sys-add-CFLAGS-and-LDFLAGS-for-daemons-and-sha.patch b/SOURCES/0060-build-sys-add-CFLAGS-and-LDFLAGS-for-daemons-and-sha.patch
new file mode 100644
index 0000000..cc9d680
--- /dev/null
+++ b/SOURCES/0060-build-sys-add-CFLAGS-and-LDFLAGS-for-daemons-and-sha.patch
@@ -0,0 +1,166 @@
+From 097c66ef2dd4779e01d13ad4a5f9733334fa9d1a Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Tue, 27 Aug 2013 10:02:04 +0200
+Subject: [PATCH 60/84] build-sys: add CFLAGS and LDFLAGS for daemons and
+ shared libs
+
+This is necessary for paranoid security guys who believe that things
+like "-Wl,-z,relro" or "-Wl,-z,bind_now" is a way how to make the
+world a safer place...
+
+[rhel7: add also SOLIB_* to libfdisk]
+
+Upstream: http://github.com/karelzak/util-linux/commit/03d00d495f3b505d9cae967c629fb38aca301e01
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1092520
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ Documentation/howto-compilation.txt | 10 +++++-----
+ configure.ac                        | 10 ++++++++++
+ libblkid/src/Makemodule.am          |  2 ++
+ libfdisk/src/Makemodule.am          |  3 +++
+ libmount/src/Makemodule.am          |  2 ++
+ libuuid/src/Makemodule.am           |  7 ++++++-
+ misc-utils/Makemodule.am            |  3 ++-
+ 7 files changed, 30 insertions(+), 7 deletions(-)
+
+diff --git a/Documentation/howto-compilation.txt b/Documentation/howto-compilation.txt
+index bebe0d2..4b39246 100644
+--- a/Documentation/howto-compilation.txt
++++ b/Documentation/howto-compilation.txt
+@@ -27,12 +27,12 @@ Compiling
+ 	The SUID_* feature is currently supported for chfn, chsh,
+ 	newgrp, su, write, mount, and umount.
+ 
+-	Preferred compilation options for developers, when
+-	using gcc, are:
++	Use DAEMON_CFLAGS and DAEMON_LDFLAGS when you want to define
++	special compiler options for daemons; supported for uuidd.
+ 
+-	export CFLAGS="-Wmissing-parameter-type -Wsign-compare
+-	-Wtype-limits -Wuninitialized -Wunused-parameter
+-	-Wunused-but-set-parameter -fno-common"
++	Use SOLIB_CFLAGS and SOLIB_LDFLAGS when you want to define
++	special compiler options for shared libraries; supported for
++	libmount, libblkid and libuuid.
+ 
+ 	FIXME: add notes about klib and uClib.
+ 
+diff --git a/configure.ac b/configure.ac
+index f7c27cd..f3c7214 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1538,6 +1538,16 @@ AC_ARG_VAR([SUID_CFLAGS],
+ AC_ARG_VAR([SUID_LDFLAGS],
+ 	   [LDFLAGS used for binaries which are usually with the suid bit])
+ 
++AC_ARG_VAR([DAEMON_CFLAGS],
++	   [CFLAGS used for binaries which are usually executed as daemons])
++AC_ARG_VAR([DAEMON_LDFLAGS],
++	   [LDFLAGS used for binaries which are usually executed as daemons])
++
++AC_ARG_VAR([SOLIB_CFLAGS],
++	   [CFLAGS used for shared libraries])
++AC_ARG_VAR([SOLIB_LDFLAGS],
++	   [LDFLAGS used for shared libraries])
++
+ LIBS=""
+ 
+ 
+diff --git a/libblkid/src/Makemodule.am b/libblkid/src/Makemodule.am
+index 04d8621..1563976 100644
+--- a/libblkid/src/Makemodule.am
++++ b/libblkid/src/Makemodule.am
+@@ -113,6 +113,7 @@ libblkid_la_LIBADD = libcommon.la
+ 
+ 
+ libblkid_la_CFLAGS = \
++	$(SOLIB_CFLAGS) \
+ 	-I$(ul_libblkid_incdir) \
+ 	-I$(top_srcdir)/libblkid/src
+ 
+@@ -128,6 +129,7 @@ libblkid_la_DEPENDENCIES = \
+ 	libblkid/src/blkid.h.in
+ 
+ libblkid_la_LDFLAGS = \
++	$(SOLIB_LDFLAGS) \
+ 	-Wl,--version-script=$(top_srcdir)/libblkid/src/blkid.sym \
+ 	-version-info $(LIBBLKID_VERSION_INFO)
+ 
+diff --git a/libfdisk/src/Makemodule.am b/libfdisk/src/Makemodule.am
+index fbfb1b4..5c50001 100644
+--- a/libfdisk/src/Makemodule.am
++++ b/libfdisk/src/Makemodule.am
+@@ -25,6 +25,8 @@ nodist_libfdisk_la_SOURCES = libfdisk/src/fdiskP.h
+ libfdisk_la_LIBADD = libcommon.la
+ 
+ libfdisk_la_CFLAGS = \
++	$(AM_CFLAGS) \
++	$(SOLIB_CFLAGS) \
+ 	-I$(ul_libfdisk_incdir) \
+ 	-I$(top_srcdir)/libfdisk/src
+ 
+@@ -39,6 +41,7 @@ libfdisk_la_CFLAGS += -I$(ul_libuuid_incdir)
+ endif
+ 
+ libfdisk_la_DEPENDENCIES = $(libfdisk_la_LIBADD)
++libfdisk_la_LDFLAGS = $(SOLIB_LDFLAGS)
+ 
+ 
+ check_PROGRAMS += \
+diff --git a/libmount/src/Makemodule.am b/libmount/src/Makemodule.am
+index 494e02a..8ef07e5 100644
+--- a/libmount/src/Makemodule.am
++++ b/libmount/src/Makemodule.am
+@@ -33,6 +33,7 @@ nodist_libmount_la_SOURCES = libmount/src/mountP.h
+ libmount_la_LIBADD = libcommon.la libblkid.la $(SELINUX_LIBS)
+ 
+ libmount_la_CFLAGS = \
++	$(SOLIB_CFLAGS) \
+ 	-I$(ul_libblkid_incdir) \
+ 	-I$(ul_libmount_incdir) \
+ 	-I$(top_srcdir)/libmount/src
+@@ -43,6 +44,7 @@ libmount_la_DEPENDENCIES = \
+ 	libmount/src/libmount.h.in
+ 
+ libmount_la_LDFLAGS = \
++	$(SOLIB_LDFLAGS) \
+ 	-Wl,--version-script=$(top_srcdir)/libmount/src/libmount.sym \
+ 	-version-info $(LIBMOUNT_VERSION_INFO)
+ 
+diff --git a/libuuid/src/Makemodule.am b/libuuid/src/Makemodule.am
+index 73f1ba9..a20cb4c 100644
+--- a/libuuid/src/Makemodule.am
++++ b/libuuid/src/Makemodule.am
+@@ -29,9 +29,14 @@ libuuid_la_SOURCES = \
+ 
+ libuuid_la_DEPENDENCIES = libuuid/src/uuid.sym
+ libuuid_la_LIBADD       = $(SOCKET_LIBS)
+-libuuid_la_CFLAGS       = -I$(ul_libuuid_incdir) -Ilibuuid/src
++
++libuuid_la_CFLAGS = \
++	 $(SOLIB_CFLAGS) \
++	 -I$(ul_libuuid_incdir) \
++	 -Ilibuuid/src
+ 
+ libuuid_la_LDFLAGS = \
++	$(SOLIB_LDFLAGS) \
+ 	-Wl,--version-script=$(top_srcdir)/libuuid/src/uuid.sym \
+ 	-version-info $(LIBUUID_VERSION_INFO)
+ 
+diff --git a/misc-utils/Makemodule.am b/misc-utils/Makemodule.am
+index a615047..70a78f2 100644
+--- a/misc-utils/Makemodule.am
++++ b/misc-utils/Makemodule.am
+@@ -77,7 +77,8 @@ if BUILD_UUIDD
+ usrsbin_exec_PROGRAMS += uuidd
+ dist_man_MANS += misc-utils/uuidd.8
+ uuidd_LDADD = $(LDADD) libuuid.la
+-uuidd_CFLAGS = $(AM_CFLAGS) -I$(ul_libuuid_incdir)
++uuidd_CFLAGS = $(DAEMON_CFLAGS) $(AM_CFLAGS) -I$(ul_libuuid_incdir)
++uuidd_LDFLAGS = $(DAEMON_LDFLAGS) $(AM_LDFLAGS)
+ uuidd_SOURCES = misc-utils/uuidd.c
+ if USE_SOCKET_ACTIVATION
+ uuidd_SOURCES += misc-utils/sd-daemon.c misc-utils/sd-daemon.h
+-- 
+2.7.4
+
diff --git a/SOURCES/0060-fdisk-backport-DOS-logical-partitions-chain-reorder.patch b/SOURCES/0060-fdisk-backport-DOS-logical-partitions-chain-reorder.patch
deleted file mode 100644
index 86e577c..0000000
--- a/SOURCES/0060-fdisk-backport-DOS-logical-partitions-chain-reorder.patch
+++ /dev/null
@@ -1,231 +0,0 @@
-From 81745b42a2722f11006c05de87ff90de08cf8cee Mon Sep 17 00:00:00 2001
-From: Karel Zak <kzak@redhat.com>
-Date: Tue, 28 Jun 2016 11:30:21 +0200
-Subject: [PATCH] fdisk: backport DOS logical partitions chain reorder
-
-... from the current upstream.
-
-Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1304246
-Signed-off-by: Karel Zak <kzak@redhat.com>
----
- fdisks/fdiskdoslabel.c | 170 +++++++++++++++++++++++++++++++++----------------
- include/c.h            |   8 +++
- 2 files changed, 124 insertions(+), 54 deletions(-)
-
-diff --git a/fdisks/fdiskdoslabel.c b/fdisks/fdiskdoslabel.c
-index fe04ac7..b7eb35a 100644
---- a/fdisks/fdiskdoslabel.c
-+++ b/fdisks/fdiskdoslabel.c
-@@ -55,6 +55,22 @@ static int MBRbuffer_changed;
- #define cround(c, n)	(fdisk_context_use_cylinders(c) ? \
- 				((n) / fdisk_context_get_units_per_sector(c)) + 1 : (n))
- 
-+
-+static unsigned long long
-+get_abs_partition_start(struct pte *pe)
-+{
-+	return pe->offset + get_start_sect(pe->part_table);
-+}
-+
-+static unsigned long long
-+get_abs_partition_end(struct pte *pe)
-+{
-+	unsigned long long size;
-+
-+	size = get_nr_sects(pe->part_table);
-+	return get_abs_partition_start(pe) + size - (size ? 1 : 0);
-+}
-+
- static void warn_alignment(struct fdisk_context *cxt)
- {
- 	if (nowarn)
-@@ -1254,67 +1270,113 @@ void dos_list_table_expert(struct fdisk_context *cxt, int extend)
- 	}
- }
- 
--/*
-- * Fix the chain of logicals.
-- * extended_offset is unchanged, the set of sectors used is unchanged
-- * The chain is sorted so that sectors increase, and so that
-- * starting sectors increase.
-- *
-- * After this it may still be that cfdisk doesn't like the table.
-- * (This is because cfdisk considers expanded parts, from link to
-- * end of partition, and these may still overlap.)
-- * Now
-- *   sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
-- * may help.
-- */
-+
-+static void print_chain_of_logicals(struct fdisk_context *cxt)
-+{
-+	size_t i;
-+
-+	fputc('\n', stdout);
-+
-+	for (i = 4; i < cxt->label->nparts_max; i++) {
-+		struct pte *pe = &ptes[i];
-+
-+		fprintf(stderr, "#%02zu EBR [%10ju], "
-+			"data[start=%10ju (%10ju), size=%10ju], "
-+			"link[start=%10ju (%10ju), size=%10ju]\n",
-+			i, (uintmax_t) pe->offset,
-+			/* data */
-+			(uintmax_t) get_start_sect(pe->part_table),
-+			(uintmax_t) get_abs_partition_start(pe),
-+			(uintmax_t) get_nr_sects(pe->part_table),
-+			/* link */
-+			(uintmax_t) get_start_sect(pe->ext_pointer),
-+			(uintmax_t) (extended_offset + get_start_sect(pe->ext_pointer)),
-+			(uintmax_t) get_nr_sects(pe->ext_pointer));
-+	}
-+}
-+
-+static int cmp_ebr_offsets(const void *a, const void *b)
-+{
-+	struct pte *ae = (struct pte *) a,
-+		   *be = (struct pte *) b;
-+
-+	if (ae->offset == 0 && be->offset == 0)
-+		return 0;
-+	if (ae->offset == 0)
-+		return 1;
-+	if (be->offset == 0)
-+		return -1;
-+
-+	return cmp_numbers(ae->offset, be->offset);
-+}
-+
- static void fix_chain_of_logicals(struct fdisk_context *cxt)
- {
--	size_t j, oj, ojj, sj, sjj;
--	struct partition *pj,*pjj,tmp;
--
--	/* Stage 1: sort sectors but leave sector of part 4 */
--	/* (Its sector is the global extended_offset.) */
-- stage1:
--	for (j = 5; j < cxt->label->nparts_max - 1; j++) {
--		oj = ptes[j].offset;
--		ojj = ptes[j+1].offset;
--		if (oj > ojj) {
--			ptes[j].offset = ojj;
--			ptes[j+1].offset = oj;
--			pj = ptes[j].part_table;
--			set_start_sect(pj, get_start_sect(pj)+oj-ojj);
--			pjj = ptes[j+1].part_table;
--			set_start_sect(pjj, get_start_sect(pjj)+ojj-oj);
--			set_start_sect(ptes[j-1].ext_pointer,
--				       ojj-extended_offset);
--			set_start_sect(ptes[j].ext_pointer,
--				       oj-extended_offset);
--			goto stage1;
-+	struct pte *last;
-+	size_t i;
-+
-+	DBG(CONTEXT, print_chain_of_logicals(cxt));
-+
-+	/* Sort chain by EBR offsets */
-+	qsort(&ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte),
-+			cmp_ebr_offsets);
-+
-+again:
-+	/* Sort data partitions by start */
-+	for (i = 4; i < cxt->label->nparts_max - 1; i++) {
-+		struct pte *cur = &ptes[i],
-+			   *nxt = &ptes[i + 1];
-+
-+		if (get_abs_partition_start(cur) >
-+		    get_abs_partition_start(nxt)) {
-+
-+			struct partition tmp = *cur->part_table;
-+			sector_t cur_start = get_abs_partition_start(cur),
-+				 nxt_start = get_abs_partition_start(nxt);
-+
-+			/* swap data partitions */
-+			*cur->part_table = *nxt->part_table;
-+			*nxt->part_table = tmp;
-+
-+			/* Recount starts according to EBR offsets, the absolute
-+			 * address still has to be the same! */
-+			set_start_sect(cur->part_table, nxt_start - cur->offset);
-+			set_start_sect(nxt->part_table, cur_start - nxt->offset);
-+
-+			cur->changed = 1;
-+			nxt->changed = 1;
-+			goto again;
- 		}
- 	}
- 
--	/* Stage 2: sort starting sectors */
-- stage2:
--	for (j = 4; j < cxt->label->nparts_max - 1; j++) {
--		pj = ptes[j].part_table;
--		pjj = ptes[j+1].part_table;
--		sj = get_start_sect(pj);
--		sjj = get_start_sect(pjj);
--		oj = ptes[j].offset;
--		ojj = ptes[j+1].offset;
--		if (oj+sj > ojj+sjj) {
--			tmp = *pj;
--			*pj = *pjj;
--			*pjj = tmp;
--			set_start_sect(pj, ojj+sjj-oj);
--			set_start_sect(pjj, oj+sj-ojj);
--			goto stage2;
--		}
-+	/* Update EBR links */
-+	for (i = 4; i < cxt->label->nparts_max - 1; i++) {
-+		struct pte *cur = &ptes[i],
-+			   *nxt = &ptes[i + 1];
-+
-+		sector_t noff = nxt->offset - extended_offset,
-+		         ooff = get_start_sect(cur->ext_pointer);
-+
-+		if (noff == ooff)
-+			continue;
-+
-+		DBG(CONTEXT, dbgprint("DOS: fix EBR [%10ju] link %ju -> %ju",
-+			(uintmax_t) cur->offset,
-+			(uintmax_t) ooff, (uintmax_t) noff));
-+
-+		set_partition(cxt, i, 1, nxt->offset,
-+				get_abs_partition_end(nxt),
-+				EXTENDED);
-+	}
-+
-+	/* always terminate the chain ! */
-+	last = &ptes[cxt->label->nparts_max - 1];
-+	if (last) {
-+		clear_partition(last->ext_pointer);
-+		last->changed = 1;
- 	}
- 
--	/* Probably something was changed */
--	for (j = 4; j < cxt->label->nparts_max; j++)
--		ptes[j].changed = 1;
-+	DBG(CONTEXT, print_chain_of_logicals(cxt));
- }
- 
- void dos_fix_partition_table_order(struct fdisk_context *cxt)
-diff --git a/include/c.h b/include/c.h
-index a50e8a5..ef2ea69 100644
---- a/include/c.h
-+++ b/include/c.h
-@@ -110,6 +110,14 @@
- 	_max1 > _max2 ? _max1 : _max2; })
- #endif
- 
-+#ifndef cmp_numbers
-+# define cmp_numbers(x, y) __extension__ ({	\
-+	__typeof__(x) _a = (x);			\
-+	__typeof__(y) _b = (y);			\
-+	(void) (&_a == &_b);			\
-+	_a == _b ? 0 : _a > _b ? 1 : -1; })
-+#endif
-+
- #ifndef offsetof
- #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
- #endif
--- 
-2.7.4
-
diff --git a/SOURCES/0061-libmount-be-more-restrictive-about-valid-tag-names.patch b/SOURCES/0061-libmount-be-more-restrictive-about-valid-tag-names.patch
new file mode 100644
index 0000000..7e5d394
--- /dev/null
+++ b/SOURCES/0061-libmount-be-more-restrictive-about-valid-tag-names.patch
@@ -0,0 +1,183 @@
+From f5ef29a5b5c51fe2039352dabcc4946fa2f55861 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Tue, 2 Jul 2013 10:46:10 +0200
+Subject: [PATCH 61/84] libmount: be more restrictive about valid tag names
+
+ # mount DUMMY=filename.img /mnt
+
+The 'DUMMY=filename.img' is a filename and should not be
+interpreted as tag name. The valid tag names are LABEL, UUID,
+PARTLABEL and PARTUUID only.
+
+Upstream: http://github.com/karelzak/util-linux/commit/2c6b25f01802808b142d450af3352605720899da
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1248003
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ libmount/src/cache.c  | 29 ++++++++++++-----------------
+ libmount/src/fs.c     |  9 ++++++---
+ libmount/src/mountP.h |  2 ++
+ libmount/src/tab.c    | 20 ++++++++------------
+ libmount/src/utils.c  | 12 ++++++++++++
+ 5 files changed, 40 insertions(+), 32 deletions(-)
+
+diff --git a/libmount/src/cache.c b/libmount/src/cache.c
+index 7b65122..43a4daf 100644
+--- a/libmount/src/cache.c
++++ b/libmount/src/cache.c
+@@ -583,22 +583,18 @@ error:
+ char *mnt_resolve_spec(const char *spec, struct libmnt_cache *cache)
+ {
+ 	char *cn = NULL;
++	char *t = NULL, *v = NULL;
+ 
+ 	if (!spec)
+ 		return NULL;
+ 
+-	if (strchr(spec, '=')) {
+-		char *tag, *val;
+-
+-		if (!blkid_parse_tag_string(spec, &tag, &val)) {
+-			cn = mnt_resolve_tag(tag, val, cache);
+-
+-			free(tag);
+-			free(val);
+-		}
+-	} else
++	if (blkid_parse_tag_string(spec, &t, &v) == 0 && mnt_valid_tagname(t))
++		cn = mnt_resolve_tag(t, v, cache);
++	else
+ 		cn = mnt_resolve_path(spec, cache);
+ 
++	free(t);
++	free(v);
+ 	return cn;
+ }
+ 
+@@ -663,6 +659,7 @@ int test_read_tags(struct libmnt_test *ts, int argc, char *argv[])
+ 
+ 	while(fgets(line, sizeof(line), stdin)) {
+ 		size_t sz = strlen(line);
++		char *t = NULL, *v = NULL;
+ 
+ 		if (sz > 0 && line[sz - 1] == '\n')
+ 			line[sz - 1] = '\0';
+@@ -674,16 +671,14 @@ int test_read_tags(struct libmnt_test *ts, int argc, char *argv[])
+ 			if (mnt_cache_read_tags(cache, line) < 0)
+ 				fprintf(stderr, "%s: read tags failed\n", line);
+ 
+-		} else if (strchr(line, '=')) {
+-			char *tag, *val;
++		} else if (blkid_parse_tag_string(line, &t, &v) == 0) {
+ 			const char *cn = NULL;
+ 
+-			if (!blkid_parse_tag_string(line, &tag, &val)) {
+-				cn = cache_find_tag(cache, tag, val);
++			if (mnt_valid_tagname(t))
++				cn = cache_find_tag(cache, t, v);
++			free(t);
++			free(v);
+ 
+-				free(tag);
+-				free(val);
+-			}
+ 			if (cn)
+ 				printf("%s: %s\n", line, cn);
+ 			else
+diff --git a/libmount/src/fs.c b/libmount/src/fs.c
+index c95cdc7..75e3bbb 100644
+--- a/libmount/src/fs.c
++++ b/libmount/src/fs.c
+@@ -318,9 +318,12 @@ int __mnt_fs_set_source_ptr(struct libmnt_fs *fs, char *source)
+ 
+ 	assert(fs);
+ 
+-	if (source && *source != '/' && strchr(source, '=')) {
+-		if (blkid_parse_tag_string(source, &t, &v) != 0)
+-			return -1;
++	if (source && blkid_parse_tag_string(source, &t, &v) == 0 &&
++	    !mnt_valid_tagname(t)) {
++		/* parsable but unknown tag -- ignore */
++		free(t);
++		free(v);
++		t = v = NULL;
+ 	}
+ 
+ 	if (fs->source != source)
+diff --git a/libmount/src/mountP.h b/libmount/src/mountP.h
+index e064a68..7b0848f 100644
+--- a/libmount/src/mountP.h
++++ b/libmount/src/mountP.h
+@@ -136,6 +136,8 @@ extern int startswith(const char *s, const char *sx)
+ 
+ extern char *stripoff_last_component(char *path);
+ 
++extern int mnt_valid_tagname(const char *tagname);
++
+ extern int is_file_empty(const char *name);
+ 
+ extern int mkdir_p(const char *path, mode_t mode);
+diff --git a/libmount/src/tab.c b/libmount/src/tab.c
+index e3524a8..1ba1eec 100644
+--- a/libmount/src/tab.c
++++ b/libmount/src/tab.c
+@@ -801,7 +801,8 @@ struct libmnt_fs *mnt_table_find_tag(struct libmnt_table *tb, const char *tag,
+ struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb,
+ 					const char *source, int direction)
+ {
+-	struct libmnt_fs *fs = NULL;
++	struct libmnt_fs *fs;
++	char *t = NULL, *v = NULL;
+ 
+ 	assert(tb);
+ 
+@@ -812,18 +813,13 @@ struct libmnt_fs *mnt_table_find_source(struct libmnt_table *tb,
+ 
+ 	DBG(TAB, mnt_debug_h(tb, "lookup SOURCE: '%s'", source));
+ 
+-	if (source && *source && strchr(source, '=')) {
+-		char *tag, *val;
+-
+-		if (blkid_parse_tag_string(source, &tag, &val) == 0) {
+-
+-			fs = mnt_table_find_tag(tb, tag, val, direction);
+-
+-			free(tag);
+-			free(val);
+-		}
+-	} else
++	if (blkid_parse_tag_string(source, &t, &v) || !mnt_valid_tagname(t))
+ 		fs = mnt_table_find_srcpath(tb, source, direction);
++	else
++		fs = mnt_table_find_tag(tb, t, v, direction);
++
++	free(t);
++	free(v);
+ 
+ 	return fs;
+ }
+diff --git a/libmount/src/utils.c b/libmount/src/utils.c
+index 6a444ad..9305bb8 100644
+--- a/libmount/src/utils.c
++++ b/libmount/src/utils.c
+@@ -65,6 +65,18 @@ int is_file_empty(const char *name)
+ 	return (stat(name, &st) != 0 || st.st_size == 0);
+ }
+ 
++int mnt_valid_tagname(const char *tagname)
++{
++	if (tagname && *tagname && (
++	    strcmp("UUID", tagname) == 0 ||
++	    strcmp("LABEL", tagname) == 0 ||
++	    strcmp("PARTUUID", tagname) == 0 ||
++	    strcmp("PARTLABEL", tagname) == 0))
++		return 1;
++
++	return 0;
++}
++
+ int mnt_parse_offset(const char *str, size_t len, uintmax_t *res)
+ {
+ 	char *p;
+-- 
+2.7.4
+
diff --git a/SOURCES/0062-mount-umount-swapon-fsck-lsblk-findmnt-ignore-malfor.patch b/SOURCES/0062-mount-umount-swapon-fsck-lsblk-findmnt-ignore-malfor.patch
new file mode 100644
index 0000000..5bfb445
--- /dev/null
+++ b/SOURCES/0062-mount-umount-swapon-fsck-lsblk-findmnt-ignore-malfor.patch
@@ -0,0 +1,172 @@
+From 28663e752e125da99f8636ea0227d168f1e0e6aa Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Thu, 15 Oct 2015 11:53:44 +0200
+Subject: [PATCH 62/84] mount, umount, swapon, fsck, lsblk, findmnt: ignore
+ malformed lines
+
+The libmount provides way how to deal with parsing errors in fstab --
+on error callback function is executed and according to the return
+libmount manipulate with the malformed line, possible are three
+states:
+
+  1/ fatal error; all file ignored              (callback rc < 0)
+  2/ recoverable error; malformed line ignored  (callback rc > 0)
+  3/ ignore the error                           (callback rc == 0)
+
+The 2/ is the default if no callback specified.
+
+Unfortunately our utils uses 3/. The correct way is to use 2/.
+
+Upstream: http://github.com/karelzak/util-linux/commit/1cd9d0d7463850ef6b16a78b8a55e56dbf9a8db1
+Upstream: http://github.com/karelzak/util-linux/commit/1bb02a2da9f1bf7d80b352d540b29371099ab570
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1271850
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ disk-utils/fsck.c         |  2 +-
+ libmount/src/tab_parse.c  |  2 +-
+ misc-utils/findmnt.c      |  2 +-
+ misc-utils/lsblk.c        | 11 +++++++++++
+ sys-utils/mount.c         |  2 +-
+ sys-utils/swapon-common.c | 11 +++++++++++
+ sys-utils/umount.c        |  2 +-
+ 7 files changed, 27 insertions(+), 5 deletions(-)
+
+diff --git a/disk-utils/fsck.c b/disk-utils/fsck.c
+index 6e3a2c0..3ef8e5b 100644
+--- a/disk-utils/fsck.c
++++ b/disk-utils/fsck.c
+@@ -421,7 +421,7 @@ static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)),
+ 			const char *filename, int line)
+ {
+ 	warnx(_("%s: parse error at line %d -- ignore"), filename, line);
+-	return 0;
++	return 1;
+ }
+ 
+ /*
+diff --git a/libmount/src/tab_parse.c b/libmount/src/tab_parse.c
+index e930fd8..987e671 100644
+--- a/libmount/src/tab_parse.c
++++ b/libmount/src/tab_parse.c
+@@ -540,7 +540,7 @@ int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filenam
+ 		}
+ 		if (rc) {
+ 			mnt_free_fs(fs);
+-			if (rc == 1)
++			if (rc > 0)
+ 				continue;	/* recoverable error */
+ 			if (feof(f))
+ 				break;
+diff --git a/misc-utils/findmnt.c b/misc-utils/findmnt.c
+index 615ba08..f16da91 100644
+--- a/misc-utils/findmnt.c
++++ b/misc-utils/findmnt.c
+@@ -752,7 +752,7 @@ static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)),
+ 			const char *filename, int line)
+ {
+ 	warnx(_("%s: parse error at line %d"), filename, line);
+-	return 0;
++	return 1;
+ }
+ 
+ static char **append_tabfile(char **files, int *nfiles, char *filename)
+diff --git a/misc-utils/lsblk.c b/misc-utils/lsblk.c
+index 9e12a90..cd28c1d 100644
+--- a/misc-utils/lsblk.c
++++ b/misc-utils/lsblk.c
+@@ -337,6 +337,15 @@ static char *get_device_path(struct blkdev_cxt *cxt)
+ 	return xstrdup(path);
+ }
+ 
++static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)),
++			const char *filename, int line)
++{
++	if (filename)
++		warnx(_("%s: parse error: ignore entry at line %d."),
++							filename, line);
++	return 1;
++}
++
+ static int is_active_swap(const char *filename)
+ {
+ 	if (!swaps) {
+@@ -346,6 +355,7 @@ static int is_active_swap(const char *filename)
+ 		if (!mntcache)
+ 			mntcache = mnt_new_cache();
+ 
++		mnt_table_set_parser_errcb(swaps, table_parser_errcb);
+ 		mnt_table_set_cache(swaps, mntcache);
+ 		mnt_table_parse_swaps(swaps, NULL);
+ 	}
+@@ -368,6 +378,7 @@ static char *get_device_mountpoint(struct blkdev_cxt *cxt)
+ 		if (!mntcache)
+ 			mntcache = mnt_new_cache();
+ 
++		mnt_table_set_parser_errcb(mtab, table_parser_errcb);
+ 		mnt_table_set_cache(mtab, mntcache);
+ 		mnt_table_parse_mtab(mtab, NULL);
+ 	}
+diff --git a/sys-utils/mount.c b/sys-utils/mount.c
+index 0998b01..f332070 100644
+--- a/sys-utils/mount.c
++++ b/sys-utils/mount.c
+@@ -101,7 +101,7 @@ static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)
+ 	if (filename)
+ 		warnx(_("%s: parse error: ignore entry at line %d."),
+ 							filename, line);
+-	return 0;
++	return 1;
+ }
+ 
+ /*
+diff --git a/sys-utils/swapon-common.c b/sys-utils/swapon-common.c
+index 5c95ef3..5f14ddb 100644
+--- a/sys-utils/swapon-common.c
++++ b/sys-utils/swapon-common.c
+@@ -11,12 +11,22 @@ static struct libmnt_table *swaps, *fstab;
+ 
+ struct libmnt_cache *mntcache;
+ 
++static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)),
++			const char *filename, int line)
++{
++	if (filename)
++		warnx(_("%s: parse error: ignore entry at line %d."),
++							filename, line);
++	return 1;
++}
++
+ struct libmnt_table *get_fstab(void)
+ {
+ 	if (!fstab) {
+ 		fstab = mnt_new_table();
+ 		if (!fstab)
+ 			return NULL;
++		mnt_table_set_parser_errcb(fstab, table_parser_errcb);
+ 		mnt_table_set_cache(fstab, mntcache);
+ 		if (mnt_table_parse_fstab(fstab, NULL) != 0)
+ 			return NULL;
+@@ -32,6 +42,7 @@ struct libmnt_table *get_swaps(void)
+ 		if (!swaps)
+ 			return NULL;
+ 		mnt_table_set_cache(swaps, mntcache);
++		mnt_table_set_parser_errcb(swaps, table_parser_errcb);
+ 		if (mnt_table_parse_swaps(swaps, NULL) != 0)
+ 			return NULL;
+ 	}
+diff --git a/sys-utils/umount.c b/sys-utils/umount.c
+index 1bd275f..9c47744 100644
+--- a/sys-utils/umount.c
++++ b/sys-utils/umount.c
+@@ -45,7 +45,7 @@ static int table_parser_errcb(struct libmnt_table *tb __attribute__((__unused__)
+ 	if (filename)
+ 		warnx(_("%s: parse error: ignore entry at line %d."),
+ 							filename, line);
+-	return 0;
++	return 1;
+ }
+ 
+ 
+-- 
+2.7.4
+
diff --git a/SOURCES/0063-login-mount-fix-__SC_GETPW_R_SIZE_MAX-usage.patch b/SOURCES/0063-login-mount-fix-__SC_GETPW_R_SIZE_MAX-usage.patch
new file mode 100644
index 0000000..73f80e0
--- /dev/null
+++ b/SOURCES/0063-login-mount-fix-__SC_GETPW_R_SIZE_MAX-usage.patch
@@ -0,0 +1,146 @@
+From 3d1333293ef48117060cd4e285e9c49a6d061e83 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Tue, 15 Dec 2015 12:25:56 +0100
+Subject: [PATCH 63/84] login, mount: fix __SC_GETPW_R_SIZE_MAX usage
+
+sysconf(_SC_GETPW_R_SIZE_MAX) returns initial suggested size for pwd
+buffer (see getpwnam_r man page or POSIX). This is not large enough in
+some cases.
+
+Yes, this sysconf option is misnamed (should be _SC_GETPW_R_SIZE_MIN).
+
+Upstream: http://github.com/karelzak/util-linux/commit/f7ac9e71b18fa7314151f2ab65ee0bdd2ea89c07
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1290689
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ include/c.h          |  7 +++++++
+ libmount/src/utils.c | 25 ++++++-------------------
+ login-utils/login.c  | 12 ++----------
+ 3 files changed, 15 insertions(+), 29 deletions(-)
+
+diff --git a/include/c.h b/include/c.h
+index a50e8a5..7b59ce8 100644
+--- a/include/c.h
++++ b/include/c.h
+@@ -300,4 +300,11 @@ static inline int usleep(useconds_t usec)
+ # define SEEK_HOLE	4
+ #endif
+ 
++/*
++ * Note that sysconf(_SC_GETPW_R_SIZE_MAX) returns *initial* suggested size for
++ * pwd buffer and in some cases it is not large enough. See POSIX and
++ * getpwnam_r man page for more details.
++ */
++#define UL_GETPW_BUFSIZ	(16 * 1024)
++
+ #endif /* UTIL_LINUX_C_H */
+diff --git a/libmount/src/utils.c b/libmount/src/utils.c
+index 9305bb8..7c6f5b1 100644
+--- a/libmount/src/utils.c
++++ b/libmount/src/utils.c
+@@ -538,16 +538,6 @@ int mnt_get_filesystems(char ***filesystems, const char *pattern)
+ 	return rc;
+ }
+ 
+-static size_t get_pw_record_size(void)
+-{
+-#ifdef _SC_GETPW_R_SIZE_MAX
+-	long sz = sysconf(_SC_GETPW_R_SIZE_MAX);
+-	if (sz > 0)
+-		return sz;
+-#endif
+-	return 16384;
+-}
+-
+ /*
+  * Returns allocated string with username or NULL.
+  */
+@@ -555,14 +545,13 @@ char *mnt_get_username(const uid_t uid)
+ {
+         struct passwd pwd;
+ 	struct passwd *res;
+-	size_t sz = get_pw_record_size();
+ 	char *buf, *username = NULL;
+ 
+-	buf = malloc(sz);
++	buf = malloc(UL_GETPW_BUFSIZ);
+ 	if (!buf)
+ 		return NULL;
+ 
+-	if (!getpwuid_r(uid, &pwd, buf, sz, &res) && res)
++	if (!getpwuid_r(uid, &pwd, buf, UL_GETPW_BUFSIZ, &res) && res)
+ 		username = strdup(pwd.pw_name);
+ 
+ 	free(buf);
+@@ -574,17 +563,16 @@ int mnt_get_uid(const char *username, uid_t *uid)
+ 	int rc = -1;
+         struct passwd pwd;
+ 	struct passwd *pw;
+-	size_t sz = get_pw_record_size();
+ 	char *buf;
+ 
+ 	if (!username || !uid)
+ 		return -EINVAL;
+ 
+-	buf = malloc(sz);
++	buf = malloc(UL_GETPW_BUFSIZ);
+ 	if (!buf)
+ 		return -ENOMEM;
+ 
+-	if (!getpwnam_r(username, &pwd, buf, sz, &pw) && pw) {
++	if (!getpwnam_r(username, &pwd, buf, UL_GETPW_BUFSIZ, &pw) && pw) {
+ 		*uid= pw->pw_uid;
+ 		rc = 0;
+ 	} else {
+@@ -602,17 +590,16 @@ int mnt_get_gid(const char *groupname, gid_t *gid)
+ 	int rc = -1;
+         struct group grp;
+ 	struct group *gr;
+-	size_t sz = get_pw_record_size();
+ 	char *buf;
+ 
+ 	if (!groupname || !gid)
+ 		return -EINVAL;
+ 
+-	buf = malloc(sz);
++	buf = malloc(UL_GETPW_BUFSIZ);
+ 	if (!buf)
+ 		return -ENOMEM;
+ 
+-	if (!getgrnam_r(groupname, &grp, buf, sz, &gr) && gr) {
++	if (!getgrnam_r(groupname, &grp, buf, UL_GETPW_BUFSIZ, &gr) && gr) {
+ 		*gid= gr->gr_gid;
+ 		rc = 0;
+ 	} else {
+diff --git a/login-utils/login.c b/login-utils/login.c
+index a59dd3a..e0e960f 100644
+--- a/login-utils/login.c
++++ b/login-utils/login.c
+@@ -671,22 +671,14 @@ static struct passwd *get_passwd_entry(const char *username,
+ 					 struct passwd *pwd)
+ {
+ 	struct passwd *res = NULL;
+-	size_t sz = 16384;
+ 	int x;
+ 
+ 	if (!pwdbuf || !username)
+ 		return NULL;
+ 
+-#ifdef _SC_GETPW_R_SIZE_MAX
+-	{
+-		long xsz = sysconf(_SC_GETPW_R_SIZE_MAX);
+-		if (xsz > 0)
+-			sz = (size_t) xsz;
+-	}
+-#endif
+-	*pwdbuf = xrealloc(*pwdbuf, sz);
++	*pwdbuf = xrealloc(*pwdbuf, UL_GETPW_BUFSIZ);
+ 
+-	x = getpwnam_r(username, pwd, *pwdbuf, sz, &res);
++	x = getpwnam_r(username, pwd, *pwdbuf, UL_GETPW_BUFSIZ, &res);
+ 	if (!res) {
+ 		errno = x;
+ 		return NULL;
+-- 
+2.7.4
+
diff --git a/SOURCES/0064-bash-completion-use-n-as-IFS-when-ask-for-filenames.patch b/SOURCES/0064-bash-completion-use-n-as-IFS-when-ask-for-filenames.patch
new file mode 100644
index 0000000..6e63208
--- /dev/null
+++ b/SOURCES/0064-bash-completion-use-n-as-IFS-when-ask-for-filenames.patch
@@ -0,0 +1,623 @@
+From 28907c1a50132c9a308b8d2c22bf4905041c446f Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Mon, 30 Sep 2013 15:49:00 +0200
+Subject: [PATCH 64/84] bash-completion: use '\n' as IFS when ask for filenames
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The bash completion for more(1) treats the space-separated pieces of
+filenames as different files.
+
+	$ touch foo\ bar
+	$ more foo<TAB>
+	bar foo
+
+Upstream: http://github.com/karelzak/util-linux/commit/ce3e6b15e2c4478b2df9a7016c168b16325abfb0
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1296366
+Reported-by: Ángel González <ingenit@zoho.com>
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ bash-completion/blkid        | 1 +
+ bash-completion/colcrt       | 1 +
+ bash-completion/column       | 1 +
+ bash-completion/cytune       | 1 +
+ bash-completion/dmesg        | 1 +
+ bash-completion/fallocate    | 1 +
+ bash-completion/findmnt      | 1 +
+ bash-completion/flock        | 1 +
+ bash-completion/fsck.cramfs  | 1 +
+ bash-completion/hexdump      | 1 +
+ bash-completion/hwclock      | 1 +
+ bash-completion/ionice       | 1 +
+ bash-completion/last         | 1 +
+ bash-completion/ldattach     | 1 +
+ bash-completion/logger       | 1 +
+ bash-completion/look         | 1 +
+ bash-completion/losetup      | 1 +
+ bash-completion/lscpu        | 1 +
+ bash-completion/lslocks      | 1 +
+ bash-completion/mcookie      | 1 +
+ bash-completion/mkfs.cramfs  | 1 +
+ bash-completion/mkswap       | 1 +
+ bash-completion/more         | 2 ++
+ bash-completion/mountpoint   | 1 +
+ bash-completion/namei        | 1 +
+ bash-completion/nsenter      | 1 +
+ bash-completion/pg           | 1 +
+ bash-completion/pivot_root   | 1 +
+ bash-completion/readprofile  | 1 +
+ bash-completion/rename       | 1 +
+ bash-completion/rev          | 1 +
+ bash-completion/script       | 1 +
+ bash-completion/scriptreplay | 1 +
+ bash-completion/setterm      | 1 +
+ bash-completion/sfdisk       | 1 +
+ bash-completion/su           | 1 +
+ bash-completion/swaplabel    | 1 +
+ bash-completion/tailf        | 1 +
+ bash-completion/tunelp       | 1 +
+ bash-completion/ul           | 1 +
+ bash-completion/utmpdump     | 1 +
+ bash-completion/uuidd        | 3 +++
+ bash-completion/wall         | 1 +
+ bash-completion/wdctl        | 1 +
+ bash-completion/whereis      | 1 +
+ 45 files changed, 48 insertions(+)
+
+diff --git a/bash-completion/blkid b/bash-completion/blkid
+index b439328..9f97dd9 100644
+--- a/bash-completion/blkid
++++ b/bash-completion/blkid
+@@ -6,6 +6,7 @@ _blkid_module()
+ 	prev="${COMP_WORDS[COMP_CWORD-1]}"
+ 	case $prev in
+ 		'-c')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -f -- $cur) )
+ 			return 0
+diff --git a/bash-completion/colcrt b/bash-completion/colcrt
+index f9e4c33..c66d7e6 100644
+--- a/bash-completion/colcrt
++++ b/bash-completion/colcrt
+@@ -19,6 +19,7 @@ _colcrt_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/column b/bash-completion/column
+index f5cb86b..3af8e73 100644
+--- a/bash-completion/column
++++ b/bash-completion/column
+@@ -30,6 +30,7 @@ _column_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/cytune b/bash-completion/cytune
+index 4f42838..f685c88 100644
+--- a/bash-completion/cytune
++++ b/bash-completion/cytune
+@@ -33,6 +33,7 @@ _cytune_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- ${cur:-"/dev/tty"}) )
+ 	return 0
+diff --git a/bash-completion/dmesg b/bash-completion/dmesg
+index 60ecc1a..eab41ba 100644
+--- a/bash-completion/dmesg
++++ b/bash-completion/dmesg
+@@ -6,6 +6,7 @@ _dmesg_module()
+ 	prev="${COMP_WORDS[COMP_CWORD-1]}"
+ 	case $prev in
+ 		'-F'|'--file')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -f -- $cur) )
+ 			return 0
+diff --git a/bash-completion/fallocate b/bash-completion/fallocate
+index 5007b60..2c6e4cb 100644
+--- a/bash-completion/fallocate
++++ b/bash-completion/fallocate
+@@ -20,6 +20,7 @@ _fallocate_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/findmnt b/bash-completion/findmnt
+index 9386d8f..3ed331a 100644
+--- a/bash-completion/findmnt
++++ b/bash-completion/findmnt
+@@ -18,6 +18,7 @@ _findmnt_module()
+ 			return 0
+ 			;;
+ 		'-F'|'--tab-file')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -f -- $cur) )
+ 			return 0
+diff --git a/bash-completion/flock b/bash-completion/flock
+index 8cd60d3..8e7f8b8 100644
+--- a/bash-completion/flock
++++ b/bash-completion/flock
+@@ -38,6 +38,7 @@ _flock_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- ${cur:-"/"}) )
+ 	return 0
+diff --git a/bash-completion/fsck.cramfs b/bash-completion/fsck.cramfs
+index 84f6f31..acf6564 100644
+--- a/bash-completion/fsck.cramfs
++++ b/bash-completion/fsck.cramfs
+@@ -6,6 +6,7 @@ _fsck.cramfs_module()
+ 	prev="${COMP_WORDS[COMP_CWORD-1]}"
+ 	case $prev in
+ 		'-x'|'--destination')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -o dirnames -- ${cur:-"/"}) )
+ 			return 0
+diff --git a/bash-completion/hexdump b/bash-completion/hexdump
+index 0c91187..c17bcae 100644
+--- a/bash-completion/hexdump
++++ b/bash-completion/hexdump
+@@ -28,6 +28,7 @@ _hexdump_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/hwclock b/bash-completion/hwclock
+index 0c4ebaf..de1ac20 100644
+--- a/bash-completion/hwclock
++++ b/bash-completion/hwclock
+@@ -6,6 +6,7 @@ _hwclock_module()
+ 	prev="${COMP_WORDS[COMP_CWORD-1]}"
+ 	case $prev in
+ 		'-f'|'--rtc'|'--adjfile')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -f -- $cur) )
+ 			return 0
+diff --git a/bash-completion/ionice b/bash-completion/ionice
+index 3a01c51..1b1c5fe 100644
+--- a/bash-completion/ionice
++++ b/bash-completion/ionice
+@@ -30,6 +30,7 @@ _ionice_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/last b/bash-completion/last
+index 493051e..c93be3d 100644
+--- a/bash-completion/last
++++ b/bash-completion/last
+@@ -6,6 +6,7 @@ _last_module()
+ 	prev="${COMP_WORDS[COMP_CWORD-1]}"
+ 	case $prev in
+ 		'-f')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -f -- $cur) )
+ 			return 0
+diff --git a/bash-completion/ldattach b/bash-completion/ldattach
+index 830142d..0b9d260 100644
+--- a/bash-completion/ldattach
++++ b/bash-completion/ldattach
+@@ -42,6 +42,7 @@ _ldattach_module()
+ 			return 0
+ 			;;
+ 		/*)
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -f -- $cur) )
+ 			return 0
+diff --git a/bash-completion/logger b/bash-completion/logger
+index f46be8a..963abc7 100644
+--- a/bash-completion/logger
++++ b/bash-completion/logger
+@@ -6,6 +6,7 @@ _logger_module()
+ 	prev="${COMP_WORDS[COMP_CWORD-1]}"
+ 	case $prev in
+ 		'-f'|'--file')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -f -- $cur) )
+ 			return 0
+diff --git a/bash-completion/look b/bash-completion/look
+index e8676ba..303a756 100644
+--- a/bash-completion/look
++++ b/bash-completion/look
+@@ -20,6 +20,7 @@ _look_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/losetup b/bash-completion/losetup
+index 75240b8..874c549 100644
+--- a/bash-completion/losetup
++++ b/bash-completion/losetup
+@@ -60,6 +60,7 @@ _losetup_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/lscpu b/bash-completion/lscpu
+index bce07c4..244b418 100644
+--- a/bash-completion/lscpu
++++ b/bash-completion/lscpu
+@@ -41,6 +41,7 @@ _lscpu_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/lslocks b/bash-completion/lslocks
+index 337d07e..c9cff2c 100644
+--- a/bash-completion/lslocks
++++ b/bash-completion/lslocks
+@@ -38,6 +38,7 @@ _lslocks_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/mcookie b/bash-completion/mcookie
+index eb3f54b..4345b6e 100644
+--- a/bash-completion/mcookie
++++ b/bash-completion/mcookie
+@@ -6,6 +6,7 @@ _mcookie_module()
+ 	prev="${COMP_WORDS[COMP_CWORD-1]}"
+ 	case $prev in
+ 		'-f'|'--file')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -f -- $cur) )
+ 			return 0
+diff --git a/bash-completion/mkfs.cramfs b/bash-completion/mkfs.cramfs
+index 38ab3b6..af74e12 100644
+--- a/bash-completion/mkfs.cramfs
++++ b/bash-completion/mkfs.cramfs
+@@ -36,6 +36,7 @@ _mkfs.cramfs_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/mkswap b/bash-completion/mkswap
+index c411b30..b72efaf 100644
+--- a/bash-completion/mkswap
++++ b/bash-completion/mkswap
+@@ -28,6 +28,7 @@ _mkswap_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/more b/bash-completion/more
+index 945c7b3..809cea2 100644
+--- a/bash-completion/more
++++ b/bash-completion/more
+@@ -21,6 +21,8 @@ _more_module()
+ 			return 0
+ 			;;
+ 	esac
++
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/mountpoint b/bash-completion/mountpoint
+index 8fe27b8..f74efc9 100644
+--- a/bash-completion/mountpoint
++++ b/bash-completion/mountpoint
+@@ -20,6 +20,7 @@ _mountpoint_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- ${cur:-"/"}) )
+ 	return 0
+diff --git a/bash-completion/namei b/bash-completion/namei
+index 63fb37a..6402735 100644
+--- a/bash-completion/namei
++++ b/bash-completion/namei
+@@ -16,6 +16,7 @@ _namei_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/nsenter b/bash-completion/nsenter
+index 2970b8d..268f378 100644
+--- a/bash-completion/nsenter
++++ b/bash-completion/nsenter
+@@ -40,6 +40,7 @@ _nsenter_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/pg b/bash-completion/pg
+index 9b1bad9..8fce130 100644
+--- a/bash-completion/pg
++++ b/bash-completion/pg
+@@ -25,6 +25,7 @@ _pg_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/pivot_root b/bash-completion/pivot_root
+index 961c883..95df4b4 100644
+--- a/bash-completion/pivot_root
++++ b/bash-completion/pivot_root
+@@ -11,6 +11,7 @@ _pivot_root_module()
+ 	esac
+ 	case $COMP_CWORD in
+ 		1|2)
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -o dirnames -- ${cur:-"/"}) )
+ 			;;
+diff --git a/bash-completion/readprofile b/bash-completion/readprofile
+index a5f45f5..bd265a6 100644
+--- a/bash-completion/readprofile
++++ b/bash-completion/readprofile
+@@ -6,6 +6,7 @@ _readprofile_module()
+ 	prev="${COMP_WORDS[COMP_CWORD-1]}"
+ 	case $prev in
+ 		'-m'|'--mapfile'|'-p'|'--profile')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -f -- $cur) )
+ 			return 0
+diff --git a/bash-completion/rename b/bash-completion/rename
+index 3842c4d..81365a5 100644
+--- a/bash-completion/rename
++++ b/bash-completion/rename
+@@ -24,6 +24,7 @@ _rename_module()
+ 			COMPREPLY=( $(compgen -W "replacement" -- $cur) )
+ 			;;
+ 		*)
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -f -- $cur) )
+ 			;;
+diff --git a/bash-completion/rev b/bash-completion/rev
+index 619c5c4..e5397d5 100644
+--- a/bash-completion/rev
++++ b/bash-completion/rev
+@@ -16,6 +16,7 @@ _rev_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/script b/bash-completion/script
+index 329fc48..d7efd7e 100644
+--- a/bash-completion/script
++++ b/bash-completion/script
+@@ -32,6 +32,7 @@ _script_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/scriptreplay b/bash-completion/scriptreplay
+index 2ad7b11..a4aa8a0 100644
+--- a/bash-completion/scriptreplay
++++ b/bash-completion/scriptreplay
+@@ -24,6 +24,7 @@ _scriptreplay_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/setterm b/bash-completion/setterm
+index 7fa0a4e..a7ef6eb 100644
+--- a/bash-completion/setterm
++++ b/bash-completion/setterm
+@@ -45,6 +45,7 @@ _setterm_module()
+ 			return 0
+ 			;;
+ 		'-file')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -f -- $cur) )
+ 			return 0
+diff --git a/bash-completion/sfdisk b/bash-completion/sfdisk
+index 0226b04..609104c 100644
+--- a/bash-completion/sfdisk
++++ b/bash-completion/sfdisk
+@@ -14,6 +14,7 @@ _sfdisk_module()
+ 			return 0
+ 			;;
+ 		'-O'|'-I')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -f -- $cur) )
+ 			return 0
+diff --git a/bash-completion/su b/bash-completion/su
+index e739b56..dad1b5f 100644
+--- a/bash-completion/su
++++ b/bash-completion/su
+@@ -38,6 +38,7 @@ _su_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/swaplabel b/bash-completion/swaplabel
+index 093169e..08aa9cd 100644
+--- a/bash-completion/swaplabel
++++ b/bash-completion/swaplabel
+@@ -24,6 +24,7 @@ _swaplabel_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/tailf b/bash-completion/tailf
+index e3dd295..0d4c869 100644
+--- a/bash-completion/tailf
++++ b/bash-completion/tailf
+@@ -20,6 +20,7 @@ _tailf_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/tunelp b/bash-completion/tunelp
+index 614b235..bd2cce2 100644
+--- a/bash-completion/tunelp
++++ b/bash-completion/tunelp
+@@ -44,6 +44,7 @@ _tunelp_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- ${cur:-"/dev/lp"}) )
+ 	return 0
+diff --git a/bash-completion/ul b/bash-completion/ul
+index c00e510..449cbe0 100644
+--- a/bash-completion/ul
++++ b/bash-completion/ul
+@@ -25,6 +25,7 @@ _ul_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/utmpdump b/bash-completion/utmpdump
+index 3b868ce..7e4fd5a 100644
+--- a/bash-completion/utmpdump
++++ b/bash-completion/utmpdump
+@@ -16,6 +16,7 @@ _utmpdump_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/uuidd b/bash-completion/uuidd
+index c45b067..c8f6697 100644
+--- a/bash-completion/uuidd
++++ b/bash-completion/uuidd
+@@ -6,16 +6,19 @@ _uuidd_module()
+ 	prev="${COMP_WORDS[COMP_CWORD-1]}"
+ 	case $prev in
+ 		'-p'|'--pid'|'-s'|'--socket')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -f -- $cur) )
+ 			return 0
+ 			;;
+ 		'-T'|'--timeout')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -W "timeout" -- $cur) )
+ 			return 0
+ 			;;
+ 		'-n'|'--uuids')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -W "number" -- $cur) )
+ 			return 0
+diff --git a/bash-completion/wall b/bash-completion/wall
+index 55d9658..e3145ff 100644
+--- a/bash-completion/wall
++++ b/bash-completion/wall
+@@ -20,6 +20,7 @@ _wall_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- $cur) )
+ 	return 0
+diff --git a/bash-completion/wdctl b/bash-completion/wdctl
+index 33b4e1f..4f16e76 100644
+--- a/bash-completion/wdctl
++++ b/bash-completion/wdctl
+@@ -56,6 +56,7 @@ _wdctl_module()
+ 			return 0
+ 			;;
+ 	esac
++	local IFS=$'\n'
+ 	compopt -o filenames
+ 	COMPREPLY=( $(compgen -f -- ${cur:-"/dev/"}) )
+ 	return 0
+diff --git a/bash-completion/whereis b/bash-completion/whereis
+index 2273a07..0dcbac3 100644
+--- a/bash-completion/whereis
++++ b/bash-completion/whereis
+@@ -6,6 +6,7 @@ _whereis_module()
+ 	prev="${COMP_WORDS[COMP_CWORD-1]}"
+ 	case $prev in
+ 		'-B'|'-M'|'-S')
++			local IFS=$'\n'
+ 			compopt -o filenames
+ 			COMPREPLY=( $(compgen -o dirnames -- ${cur:-"/"}) )
+ 			return 0
+-- 
+2.7.4
+
diff --git a/SOURCES/0065-hwclock-change-audit-message.patch b/SOURCES/0065-hwclock-change-audit-message.patch
new file mode 100644
index 0000000..a50d8e2
--- /dev/null
+++ b/SOURCES/0065-hwclock-change-audit-message.patch
@@ -0,0 +1,31 @@
+From 19b9d9197374c5811e32777ca70a32eef37a1fb0 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Thu, 7 Jan 2016 13:23:24 +0100
+Subject: [PATCH 65/84] hwclock: change audit message
+
+The preferred layout is name=value for audit messages.
+
+Upstream: http://github.com/karelzak/util-linux/commit/fbed7e09f826e7804e99522cc1dd3cf54c9cdb67
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1296521
+References: https://bugzilla.redhat.com/show_bug.cgi?id=1296278
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ sys-utils/hwclock.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sys-utils/hwclock.c b/sys-utils/hwclock.c
+index c7789c8..ac9294b 100644
+--- a/sys-utils/hwclock.c
++++ b/sys-utils/hwclock.c
+@@ -1939,7 +1939,7 @@ void __attribute__((__noreturn__)) hwaudit_exit(int status)
+ {
+ 	if (hwaudit_on) {
+ 		audit_log_user_message(hwaudit_fd, AUDIT_USYS_CONFIG,
+-				       "changing system time", NULL, NULL, NULL,
++				       "op=change-system-time", NULL, NULL, NULL,
+ 				       status ? 0 : 1);
+ 		close(hwaudit_fd);
+ 	}
+-- 
+2.7.4
+
diff --git a/SOURCES/0066-su-clean-up-groups-initialization.patch b/SOURCES/0066-su-clean-up-groups-initialization.patch
new file mode 100644
index 0000000..ebd0225
--- /dev/null
+++ b/SOURCES/0066-su-clean-up-groups-initialization.patch
@@ -0,0 +1,186 @@
+From b2a41801904c4b281a717dde7f5e146cbd4500b3 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Mon, 15 Feb 2016 13:55:37 +0100
+Subject: [PATCH 66/84] su: clean up groups initialization
+
+This patch does not change any su/runuser behaviour, code changes:
+
+* don't use huge groups[NGROUPS_MAX]; the array has 256k, but we need
+  it only occasionally when -G/-g specified.
+
+* the current code uses groups[0] for -g and the rest for -G, this patch adds
+  'gid' to remember -g argument to avoid memmove()
+
+* add function add_supp_group() to simplify su_main()
+
+* add note about -G and -g relation to the man pages (undocumented now)
+
+Upstream: http://github.com/karelzak/util-linux/commit/c619d3d167115990e9228b27851e0cc2faa8f936
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1304426
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ login-utils/runuser.1   |  5 ++--
+ login-utils/su-common.c | 68 +++++++++++++++++++++++++++----------------------
+ login-utils/su.1        |  5 ++--
+ 3 files changed, 44 insertions(+), 34 deletions(-)
+
+diff --git a/login-utils/runuser.1 b/login-utils/runuser.1
+index 7201ff0..d82dbb0 100644
+--- a/login-utils/runuser.1
++++ b/login-utils/runuser.1
+@@ -75,8 +75,9 @@ shell.
+ \fB\-g\fR, \fB\-\-group\fR=\fIgroup\fR\fR
+ specify the primary group, this option is allowed for root user only
+ .TP
+-\fB\-G\fR, \fB\-\-supp-group\fR=\fIgroup\fR\fR
+-specify a supplemental group, this option is allowed for root user only
++.BR \-G , " \-\-supp\-group" = \fIgroup
++Specify a supplemental group.  This option is available to the root user only.  The first specified
++supplementary group is also used as a primary group if the option \fB\-\-group\fR is unspecified.
+ .TP
+ \fB\-\fR, \fB\-l\fR, \fB\-\-login\fR
+ Starts the shell as login shell with an environment similar to a real
+diff --git a/login-utils/su-common.c b/login-utils/su-common.c
+index dd87804..d53d690 100644
+--- a/login-utils/su-common.c
++++ b/login-utils/su-common.c
+@@ -535,7 +535,7 @@ modify_environment (const struct passwd *pw, const char *shell)
+ /* Become the user and group(s) specified by PW.  */
+ 
+ static void
+-init_groups (const struct passwd *pw, gid_t *groups, int num_groups)
++init_groups (const struct passwd *pw, gid_t *groups, size_t num_groups)
+ {
+   int retval;
+ 
+@@ -707,6 +707,28 @@ evaluate_uid(void)
+   return (uid_t) 0 == ruid && ruid == euid ? 0 : 1;
+ }
+ 
++static gid_t
++add_supp_group(const char *name, gid_t **groups, size_t *ngroups)
++{
++  struct group *gr;
++
++  if (*ngroups >= NGROUPS_MAX)
++    errx(EXIT_FAILURE,
++	P_("specifying more than %d supplemental group is not possible",
++	   "specifying more than %d supplemental groups is not possible",
++	     NGROUPS_MAX - 1), NGROUPS_MAX - 1);
++
++  gr = getgrnam(name);
++  if (!gr)
++    errx(EXIT_FAILURE, _("group %s does not exist"), name);
++
++  *groups = xrealloc(*groups, sizeof(gid_t) * (*ngroups + 1));
++  (*groups)[*ngroups] = gr->gr_gid;
++  (*ngroups)++;
++
++  return gr->gr_gid;
++}
++
+ int
+ su_main (int argc, char **argv, int mode)
+ {
+@@ -717,10 +739,12 @@ su_main (int argc, char **argv, int mode)
+   char *shell = NULL;
+   struct passwd *pw;
+   struct passwd pw_copy;
+-  struct group *gr;
+-  gid_t groups[NGROUPS_MAX];
+-  int num_supp_groups = 0;
+-  int use_gid = 0;
++
++  gid_t *groups = NULL;
++  size_t ngroups = 0;
++  bool use_supp = false;
++  bool use_gid = false;
++  gid_t gid = 0;
+ 
+   static const struct option longopts[] = {
+     {"command", required_argument, NULL, 'c'},
+@@ -765,23 +789,13 @@ su_main (int argc, char **argv, int mode)
+ 	  break;
+ 
+ 	case 'g':
+-	  gr = getgrnam(optarg);
+-	  if (!gr)
+-	    errx(EXIT_FAILURE, _("group %s does not exist"), optarg);
+-	  use_gid = 1;
+-	  groups[0] = gr->gr_gid;
++	  use_gid = true;
++	  gid = add_supp_group(optarg, &groups, &ngroups);
+ 	  break;
+ 
+ 	case 'G':
+-	  num_supp_groups++;
+-	  if (num_supp_groups >= NGROUPS_MAX)
+-	     errx(EXIT_FAILURE,
+-		  _("can't specify more than %d supplemental groups"),
+-		  NGROUPS_MAX - 1);
+-	  gr = getgrnam(optarg);
+-	  if (!gr)
+-	    errx(EXIT_FAILURE, _("group %s does not exist"), optarg);
+-	  groups[num_supp_groups] = gr->gr_gid;
++	  use_supp = true;
++	  add_supp_group(optarg, &groups, &ngroups);
+ 	  break;
+ 
+ 	case 'l':
+@@ -852,7 +866,7 @@ su_main (int argc, char **argv, int mode)
+     break;
+   }
+ 
+-  if ((num_supp_groups || use_gid) && restricted)
++  if ((use_supp || use_gid) && restricted)
+     errx(EXIT_FAILURE, _("only root can specify alternative groups"));
+ 
+   logindefs_load_defaults = load_config;
+@@ -878,16 +892,10 @@ su_main (int argc, char **argv, int mode)
+ 			  : DEFAULT_SHELL);
+   endpwent ();
+ 
+-  if (num_supp_groups && !use_gid)
+-  {
+-    pw->pw_gid = groups[1];
+-    memmove (groups, groups + 1, sizeof(gid_t) * num_supp_groups);
+-  }
+-  else if (use_gid)
+-  {
++  if (use_supp && !use_gid)
+     pw->pw_gid = groups[0];
+-    num_supp_groups++;
+-  }
++  else if (use_gid)
++    pw->pw_gid = gid;
+ 
+   authenticate (pw);
+ 
+@@ -912,7 +920,7 @@ su_main (int argc, char **argv, int mode)
+     shell = xstrdup (shell ? shell : pw->pw_shell);
+   }
+ 
+-  init_groups (pw, groups, num_supp_groups);
++  init_groups (pw, groups, ngroups);
+ 
+   if (!simulate_login || command)
+     suppress_pam_info = 1;		/* don't print PAM info messages */
+diff --git a/login-utils/su.1 b/login-utils/su.1
+index eab1a6f..1f69868 100644
+--- a/login-utils/su.1
++++ b/login-utils/su.1
+@@ -62,8 +62,9 @@ shell.
+ \fB\-g\fR, \fB\-\-group\fR=\fIgroup\fR\fR
+ specify the primary group, this option is allowed for root user only
+ .TP
+-\fB\-G\fR, \fB\-\-supp-group\fR=\fIgroup\fR\fR
+-specify a supplemental group, this option is allowed for root user only
++.BR \-G , " \-\-supp\-group" = \fIgroup
++Specify a supplemental group.  This option is available to the root user only.  The first specified
++supplementary group is also used as a primary group if the option \fB\-\-group\fR is unspecified.
+ .TP
+ \fB\-\fR, \fB\-l\fR, \fB\-\-login\fR
+ Starts the shell as login shell with an environment similar to a real
+-- 
+2.7.4
+
diff --git a/SOURCES/0067-lscpu-Fix-model-and-model-name-on-Power-Systems.patch b/SOURCES/0067-lscpu-Fix-model-and-model-name-on-Power-Systems.patch
new file mode 100644
index 0000000..9bf633c
--- /dev/null
+++ b/SOURCES/0067-lscpu-Fix-model-and-model-name-on-Power-Systems.patch
@@ -0,0 +1,83 @@
+From 246ab88515fca389c02602521fc765d3e597fd7a Mon Sep 17 00:00:00 2001
+From: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
+Date: Mon, 14 Mar 2016 20:18:07 +0530
+Subject: [PATCH 67/84] lscpu: Fix model and model name on Power Systems
+
+On Power System, lspcu presently displays system model number instead of
+processor model name. 'model' tag in cpuinfo contains system model name,
+not processor model. Instead it uses 'cpu' tag for processor model name.
+Also it uses 'revision' tag for processor model.
+
+Fix lspcu so that it displays processor model number. Also display processor
+model name.
+
+cpuinfo output on Power System:
+  ...
+  ...
+
+  processor	: 127
+  cpu		: POWER8E (raw), altivec supported
+  clock		: 4322.000000MHz
+  revision	: 2.1 (pvr 004b 0201)
+
+  timebase	: 512000000
+  platform	: PowerNV
+  model		: 8286-42A
+  machine	: PowerNV 8286-42A
+  firmware	: OPAL
+
+Output without this patch:
+  Architecture:          ppc64le
+  Byte Order:            Little Endian
+  CPU(s):                128
+  On-line CPU(s) list:   0-127
+  Thread(s) per core:    8
+  Core(s) per socket:    4
+  Socket(s):             4
+  NUMA node(s):          4
+  Model:                 8286-42A
+  ...
+  ...
+
+Output with this patch:
+  Architecture:          ppc64le
+  Byte Order:            Little Endian
+  CPU(s):                128
+  On-line CPU(s) list:   0-127
+  Thread(s) per core:    8
+  Core(s) per socket:    4
+  Socket(s):             4
+  NUMA node(s):          4
+  Model:                 2.1 (pvr 004b 0201)
+  Model name:            POWER8E (raw), altivec supported
+  ...
+  ...
+
+Upstream: http://github.com/karelzak/util-linux/commit/3ac03fe4d20558b55635a048d7f2fb0f5e85ee2a
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1326615
+Signed-off-by: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
+---
+ sys-utils/lscpu.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c
+index 4af8176..68b15af 100644
+--- a/sys-utils/lscpu.c
++++ b/sys-utils/lscpu.c
+@@ -355,8 +355,13 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod)
+ 		else if (lookup(buf, "vendor_id", &desc->vendor)) ;
+ 		else if (lookup(buf, "family", &desc->family)) ;
+ 		else if (lookup(buf, "cpu family", &desc->family)) ;
++#if defined(__powerpc__) || defined(__powerpc64__)
++		else if (lookup(buf, "revision", &desc->model)) ;
++		else if (lookup(buf, "cpu", &desc->modelname)) ;
++#else
+ 		else if (lookup(buf, "model", &desc->model)) ;
+ 		else if (lookup(buf, "model name", &desc->modelname)) ;
++#endif
+ 		else if (lookup(buf, "stepping", &desc->stepping)) ;
+ 		else if (lookup(buf, "cpu MHz", &desc->mhz)) ;
+ 		else if (lookup(buf, "flags", &desc->flags)) ;		/* x86 */
+-- 
+2.7.4
+
diff --git a/SOURCES/0068-lscpu-use-cpu-and-revision-tag-if-available.patch b/SOURCES/0068-lscpu-use-cpu-and-revision-tag-if-available.patch
new file mode 100644
index 0000000..c1962e2
--- /dev/null
+++ b/SOURCES/0068-lscpu-use-cpu-and-revision-tag-if-available.patch
@@ -0,0 +1,105 @@
+From cd0d8ef86151d72a246d565844d4c0470feb6b20 Mon Sep 17 00:00:00 2001
+From: Ruediger Meier <ruediger.meier@ga-group.nl>
+Date: Wed, 16 Mar 2016 13:18:18 +0100
+Subject: [PATCH 68/84] lscpu: use cpu and revision tag if available
+
+Avoid ifdef which does not work with --sysroot. Our existing test
+dumps produce even better output now for ppc and sparc.
+
+The logic moved to the printing section.
+
+Upstream: http://github.com/karelzak/util-linux/commit/641350fe822e7f1ac10873dad9a364bdeaba8083
+Upstream: http://github.com/karelzak/util-linux/commit/86c4817e0ea02656ddb62fe27757a9fd4f13b2d3
+Upstream: http://github.com/karelzak/util-linux/commit/c95e3889725389e9d7e24d29c2a71b015959575f
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1326615
+CC: Vasant Hegde <hegdevasant@linux.vnet.ibm.com>
+Signed-off-by: Ruediger Meier <ruediger.meier@ga-group.nl>
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ sys-utils/lscpu.c                             | 17 ++++++++---------
+ tests/expected/lscpu/lscpu-ppc64-POWER7       |  3 ++-
+ tests/expected/lscpu/lscpu-ppc64-POWER7-64cpu |  3 ++-
+ 3 files changed, 12 insertions(+), 11 deletions(-)
+
+diff --git a/sys-utils/lscpu.c b/sys-utils/lscpu.c
+index 68b15af..7a00636 100644
+--- a/sys-utils/lscpu.c
++++ b/sys-utils/lscpu.c
+@@ -141,6 +141,8 @@ struct lscpu_desc {
+ 	char	*family;
+ 	char	*model;
+ 	char	*modelname;
++	char	*revision;  /* alternative for model (ppc) */
++	char	*cpu;       /* alternative for modelname (ppc, sparc) */
+ 	char	*virtflag;	/* virtualization flag (vmx, svm) */
+ 	char	*hypervisor;	/* hypervisor software */
+ 	int	hyper;		/* hypervisor vendor ID */
+@@ -355,13 +357,8 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod)
+ 		else if (lookup(buf, "vendor_id", &desc->vendor)) ;
+ 		else if (lookup(buf, "family", &desc->family)) ;
+ 		else if (lookup(buf, "cpu family", &desc->family)) ;
+-#if defined(__powerpc__) || defined(__powerpc64__)
+-		else if (lookup(buf, "revision", &desc->model)) ;
+-		else if (lookup(buf, "cpu", &desc->modelname)) ;
+-#else
+ 		else if (lookup(buf, "model", &desc->model)) ;
+ 		else if (lookup(buf, "model name", &desc->modelname)) ;
+-#endif
+ 		else if (lookup(buf, "stepping", &desc->stepping)) ;
+ 		else if (lookup(buf, "cpu MHz", &desc->mhz)) ;
+ 		else if (lookup(buf, "flags", &desc->flags)) ;		/* x86 */
+@@ -369,6 +366,8 @@ read_basicinfo(struct lscpu_desc *desc, struct lscpu_modifier *mod)
+ 		else if (lookup(buf, "type", &desc->flags)) ;		/* sparc64 */
+ 		else if (lookup(buf, "bogomips", &desc->bogomips)) ;
+ 		else if (lookup(buf, "bogomips per cpu", &desc->bogomips)) ; /* s390 */
++		else if (lookup(buf, "cpu", &desc->cpu)) ;
++		else if (lookup(buf, "revision", &desc->revision)) ;
+ 		else
+ 			continue;
+ 	}
+@@ -1264,10 +1263,10 @@ print_summary(struct lscpu_desc *desc, struct lscpu_modifier *mod)
+ 		print_s(_("Vendor ID:"), desc->vendor);
+ 	if (desc->family)
+ 		print_s(_("CPU family:"), desc->family);
+-	if (desc->model)
+-		print_s(_("Model:"), desc->model);
+-	if (desc->modelname)
+-		print_s(_("Model name:"), desc->modelname);
++	if (desc->model || desc->revision)
++		print_s(_("Model:"), desc->revision ? desc->revision : desc->model);
++	if (desc->modelname || desc->cpu)
++		print_s(_("Model name:"), desc->cpu ? desc->cpu : desc->modelname);
+ 	if (desc->stepping)
+ 		print_s(_("Stepping:"), desc->stepping);
+ 	if (desc->mhz)
+diff --git a/tests/expected/lscpu/lscpu-ppc64-POWER7 b/tests/expected/lscpu/lscpu-ppc64-POWER7
+index 0d6c5ba..9a3c0c9 100644
+--- a/tests/expected/lscpu/lscpu-ppc64-POWER7
++++ b/tests/expected/lscpu/lscpu-ppc64-POWER7
+@@ -4,7 +4,8 @@ Thread(s) per core:    4
+ Core(s) per socket:    1
+ Socket(s):             4
+ NUMA node(s):          1
+-Model:                 IBM,8233-E8B
++Model:                 2.1 (pvr 003f 0201)
++Model name:            POWER7 (architected), altivec supported
+ L1d cache:             32K
+ L1i cache:             32K
+ NUMA node0 CPU(s):     0-15
+diff --git a/tests/expected/lscpu/lscpu-ppc64-POWER7-64cpu b/tests/expected/lscpu/lscpu-ppc64-POWER7-64cpu
+index 40e2736..d4ae6c1 100644
+--- a/tests/expected/lscpu/lscpu-ppc64-POWER7-64cpu
++++ b/tests/expected/lscpu/lscpu-ppc64-POWER7-64cpu
+@@ -4,7 +4,8 @@ Thread(s) per core:    4
+ Core(s) per socket:    1
+ Socket(s):             16
+ NUMA node(s):          2
+-Model:                 IBM,8231-E2B
++Model:                 2.1 (pvr 003f 0201)
++Model name:            POWER7 (architected), altivec supported
+ L1d cache:             32K
+ L1i cache:             32K
+ NUMA node0 CPU(s):     0-63
+-- 
+2.7.4
+
diff --git a/SOURCES/0069-findfs-add-ability-to-work-with-PART-UUID-LABEL-too.patch b/SOURCES/0069-findfs-add-ability-to-work-with-PART-UUID-LABEL-too.patch
new file mode 100644
index 0000000..48c3db4
--- /dev/null
+++ b/SOURCES/0069-findfs-add-ability-to-work-with-PART-UUID-LABEL-too.patch
@@ -0,0 +1,137 @@
+From 2555bd3bad9ea8e7ae40a727f59bb546d2aa2717 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Fri, 28 Mar 2014 10:36:05 +0100
+Subject: [PATCH 69/84] findfs: add ability to work with PART{UUID,LABEL}= too
+
+Upstream: http://github.com/karelzak/util-linux/commit/c48508c2faa356c48c26d7d0070a6f20ae4ba9a0
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1335671
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ misc-utils/findfs.8 | 51 +++++++++++++++++++++++++++++++++++++++------------
+ misc-utils/findfs.c | 17 +++++------------
+ 2 files changed, 44 insertions(+), 24 deletions(-)
+
+diff --git a/misc-utils/findfs.8 b/misc-utils/findfs.8
+index 8a6bca1..b92cd45 100644
+--- a/misc-utils/findfs.8
++++ b/misc-utils/findfs.8
+@@ -7,19 +7,45 @@
+ findfs \- find a filesystem by label or UUID
+ .SH SYNOPSIS
+ .B findfs
+-.BI LABEL= label
+-.sp
+-.B findfs
+-.BI UUID= uuid
++.BI NAME= value
+ .SH DESCRIPTION
+ .B findfs
+-will search the disks in the system looking for a filesystem which has
+-a label matching
+-.I label
+-or a UUID equal to
+-.IR uuid .
+-If the filesystem is found, the device name for the filesystem will
+-be printed on stdout.
++will search the block devices in the system looking for a filesystem or
++partition with specified tag. The currently supported tags are:
++.TP
++.B LABEL=<label>
++Specifies filesystem label.
++.TP
++.B UUID=<uuid>
++Specifies filesystem UUID.
++.TP
++.B PARTUUID=<uuid>
++Specifies partition UUID. This partition identifier is supported for example for
++GUID  Partition  Table (GPT) partition tables.
++.TP
++.B PARTLABEL=<label>
++Specifies partition label (name). The partition labels are supported for example for
++GUID Partition Table (GPT) or MAC partition tables.
++.PP
++If the filesystem or partition is found, the device name will be printed on
++stdout.
++
++The complete overview about filesystems and partitions you can get for example
++by
++.RS
++
++.br
++.BI "lsblk \-\-fs"
++.br
++
++.BI "partx --show <disk>"
++.br
++
++.BI blkid
++.br
++
++.RE
++
+ .PP
+ .SH AUTHOR
+ .B findfs
+@@ -30,7 +56,8 @@ the util-linux package by Karel Zak (kzak@redhat.com).
+ enables debug output.
+ .SH SEE ALSO
+ .BR blkid (8),
+-.BR fsck (8)
++.BR lsblk (8),
++.BR partx (8)
+ .SH AVAILABILITY
+ The findfs command is part of the util-linux package and is available from
+ ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+diff --git a/misc-utils/findfs.c b/misc-utils/findfs.c
+index bc4a843..29ca1cb 100644
+--- a/misc-utils/findfs.c
++++ b/misc-utils/findfs.c
+@@ -19,8 +19,7 @@ static void __attribute__((__noreturn__)) usage(int rc)
+ {
+ 	FILE *out = rc ? stderr : stdout;
+ 	fputs(USAGE_HEADER, out);
+-	fprintf(out, _(" %1$s [options] LABEL=<label>\n"
+-		       " %1$s [options] UUID=<uuid>\n"),
++	fprintf(out, _(" %s [options] {LABEL,UUID,PARTUUID,PARTLABEL}=<value>\n"),
+ 		program_invocation_short_name);
+ 	fputs(USAGE_OPTIONS, out);
+ 	fputs(USAGE_HELP, out);
+@@ -31,7 +30,7 @@ static void __attribute__((__noreturn__)) usage(int rc)
+ 
+ int main(int argc, char **argv)
+ {
+-	char	*dev, *tk, *vl;
++	char	*dev;
+ 
+ 	setlocale(LC_ALL, "");
+ 	bindtextdomain(PACKAGE, LOCALEDIR);
+@@ -43,23 +42,17 @@ int main(int argc, char **argv)
+ 		 * with version from e2fsprogs */
+ 		usage(2);
+ 
+-	if (!strncmp(argv[1], "LABEL=", 6)) {
+-		tk = "LABEL";
+-		vl = argv[1] + 6;
+-	} else if (!strncmp(argv[1], "UUID=", 5)) {
+-		tk = "UUID";
+-		vl = argv[1] + 5;
+-	} else if (strcmp(argv[1], "-V") == 0 ||
++	if (strcmp(argv[1], "-V") == 0 ||
+ 		   strcmp(argv[1], "--version") == 0) {
+ 		printf(UTIL_LINUX_VERSION);
+ 		return EXIT_SUCCESS;
+ 	} else if (strcmp(argv[1], "-h") == 0 ||
+ 		   strcmp(argv[1], "--help") == 0) {
+ 		usage(EXIT_SUCCESS);
+-	} else
++	} else if (argv[1][0] == '-')
+ 		usage(2);
+ 
+-	dev = blkid_evaluate_tag(tk, vl, NULL);
++	dev = blkid_evaluate_tag(argv[1], NULL, NULL);
+ 	if (!dev)
+ 		errx(EXIT_FAILURE, _("unable to resolve '%s'"),	argv[1]);
+ 
+-- 
+2.7.4
+
diff --git a/SOURCES/0070-libblkid-fix-memory-leak-in-blkid_parse_tag_string.patch b/SOURCES/0070-libblkid-fix-memory-leak-in-blkid_parse_tag_string.patch
new file mode 100644
index 0000000..4453744
--- /dev/null
+++ b/SOURCES/0070-libblkid-fix-memory-leak-in-blkid_parse_tag_string.patch
@@ -0,0 +1,44 @@
+From 8af4232ff50154588c75f25a951b5619f05d0421 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Tue, 19 Nov 2013 17:52:56 +0100
+Subject: [PATCH 70/84] libblkid: fix memory leak in blkid_parse_tag_string()
+
+Upstream: http://github.com/karelzak/util-linux/commit/c1178175e7adb35388a3e34495974d7f24f45d5d
+Upstream: http://github.com/karelzak/util-linux/commit/aab691cf8deb4a53782a0317b6f96c5d8b61f8e9
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1335671
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ libblkid/src/tag.c | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/libblkid/src/tag.c b/libblkid/src/tag.c
+index 3a70950..3aede04 100644
+--- a/libblkid/src/tag.c
++++ b/libblkid/src/tag.c
+@@ -237,14 +237,18 @@ int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val)
+ 			goto errout; /* missing closing quote */
+ 		*cp = '\0';
+ 	}
+-	value = value && *value ? strdup(value) : NULL;
+-	if (!value)
+-		goto errout;
++
++	if (ret_val) {
++		value = *value ? strdup(value) : NULL;
++		if (!value)
++			goto errout;
++		*ret_val = value;
++	}
+ 
+ 	if (ret_type)
+ 		*ret_type = name;
+-	if (ret_val)
+-		*ret_val = value;
++	else
++		free(name);
+ 
+ 	return 0;
+ 
+-- 
+2.7.4
+
diff --git a/SOURCES/0071-findmnt-don-t-rely-on-st_dev-for-target.patch b/SOURCES/0071-findmnt-don-t-rely-on-st_dev-for-target.patch
new file mode 100644
index 0000000..7d1bc7f
--- /dev/null
+++ b/SOURCES/0071-findmnt-don-t-rely-on-st_dev-for-target.patch
@@ -0,0 +1,104 @@
+From 0f66811659aa8fd8b14ade8a80bfecd580962b2d Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Tue, 10 Mar 2015 12:51:44 +0100
+Subject: [PATCH 71/84] findmnt: don't rely on st_dev for --target
+
+The overlay filesystem does not provide usable st_dev (in traditional
+UNIX way). It's necessary to search in /proc/self/mountinfo to detect
+which path element is mountpoint.
+
+$ findmnt --target /mnt/merged/dir-a/foo
+TARGET      SOURCE  FSTYPE  OPTIONS
+/mnt/merged overlay overlay rw,relatime,lowerdir=/mnt/low,upperdir=/mnt/high/data,workdir=/mnt/high/work
+
+Upstream: http://github.com/karelzak/util-linux/commit/cd41b385a06dde70bb45c3143d3459157bda58f8
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=587393
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ libmount/src/utils.c |  6 +++++-
+ misc-utils/findmnt.8 |  8 ++++++--
+ misc-utils/findmnt.c | 17 +++++++++++------
+ 3 files changed, 22 insertions(+), 9 deletions(-)
+
+diff --git a/libmount/src/utils.c b/libmount/src/utils.c
+index 7c6f5b1..5783d88 100644
+--- a/libmount/src/utils.c
++++ b/libmount/src/utils.c
+@@ -859,7 +859,11 @@ int mnt_open_uniq_filename(const char *filename, char **name)
+  * This function finds the mountpoint that a given path resides in. @path
+  * should be canonicalized. The returned pointer should be freed by the caller.
+  *
+- * Returns: allocated string with target of the mounted device or NULL on error
++ * WARNING: the function compares st_dev of the @path elements. This traditional
++ * way maybe be insufficient on filesystems like Linux "overlay". See also
++ * mnt_table_find_target().
++ *
++ * Returns: allocated string with the target of the mounted device or NULL on error
+  */
+ char *mnt_get_mountpoint(const char *path)
+ {
+diff --git a/misc-utils/findmnt.8 b/misc-utils/findmnt.8
+index 407636e..54739b7 100644
+--- a/misc-utils/findmnt.8
++++ b/misc-utils/findmnt.8
+@@ -179,8 +179,12 @@ Search in
+ .IR /etc/fstab .
+ The output is in the list format (see \fB--list\fR).
+ .TP
+-.BR \-T , " \-\-target \fIdir\fP"
+-Explicitly define the mount target (mountpoint directory).
++.BR \-T , " \-\-target \fIpath\fP"
++Explicitly define the mount target (mountpoint directory). If the \fIpath\fR
++is not a mountpoint file or directory than
++.B findmnt
++checks \fIpath\fR elements in reverse order for get the mountpoint (this feature is 
++supported only if search in kernel files and unsupported for \fB\-\-fstab\fP).
+ .TP
+ .BR \-t , " \-\-types \fIlist\fP"
+ Limit the set of printed filesystems.  More than one type may be
+diff --git a/misc-utils/findmnt.c b/misc-utils/findmnt.c
+index f16da91..fe899db 100644
+--- a/misc-utils/findmnt.c
++++ b/misc-utils/findmnt.c
+@@ -236,9 +236,12 @@ static void set_source_match(const char *data)
+ 		set_match(COL_SOURCE, data);
+ }
+ 
+-static void enable_extra_target_match(void)
++/* @tb has to be from kernel (so no fstab or so)! */
++static void enable_extra_target_match(struct libmnt_table *tb)
+ {
+-	char *cn = NULL, *mnt = NULL;
++	char *cn = NULL;
++	const char *tgt = NULL, *mnt = NULL;
++	struct libmnt_fs *fs;
+ 
+ 	/*
+ 	 * Check if match pattern is mountpoint, if not use the
+@@ -248,9 +251,11 @@ static void enable_extra_target_match(void)
+ 	if (!cn)
+ 		return;
+ 
+-	mnt = mnt_get_mountpoint(cn);
+-	if (!mnt || strcmp(mnt, cn) == 0)
+-		return;
++	fs = mnt_table_find_mountpoint(tb, tgt, MNT_ITER_BACKWARD);
++	if (fs)
++		mnt = mnt_fs_get_target(fs);
++	if (mnt && strcmp(mnt, tgt) != 0)
++		set_match(COL_TARGET, xstrdup(mnt));	/* replace the current setting */
+ 
+ 	/* replace the current setting with the real mountpoint */
+ 	set_match(COL_TARGET, mnt);
+@@ -1484,7 +1489,7 @@ int main(int argc, char *argv[])
+ 			 * try it again with extra functionality for target
+ 			 * match
+ 			 */
+-			enable_extra_target_match();
++			enable_extra_target_match(tb);
+ 			rc = add_matching_lines(tb, tt, direction);
+ 		}
+ 	}
+-- 
+2.7.4
+
diff --git a/SOURCES/0072-libmount-cleanup-fs-root-detection-code.patch b/SOURCES/0072-libmount-cleanup-fs-root-detection-code.patch
new file mode 100644
index 0000000..b0b881d
--- /dev/null
+++ b/SOURCES/0072-libmount-cleanup-fs-root-detection-code.patch
@@ -0,0 +1,201 @@
+From bb3483bf21b9cbe462caaa74fbc03d2eb7845d74 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Tue, 10 Mar 2015 13:35:56 +0100
+Subject: [PATCH 72/84] libmount: cleanup fs root detection code
+
+Upstream: http://github.com/karelzak/util-linux/commit/cc06a01ec551ed2bcd397a5097165b4434179b34
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=587393
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ libmount/src/tab.c                            | 32 ++++++++++++++++++++-----
+ libmount/src/utils.c                          | 34 ---------------------------
+ tests/expected/libmount/utils-fs-root         |  1 -
+ tests/expected/libmount/utils-fs-root-subdir  |  1 -
+ tests/expected/libmount/utils-fs-root-subdir2 |  1 -
+ tests/ts/libmount/utils                       | 12 ----------
+ 6 files changed, 26 insertions(+), 55 deletions(-)
+ delete mode 100644 tests/expected/libmount/utils-fs-root
+ delete mode 100644 tests/expected/libmount/utils-fs-root-subdir
+ delete mode 100644 tests/expected/libmount/utils-fs-root-subdir2
+
+diff --git a/libmount/src/tab.c b/libmount/src/tab.c
+index 1ba1eec..10ee7ce 100644
+--- a/libmount/src/tab.c
++++ b/libmount/src/tab.c
+@@ -46,6 +46,8 @@
+ #include "mountP.h"
+ #include "strutils.h"
+ #include "loopdev.h"
++#include "fileutils.h"
++#include "canonicalize.h"
+ 
+ static int is_mountinfo(struct libmnt_table *tb);
+ 
+@@ -900,6 +902,20 @@ struct libmnt_fs *mnt_table_find_devno(struct libmnt_table *tb,
+ 	return NULL;
+ }
+ 
++static char *remove_mountpoint_from_path(const char *path, const char *mnt)
++{
++        char *res;
++	const char *p;
++	size_t sz;
++
++	sz = strlen(mnt);
++	p = sz > 1 ? path + sz : path;
++
++	res = *p ? strdup(p) : strdup("/");
++	DBG(UTILS, mnt_debug("%s fs-root is %s", path, res));
++	return res;
++}
++
+ /*
+  * tb: /proc/self/mountinfo
+  * fs: filesystem
+@@ -919,7 +935,8 @@ struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb,
+ 					unsigned long mountflags,
+ 					char **fsroot)
+ {
+-	char *root = NULL, *mnt = NULL;
++	char *root = NULL;
++	const char *mnt = NULL;
+ 	const char *fstype;
+ 	struct libmnt_fs *src_fs = NULL;
+ 
+@@ -937,10 +954,15 @@ struct libmnt_fs *mnt_table_get_fs_root(struct libmnt_table *tb,
+ 		DBG(TAB, mnt_debug("fs-root for bind"));
+ 
+ 		src = xsrc = mnt_resolve_spec(mnt_fs_get_source(fs), tb->cache);
+-		if (src)
+-			mnt = mnt_get_mountpoint(src);
++		if (src) {
++			struct libmnt_fs *fs = mnt_table_find_mountpoint(tb,
++							src, MNT_ITER_BACKWARD);
++			if (fs)
++				mnt = mnt_fs_get_target(fs);
++		}
++
+ 		if (mnt)
+-			root = mnt_get_fs_root(src, mnt);
++			root = remove_mountpoint_from_path(src, mnt);
+ 
+ 		if (xsrc && !tb->cache) {
+ 			free(xsrc);
+@@ -1007,11 +1029,9 @@ dflt:
+ 
+ 	DBG(TAB, mnt_debug("FS root result: %s", root));
+ 
+-	free(mnt);
+ 	return src_fs;
+ err:
+ 	free(root);
+-	free(mnt);
+ 	return NULL;
+ }
+ 
+diff --git a/libmount/src/utils.c b/libmount/src/utils.c
+index 5783d88..2151ff9 100644
+--- a/libmount/src/utils.c
++++ b/libmount/src/utils.c
+@@ -908,28 +908,6 @@ err:
+ 	return NULL;
+ }
+ 
+-char *mnt_get_fs_root(const char *path, const char *mnt)
+-{
+-	char *m = (char *) mnt, *res;
+-	const char *p;
+-	size_t sz;
+-
+-	if (!m)
+-		m = mnt_get_mountpoint(path);
+-	if (!m)
+-		return NULL;
+-
+-	sz = strlen(m);
+-	p = sz > 1 ? path + sz : path;
+-
+-	if (m != mnt)
+-		free(m);
+-
+-	res = *p ? strdup(p) : strdup("/");
+-	DBG(UTILS, mnt_debug("%s fs-root is %s", path, res));
+-	return res;
+-}
+-
+ /*
+  * Search for @name kernel command parametr.
+  *
+@@ -1085,17 +1063,6 @@ int test_mountpoint(struct libmnt_test *ts, int argc, char *argv[])
+ 	return 0;
+ }
+ 
+-int test_fsroot(struct libmnt_test *ts, int argc, char *argv[])
+-{
+-	char *path = canonicalize_path(argv[1]),
+-	     *mnt = path ? mnt_get_fs_root(path, NULL) : NULL;
+-
+-	printf("%s: %s\n", argv[1], mnt ? : "unknown");
+-	free(mnt);
+-	free(path);
+-	return 0;
+-}
+-
+ int test_filesystems(struct libmnt_test *ts, int argc, char *argv[])
+ {
+ 	char **filesystems = NULL;
+@@ -1170,7 +1137,6 @@ int main(int argc, char *argv[])
+ 	{ "--starts-with",   test_startswith,      "<string> <prefix>" },
+ 	{ "--ends-with",     test_endswith,        "<string> <prefix>" },
+ 	{ "--mountpoint",    test_mountpoint,      "<path>" },
+-	{ "--fs-root",       test_fsroot,          "<path>" },
+ 	{ "--cd-parent",     test_chdir,           "<path>" },
+ 	{ "--kernel-cmdline",test_kernel_cmdline,  "<option> | <option>=" },
+ 	{ "--mkdir",         test_mkdir,           "<path>" },
+diff --git a/tests/expected/libmount/utils-fs-root b/tests/expected/libmount/utils-fs-root
+deleted file mode 100644
+index 7746b28..0000000
+--- a/tests/expected/libmount/utils-fs-root
++++ /dev/null
+@@ -1 +0,0 @@
+-/proc: /
+diff --git a/tests/expected/libmount/utils-fs-root-subdir b/tests/expected/libmount/utils-fs-root-subdir
+deleted file mode 100644
+index 09cdb8d..0000000
+--- a/tests/expected/libmount/utils-fs-root-subdir
++++ /dev/null
+@@ -1 +0,0 @@
+-/proc/sys/kernel: /sys/kernel
+diff --git a/tests/expected/libmount/utils-fs-root-subdir2 b/tests/expected/libmount/utils-fs-root-subdir2
+deleted file mode 100644
+index 2e8b89a..0000000
+--- a/tests/expected/libmount/utils-fs-root-subdir2
++++ /dev/null
+@@ -1 +0,0 @@
+-/etc: /etc
+diff --git a/tests/ts/libmount/utils b/tests/ts/libmount/utils
+index 6facaad..89ecf10 100755
+--- a/tests/ts/libmount/utils
++++ b/tests/ts/libmount/utils
+@@ -64,18 +64,6 @@ ts_init_subtest "mountpoint-root"
+ ts_valgrind $TESTPROG --mountpoint / &> $TS_OUTPUT
+ ts_finalize_subtest
+ 
+-ts_init_subtest "fs-root"
+-ts_valgrind $TESTPROG --fs-root /proc &> $TS_OUTPUT
+-ts_finalize_subtest
+-
+-ts_init_subtest "fs-root-subdir"
+-ts_valgrind $TESTPROG --fs-root /proc/sys/kernel &> $TS_OUTPUT
+-ts_finalize_subtest
+-
+-ts_init_subtest "fs-root-subdir2"
+-ts_valgrind $TESTPROG --fs-root /etc &> $TS_OUTPUT
+-ts_finalize_subtest
+-
+ ts_init_subtest "kernel-cmdline"
+ export LIBMOUNT_KERNEL_CMDLINE="$TS_SELF/files/kernel_cmdline"
+ ts_valgrind $TESTPROG --kernel-cmdline selinux= &>> $TS_OUTPUT
+-- 
+2.7.4
+
diff --git a/SOURCES/0073-libmount-mark-overlay-as-pseudo-FS.patch b/SOURCES/0073-libmount-mark-overlay-as-pseudo-FS.patch
new file mode 100644
index 0000000..db6ce86
--- /dev/null
+++ b/SOURCES/0073-libmount-mark-overlay-as-pseudo-FS.patch
@@ -0,0 +1,27 @@
+From 346b97a27e817a001c71d45400f17429bd82b364 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Wed, 11 Mar 2015 11:38:07 +0100
+Subject: [PATCH 73/84] libmount: mark overlay as pseudo-FS
+
+Upstream: http://github.com/karelzak/util-linux/commit/209fd7a74879d37cef3d5d1679bc9cad76b96c7a
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=587393
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ libmount/src/utils.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/libmount/src/utils.c b/libmount/src/utils.c
+index 2151ff9..c13fb96 100644
+--- a/libmount/src/utils.c
++++ b/libmount/src/utils.c
+@@ -269,6 +269,7 @@ int mnt_fstype_is_pseudofs(const char *type)
+ 		"mqueue",
+ 		"nfsd",
+ 		"none",
++		"overlay",
+ 		"pipefs",
+ 		"proc",
+ 		"pstore",
+-- 
+2.7.4
+
diff --git a/SOURCES/0074-logger-be-more-precise-about-port-description.patch b/SOURCES/0074-logger-be-more-precise-about-port-description.patch
new file mode 100644
index 0000000..8ff9c46
--- /dev/null
+++ b/SOURCES/0074-logger-be-more-precise-about-port-description.patch
@@ -0,0 +1,41 @@
+From f4ffe3de3ba721892af52c7ed5787f4470393242 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Tue, 21 Jun 2016 13:35:33 +0200
+Subject: [PATCH 74/84] logger: be more precise about --port description
+
+Upstream: http://github.com/karelzak/util-linux/commit/1c7227598824b1d9140298e9fe5742cae4131130
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1344222
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ misc-utils/logger.1 | 1 +
+ misc-utils/logger.c | 2 +-
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/misc-utils/logger.1 b/misc-utils/logger.1
+index 5eb8d09..8c4faca 100644
+--- a/misc-utils/logger.1
++++ b/misc-utils/logger.1
+@@ -72,6 +72,7 @@ port defined in /etc/services, which is often
+ \fB\-P\fR, \fB\-\-port\fR \fIport\fR
+ Use the specified
+ .IR port .
++When this option is not specified, the port defaults to syslog for udp and to syslog-conn for tcp connections.
+ .TP
+ \fB\-i\fR, \fB\-\-id\fR
+ Log the process ID of the logger process with each line.
+diff --git a/misc-utils/logger.c b/misc-utils/logger.c
+index c83c0b8..a331869 100644
+--- a/misc-utils/logger.c
++++ b/misc-utils/logger.c
+@@ -222,7 +222,7 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out)
+ 		" -f, --file <file>     log the contents of this file\n"
+ 		" -h, --help            display this help text and exit\n"), out);
+ 	fputs(_(" -n, --server <name>   write to this remote syslog server\n"
+-		" -P, --port <number>   use this UDP port\n"
++		" -P, --port <port>     use this port for UDP or TCP connection\n"
+ 		" -p, --priority <prio> mark given message with this priority\n"
+ 		" -s, --stderr          output message to standard error as well\n"), out);
+ 	fputs(_(" -t, --tag <tag>       mark every line with this tag\n"
+-- 
+2.7.4
+
diff --git a/SOURCES/0075-libfdisk-gpt-be-more-careful-with-64bit-constants.patch b/SOURCES/0075-libfdisk-gpt-be-more-careful-with-64bit-constants.patch
new file mode 100644
index 0000000..82190fa
--- /dev/null
+++ b/SOURCES/0075-libfdisk-gpt-be-more-careful-with-64bit-constants.patch
@@ -0,0 +1,182 @@
+From 428be59e33d0875cdf5bf602a75328fb3d7c58ad Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Tue, 21 Jun 2016 14:06:14 +0200
+Subject: [PATCH 75/84] libfdisk: (gpt) be more careful with 64bit constants
+
+It's probably more robust (and readable) to be explicit when we count
+with constant and 64bit numbers.
+
+Upstream: http://github.com/karelzak/util-linux/commit/0a7cdf80606cc0670ef7f740d37640b05932e0ce
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1344482
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ libfdisk/src/gpt.c | 40 ++++++++++++++++++++--------------------
+ 1 file changed, 20 insertions(+), 20 deletions(-)
+
+diff --git a/libfdisk/src/gpt.c b/libfdisk/src/gpt.c
+index 482d453..d3bdc2d 100644
+--- a/libfdisk/src/gpt.c
++++ b/libfdisk/src/gpt.c
+@@ -53,7 +53,7 @@
+ #define GPT_MBR_PROTECTIVE  1
+ #define GPT_MBR_HYBRID      2
+ 
+-#define GPT_PRIMARY_PARTITION_TABLE_LBA 0x00000001
++#define GPT_PRIMARY_PARTITION_TABLE_LBA 0x00000001ULL
+ 
+ #define EFI_PMBR_OSTYPE     0xEE
+ #define MSDOS_MBR_SIGNATURE 0xAA55
+@@ -364,7 +364,7 @@ static int gpt_mknew_pmbr(struct fdisk_context *cxt)
+ 	pmbr->partition_record[0].end_track    = 0xFF;
+ 	pmbr->partition_record[0].starting_lba = cpu_to_le32(1);
+ 	pmbr->partition_record[0].size_in_lba  =
+-		cpu_to_le32(min((uint32_t) cxt->total_sectors - 1, 0xFFFFFFFF));
++		cpu_to_le32((uint32_t) min(cxt->total_sectors - 1ULL, 0xFFFFFFFFULL));
+ 
+ 	return 0;
+ }
+@@ -379,14 +379,14 @@ static void gpt_mknew_header_common(struct fdisk_context *cxt,
+ 	header->my_lba = cpu_to_le64(lba);
+ 
+ 	if (lba == GPT_PRIMARY_PARTITION_TABLE_LBA) { /* primary */
+-		header->alternative_lba = cpu_to_le64(cxt->total_sectors - 1);
+-		header->partition_entry_lba = cpu_to_le64(2);
++		header->alternative_lba = cpu_to_le64(cxt->total_sectors - 1ULL);
++		header->partition_entry_lba = cpu_to_le64(2ULL);
+ 	} else { /* backup */
+ 		uint64_t esz = le32_to_cpu(header->npartition_entries) * sizeof(struct gpt_entry);
+ 		uint64_t esects = (esz + cxt->sector_size - 1) / cxt->sector_size;
+ 
+ 		header->alternative_lba = cpu_to_le64(GPT_PRIMARY_PARTITION_TABLE_LBA);
+-		header->partition_entry_lba = cpu_to_le64(cxt->total_sectors - 1 - esects);
++		header->partition_entry_lba = cpu_to_le64(cxt->total_sectors - 1ULL - esects);
+ 	}
+ }
+ 
+@@ -451,8 +451,8 @@ static int gpt_mknew_header(struct fdisk_context *cxt,
+ 	header->npartition_entries     = cpu_to_le32(GPT_NPARTITIONS);
+ 	header->sizeof_partition_entry = cpu_to_le32(sizeof(struct gpt_entry));
+ 
+-	last = cxt->total_sectors - 2 - esz;
+-	first = esz + 2;
++	last = cxt->total_sectors - 2ULL - esz;
++	first = esz + 2ULL;
+ 
+ 	if (first < cxt->first_lba && cxt->first_lba < last)
+ 		/* Align according to topology */
+@@ -520,7 +520,7 @@ check_hybrid:
+ 	 */
+ 	if (ret == GPT_MBR_PROTECTIVE) {
+ 		if (le32_to_cpu(pmbr->partition_record[0].size_in_lba) !=
+-		    min((uint32_t) cxt->total_sectors - 1, 0xFFFFFFFF))
++		    (uint32_t) min(cxt->total_sectors - 1ULL, 0xFFFFFFFFULL))
+ 			ret = 0;
+ 	}
+ done:
+@@ -538,7 +538,7 @@ static uint64_t last_lba(struct fdisk_context *cxt)
+ 	}
+ 
+ 	if (S_ISBLK(s.st_mode))
+-		return cxt->total_sectors - 1;
++		return cxt->total_sectors - 1ULL;
+ 	else if (S_ISREG(s.st_mode)) {
+ 		uint64_t sectors = s.st_size >> cxt->sector_size;
+ 		return (sectors / cxt->sector_size) - 1ULL;
+@@ -554,7 +554,7 @@ static ssize_t read_lba(struct fdisk_context *cxt, uint64_t lba,
+ 
+ 	if (lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1)
+ 		return -1;
+-	return read(cxt->dev_fd, buffer, bytes) != bytes;
++	return read(cxt->dev_fd, buffer, bytes) != (ssize_t) bytes;
+ }
+ 
+ 
+@@ -908,7 +908,7 @@ static uint64_t find_first_available(struct gpt_header *header,
+ 			if (first < gpt_partition_start(&e[i]))
+ 				continue;
+ 			if (first <= gpt_partition_end(&e[i])) {
+-				first = gpt_partition_end(&e[i]) + 1;
++				first = gpt_partition_end(&e[i]) + 1ULL;
+ 				first_moved = 1;
+ 			}
+ 		}
+@@ -937,7 +937,7 @@ static uint64_t find_last_free(struct gpt_header *header,
+ 		uint64_t ps = gpt_partition_start(&e[i]);
+ 
+ 		if (nearest_start > ps && ps > start)
+-			nearest_start = ps - 1;
++			nearest_start = ps - 1ULL;
+ 	}
+ 
+ 	return nearest_start;
+@@ -960,7 +960,7 @@ static uint64_t find_last_free_sector(struct gpt_header *header,
+ 		for (i = 0; i < le32_to_cpu(header->npartition_entries); i++) {
+ 			if ((last >= gpt_partition_start(&e[i])) &&
+ 			    (last <= gpt_partition_end(&e[i]))) {
+-				last = gpt_partition_start(&e[i]) - 1;
++				last = gpt_partition_start(&e[i]) - 1ULL;
+ 				last_moved = 1;
+ 			}
+ 		}
+@@ -986,7 +986,7 @@ static uint64_t find_first_in_largest(struct gpt_header *header, struct gpt_entr
+ 		first_sect =  find_first_available(header, e, start);
+ 		if (first_sect != 0) {
+ 			last_sect = find_last_free(header, e, first_sect);
+-			segment_size = last_sect - first_sect + 1;
++			segment_size = last_sect - first_sect + 1ULL;
+ 
+ 			if (segment_size > selected_size) {
+ 				selected_size = segment_size;
+@@ -1026,7 +1026,7 @@ static uint64_t get_free_sectors(struct fdisk_context *cxt, struct gpt_header *h
+ 				largest_seg = segment_sz;
+ 			totfound += segment_sz;
+ 			num++;
+-			start = last_sect + 1;
++			start = last_sect + 1ULL;
+ 		}
+ 	} while (first_sect);
+ 
+@@ -1165,7 +1165,7 @@ void gpt_list_table(struct fdisk_context *cxt,
+ 			continue;
+ 
+ 		/* the partition has to inside usable range */
+-		if (start < fu || start + size - 1 > lu)
++		if (start < fu || start + size - 1ULL > lu)
+ 			continue;
+ 
+ 		name = encode_to_utf8((unsigned char *)gpt->ents[i].partition_name,
+@@ -1266,11 +1266,11 @@ static int gpt_write_pmbr(struct fdisk_context *cxt)
+ 	 * Set size_in_lba to the size of the disk minus one. If the size of the disk
+ 	 * is too large to be represented by a 32bit LBA (2Tb), set it to 0xFFFFFFFF.
+ 	 */
+-	if (cxt->total_sectors - 1 > 0xFFFFFFFFULL)
++	if (cxt->total_sectors - 1ULL > 0xFFFFFFFFULL)
+ 		pmbr->partition_record[0].size_in_lba = cpu_to_le32(0xFFFFFFFF);
+ 	else
+ 		pmbr->partition_record[0].size_in_lba =
+-			cpu_to_le32(cxt->total_sectors - 1UL);
++			cpu_to_le32((uint32_t) (cxt->total_sectors - 1ULL));
+ 
+ 	offset = GPT_PMBR_LBA * cxt->sector_size;
+ 	if (offset != lseek(cxt->dev_fd, offset, SEEK_SET))
+@@ -1308,7 +1308,7 @@ static int gpt_write_disklabel(struct fdisk_context *cxt)
+ 		goto err0;
+ 
+ 	/* check that the backup header is properly placed */
+-	if (le64_to_cpu(gpt->pheader->alternative_lba) < cxt->total_sectors - 1)
++	if (le64_to_cpu(gpt->pheader->alternative_lba) < cxt->total_sectors - 1ULL)
+ 		/* TODO: correct this (with user authorization) and write */
+ 		goto err0;
+ 
+@@ -1645,7 +1645,7 @@ static int gpt_add_partition(
+ 
+ 		user_l = fdisk_ask_number_get_result(ask);
+ 		if (fdisk_ask_number_is_relative(ask))
+-			user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1;
++			user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1ULL;
+ 		if (user_l > user_f && user_l <= disk_l)
+ 			break;
+ 	}
+-- 
+2.7.4
+
diff --git a/SOURCES/0076-lsns-backport-new-command.patch b/SOURCES/0076-lsns-backport-new-command.patch
new file mode 100644
index 0000000..090bd16
--- /dev/null
+++ b/SOURCES/0076-lsns-backport-new-command.patch
@@ -0,0 +1,1251 @@
+From 03f539c766780a083010636cc67f96fcb2bab30f Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Fri, 24 Jun 2016 12:16:24 +0200
+Subject: [PATCH 76/84] lsns: backport new command
+
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1332084
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ bash-completion/lsns    |  55 ++++
+ configure.ac            |   6 +
+ include/Makemodule.am   |   1 +
+ include/c.h             |   8 +
+ include/debug.h         |   2 +-
+ include/idcache.h       |  28 ++
+ include/procutils.h     |   2 +
+ lib/Makemodule.am       |   1 +
+ lib/idcache.c           | 117 ++++++++
+ lib/procutils.c         |  43 +++
+ sys-utils/Makemodule.am |   7 +
+ sys-utils/lsns.8        |  78 +++++
+ sys-utils/lsns.c        | 748 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 13 files changed, 1095 insertions(+), 1 deletion(-)
+ create mode 100644 bash-completion/lsns
+ create mode 100644 include/idcache.h
+ create mode 100644 lib/idcache.c
+ create mode 100644 sys-utils/lsns.8
+ create mode 100644 sys-utils/lsns.c
+
+diff --git a/bash-completion/lsns b/bash-completion/lsns
+new file mode 100644
+index 0000000..d02df3b
+--- /dev/null
++++ b/bash-completion/lsns
+@@ -0,0 +1,55 @@
++_lsns_module()
++{
++	local cur prev OPTS LSNS_COLS_ALL
++	COMPREPLY=()
++	cur="${COMP_WORDS[COMP_CWORD]}"
++	prev="${COMP_WORDS[COMP_CWORD-1]}"
++	LSNS_COLS_ALL="
++		NS TYPE PATH NPROCS PID PPID COMMAND UID USER
++	"
++	case $prev in
++		'-o'|'--output')
++			local prefix realcur LSNS_COLS
++			realcur="${cur##*,}"
++			prefix="${cur%$realcur}"
++			for WORD in $LSNS_COLS_ALL; do
++				if ! [[ $prefix == *"$WORD"* ]]; then
++					LSNS_COLS="$WORD $LSNS_COLS"
++				fi
++			done
++			compopt -o nospace
++			COMPREPLY=( $(compgen -P "$prefix" -W "$LSNS_COLS" -S ',' -- $realcur) )
++			return 0
++			;;
++		'-p'|'--task')
++			COMPREPLY=( $(compgen -W "$(cd /proc && echo [0-9]*)" -- $cur) )
++			return 0
++			;;
++		'-t'|'--type')
++			COMPREPLY=( $(compgen -W "mnt net ipc user pid uts" -- $cur) )
++			return 0
++			;;
++		'-h'|'--help'|'-V'|'--version')
++			return 0
++			;;
++	esac
++	case $cur in
++		-*)
++			COMPREPLY=( $(compgen -W "
++					--list
++					--noheadings
++					--output
++					--task
++					--raw
++					--notruncate
++					--type
++					--help
++					--version
++				" -- $cur) )
++			return 0
++			;;
++	esac
++	COMPREPLY=( $(compgen -W "mnt net pid uts ipc user" -- $cur ) )
++	return 0
++}
++complete -F _lsns_module lsns
+diff --git a/configure.ac b/configure.ac
+index f3c7214..5d9ea39 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1032,6 +1032,12 @@ UL_REQUIRES_BUILD([lslogins], [libsmartcols])
+ AM_CONDITIONAL([BUILD_LSLOGINS], [test "x$build_lslogins" = xyes])
+ 
+ 
++UL_BUILD_INIT([lsns], [check])
++UL_REQUIRES_LINUX([lsns])
++UL_REQUIRES_BUILD([lsns], [libsmartcols])
++AM_CONDITIONAL([BUILD_LSNS], [test "x$build_lsns" = xyes])
++
++
+ UL_BUILD_INIT([chcpu], [check])
+ UL_REQUIRES_LINUX([chcpu])
+ UL_REQUIRES_HAVE([chcpu], [cpu_set_t], [cpu_set_t type])
+diff --git a/include/Makemodule.am b/include/Makemodule.am
+index 7b53244..757f317 100644
+--- a/include/Makemodule.am
++++ b/include/Makemodule.am
+@@ -15,6 +15,7 @@ dist_noinst_HEADERS += \
+ 	include/exec_shell.h \
+ 	include/exitcodes.h \
+ 	include/fileutils.h \
++	include/idcache.h \
+ 	include/ismounted.h \
+ 	include/linux_reboot.h \
+ 	include/linux_version.h \
+diff --git a/include/c.h b/include/c.h
+index 7b59ce8..a2779a5 100644
+--- a/include/c.h
++++ b/include/c.h
+@@ -110,6 +110,14 @@
+ 	_max1 > _max2 ? _max1 : _max2; })
+ #endif
+ 
++#ifndef cmp_numbers
++# define cmp_numbers(x, y) __extension__ ({	\
++	__typeof__(x) _a = (x);			\
++	__typeof__(y) _b = (y);			\
++	(void) (&_a == &_b);			\
++	_a == _b ? 0 : _a > _b ? 1 : -1; })
++#endif
++
+ #ifndef offsetof
+ #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+ #endif
+diff --git a/include/debug.h b/include/debug.h
+index 25045aa..848e474 100644
+--- a/include/debug.h
++++ b/include/debug.h
+@@ -15,7 +15,7 @@ struct dbg_mask { char *mname; int val; };
+ 
+ #define UL_DEBUG_DEFINE_MASK(m) int m ## _debug_mask
+ #define UL_DEBUG_DECLARE_MASK(m) extern UL_DEBUG_DEFINE_MASK(m)
+-#define UL_DEBUG_DEFINE_MASKANEMS(m) static const struct dbg_mask m ## _masknames[]
++#define UL_DEBUG_DEFINE_MASKNAMES(m) static const struct dbg_mask m ## _masknames[]
+ 
+ /* p - flag prefix, m - flag postfix */
+ #define UL_DEBUG_DEFINE_FLAG(p, m) p ## m
+diff --git a/include/idcache.h b/include/idcache.h
+new file mode 100644
+index 0000000..912edd5
+--- /dev/null
++++ b/include/idcache.h
+@@ -0,0 +1,28 @@
++#ifndef UTIL_LINUX_IDCACHE_H
++#define UTIL_LINUX_IDCACHE_H
++
++#include <sys/types.h>
++#include <pwd.h>
++
++#define IDCACHE_FLAGS_NAMELEN	(1 << 1)
++
++struct identry {
++	unsigned long int	id;
++	char			*name;
++	struct identry		*next;
++};
++
++struct idcache {
++	struct identry	*ent;	/* first entry */
++	int		width;	/* name width */
++};
++
++
++extern struct idcache *new_idcache(void);
++extern void add_gid(struct idcache *cache, unsigned long int id);
++extern void add_uid(struct idcache *cache, unsigned long int id);
++
++extern void free_idcache(struct idcache *ic);
++extern struct identry *get_id(struct idcache *ic, unsigned long int id);
++
++#endif /* UTIL_LINUX_IDCACHE_H */
+diff --git a/include/procutils.h b/include/procutils.h
+index 14b766c..9f8dd76 100644
+--- a/include/procutils.h
++++ b/include/procutils.h
+@@ -28,5 +28,7 @@ extern void proc_processes_filter_by_name(struct proc_processes *ps, const char
+ extern void proc_processes_filter_by_uid(struct proc_processes *ps, uid_t uid);
+ extern int proc_next_pid(struct proc_processes *ps, pid_t *pid);
+ 
++extern char *proc_get_command(pid_t pid);
++extern char *proc_get_command_name(pid_t pid);
+ 
+ #endif /* UTIL_LINUX_PROCUTILS */
+diff --git a/lib/Makemodule.am b/lib/Makemodule.am
+index eed31f1..73280f9 100644
+--- a/lib/Makemodule.am
++++ b/lib/Makemodule.am
+@@ -8,6 +8,7 @@ libcommon_la_SOURCES = \
+ 	lib/colors.c \
+ 	lib/crc32.c \
+ 	lib/env.c \
++	lib/idcache.c \
+ 	lib/fileutils.c \
+ 	lib/ismounted.c \
+ 	lib/mangle.c \
+diff --git a/lib/idcache.c b/lib/idcache.c
+new file mode 100644
+index 0000000..3c358b8
+--- /dev/null
++++ b/lib/idcache.c
+@@ -0,0 +1,117 @@
++
++#include <wchar.h>
++#include <pwd.h>
++#include <grp.h>
++#include <sys/types.h>
++
++#include "c.h"
++#include "idcache.h"
++
++#ifndef LOGIN_NAME_MAX
++#define LOGIN_NAME_MAX 256
++#endif
++
++struct identry *get_id(struct idcache *ic, unsigned long int id)
++{
++	struct identry *ent;
++
++	if (!ic)
++		return NULL;
++
++	for (ent = ic->ent; ent; ent = ent->next) {
++		if (ent->id == id)
++			return ent;
++	}
++
++	return NULL;
++}
++
++struct idcache *new_idcache()
++{
++	return calloc(1, sizeof(struct idcache));
++}
++
++void free_idcache(struct idcache *ic)
++{
++	struct identry *ent = ic->ent;
++
++	while (ent) {
++		struct identry *next = ent->next;
++		free(ent->name);
++		free(ent);
++		ent = next;
++	}
++
++	free(ic);
++}
++
++static void add_id(struct idcache *ic, char *name, unsigned long int id)
++{
++	struct identry *ent, *x;
++	int w = 0;
++
++	ent = calloc(1, sizeof(struct identry));
++	if (!ent)
++		return;
++	ent->id = id;
++
++	if (name) {
++#ifdef HAVE_WIDECHAR
++		wchar_t wc[LOGIN_NAME_MAX + 1];
++
++		if (mbstowcs(wc, name, LOGIN_NAME_MAX) > 0) {
++			wc[LOGIN_NAME_MAX] = '\0';
++			w = wcswidth(wc, LOGIN_NAME_MAX);
++		}
++		else
++#endif
++			w = strlen(name);
++	}
++
++	/* note, we ignore names with non-printable widechars */
++	if (w > 0) {
++		ent->name = strdup(name);
++		if (!ent->name) {
++			free(ent);
++			return;
++		}
++	} else {
++		if (asprintf(&ent->name, "%lu", id) < 0) {
++			free(ent);
++			return;
++		}
++	}
++
++	for (x = ic->ent; x && x->next; x = x->next);
++
++	if (x)
++		x->next = ent;
++	else
++		ic->ent = ent;
++
++	if (w <= 0)
++		w = ent->name ? strlen(ent->name) : 0;
++	ic->width = ic->width < w ? w : ic->width;
++	return;
++}
++
++void add_uid(struct idcache *cache, unsigned long int id)
++{
++	struct identry *ent= get_id(cache, id);
++
++	if (!ent) {
++		struct passwd *pw = getpwuid((uid_t) id);
++		add_id(cache, pw ? pw->pw_name : NULL, id);
++	}
++}
++
++void add_gid(struct idcache *cache, unsigned long int id)
++{
++	struct identry *ent = get_id(cache, id);
++
++	if (!ent) {
++		struct group *gr = getgrgid((gid_t) id);
++		add_id(cache, gr ? gr->gr_name : NULL, id);
++	}
++}
++
+diff --git a/lib/procutils.c b/lib/procutils.c
+index d633261..8dfdec9 100644
+--- a/lib/procutils.c
++++ b/lib/procutils.c
+@@ -25,6 +25,7 @@
+ #include "procutils.h"
+ #include "at.h"
+ #include "c.h"
++#include "all-io.h"
+ 
+ /*
+  * @pid: process ID for which we want to obtain the threads group
+@@ -193,6 +194,48 @@ int proc_next_pid(struct proc_processes *ps, pid_t *pid)
+ 	return 0;
+ }
+ 
++/* returns process command path, use free() for result */
++static char *proc_file_strdup(pid_t pid, const char *name)
++{
++	char buf[BUFSIZ], *res = NULL;
++	ssize_t sz = 0;
++	size_t i;
++	int fd;
++
++	snprintf(buf, sizeof(buf), "/proc/%d/%s", (int) pid, name);
++	fd = open(buf, O_RDONLY);
++	if (fd < 0)
++		goto done;
++
++	sz = read_all(fd, buf, sizeof(buf));
++	if (sz <= 0)
++		goto done;
++
++	for (i = 0; i < (size_t) sz; i++) {
++
++		if (buf[i] == '\0')
++			buf[i] = ' ';
++	}
++	buf[sz - 1] = '\0';
++	res = strdup(buf);
++done:
++	if (fd >= 0)
++		close(fd);
++	return res;
++}
++
++/* returns process command path, use free() for result */
++char *proc_get_command(pid_t pid)
++{
++	return proc_file_strdup(pid, "cmdline");
++}
++
++/* returns process command name, use free() for result */
++char *proc_get_command_name(pid_t pid)
++{
++	return proc_file_strdup(pid, "comm");
++}
++
+ #ifdef TEST_PROGRAM
+ 
+ static int test_tasks(int argc, char *argv[])
+diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am
+index c6c561c..9baf5a3 100644
+--- a/sys-utils/Makemodule.am
++++ b/sys-utils/Makemodule.am
+@@ -182,6 +182,13 @@ prlimit_SOURCES = sys-utils/prlimit.c
+ prlimit_LDADD = $(LDADD) libcommon.la
+ endif
+ 
++if BUILD_LSNS
++usrbin_exec_PROGRAMS += lsns
++dist_man_MANS += sys-utils/lsns.8
++lsns_SOURCES =	sys-utils/lsns.c
++lsns_LDADD = $(LDADD) libcommon.la libsmartcols.la
++lsns_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir)
++endif
+ 
+ if BUILD_MOUNT
+ #
+diff --git a/sys-utils/lsns.8 b/sys-utils/lsns.8
+new file mode 100644
+index 0000000..328df47
+--- /dev/null
++++ b/sys-utils/lsns.8
+@@ -0,0 +1,78 @@
++.\" Man page for the lsns command.
++.\" Copyright 2015 Karel Zak <kzak@redhat.com>
++.\" May be distributed under the GNU General Public License
++
++.TH LSNS 8 "December 2015" "util-linux" "System Administration"
++.SH NAME
++lsns \- list namespaces
++.SH SYNOPSIS
++.B lsns
++[options]
++.RI [ namespace ]
++
++.SH DESCRIPTION
++.B lsns
++lists information about all the currently accessible namespaces or about the
++given \fInamespace\fP.  The \fInamespace\fP identifier is an inode number.
++
++The default output is subject to change.  So whenever possible, you should
++avoid using default outputs in your scripts.  Always explicitly define expected
++columns by using the \fB\-\-output\fR option together with a columns list in
++environments where a stable output is required.
++
++Note that \fBlsns\fR reads information directly from the /proc filesystem and
++for non-root users it may return incomplete information.  The current /proc
++filesystem may be unshared and affected by a PID namespace
++(see \fBunshare \-\-mount\-proc\fP for more details).
++.B lsns
++is not able to see persistent namespaces without processes where the namespace
++instance is held by a bind mount to /proc/\fIpid\fR/ns/\fItype\fR.
++
++.SH OPTIONS
++.TP
++.BR \-l , " \-\-list"
++Use list output format.
++.TP
++.BR \-n , " \-\-noheadings"
++Do not print a header line.
++.TP
++.BR \-o , " \-\-output " \fIlist\fP
++Specify which output columns to print.  Use \fB\-\-help\fR
++to get a list of all supported columns.
++
++The default list of columns may be extended if \fIlist\fP is
++specified in the format \fB+\fIlist\fP (e.g. \fBlsns \-o +PATH\fP).
++.TP
++.BR \-p , " \-\-task " \fIpid\fP
++Display only the namespaces held by the process with this \fIpid\fR.
++.TP
++.BR \-r , " \-\-raw"
++Use the raw output format.
++.TP
++.BR \-t , " \-\-type " \fItype\fP
++Display the specified \fItype\fP of namespaces only.  The supported types are
++\fBmnt\fP, \fBnet\fP, \fBipc\fP, \fBuser\fP, \fBpid\fP and \fButs\fP.  This
++option may be given more than once.
++.TP
++.BR \-u , " \-\-notruncate"
++Do not truncate text in columns.
++.TP
++.BR \-V , " \-\-version"
++Display version information and exit.
++.TP
++.BR \-h , " \-\-help"
++Display help text and exit.
++
++.SH AUTHORS
++.nf
++Karel Zak <kzak@redhat.com>
++.fi
++
++.SH "SEE ALSO"
++.BR unshare (1),
++.BR nsenter (1),
++.BR clone (2)
++
++.SH AVAILABILITY
++The lsns command is part of the util-linux package and is available from
++ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
+diff --git a/sys-utils/lsns.c b/sys-utils/lsns.c
+new file mode 100644
+index 0000000..5ee2981
+--- /dev/null
++++ b/sys-utils/lsns.c
+@@ -0,0 +1,748 @@
++/*
++ * lsns(8) - list system namespaces
++ *
++ * Copyright (C) 2015 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
++ */
++#include <stdio.h>
++#include <string.h>
++#include <getopt.h>
++#include <stdlib.h>
++#include <assert.h>
++#include <dirent.h>
++#include <unistd.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++#include <wchar.h>
++#include <libsmartcols.h>
++
++#include "pathnames.h"
++#include "nls.h"
++#include "xalloc.h"
++#include "c.h"
++#include "list.h"
++#include "closestream.h"
++#include "optutils.h"
++#include "procutils.h"
++#include "strutils.h"
++#include "namespace.h"
++#include "path.h"
++#include "idcache.h"
++
++#include "debug.h"
++
++UL_DEBUG_DEFINE_MASK(lsns);
++UL_DEBUG_DEFINE_MASKNAMES(lsns) = UL_DEBUG_EMPTY_MASKNAMES;
++
++#define LSNS_DEBUG_INIT		(1 << 1)
++#define LSNS_DEBUG_PROC		(1 << 2)
++#define LSNS_DEBUG_NS		(1 << 3)
++#define LSNS_DEBUG_ALL		0xFFFF
++
++#define DBG(m, x)       __UL_DBG(lsns, LSNS_DEBUG_, m, x)
++#define ON_DBG(m, x)    __UL_DBG_CALL(lsns, LSNS_DEBUG_, m, x)
++
++struct idcache *uid_cache = NULL;
++
++/* column IDs */
++enum {
++	COL_NS = 0,
++	COL_TYPE,
++	COL_PATH,
++	COL_NPROCS,
++	COL_PID,
++	COL_PPID,
++	COL_COMMAND,
++	COL_UID,
++	COL_USER
++};
++
++/* column names */
++struct colinfo {
++	const char *name; /* header */
++	double	   whint; /* width hint (N < 1 is in percent of termwidth) */
++	int	   flags; /* SCOLS_FL_* */
++	const char *help;
++};
++
++/* columns descriptions */
++static const struct colinfo infos[] = {
++	[COL_NS]      = { "NS",     10, SCOLS_FL_RIGHT, N_("namespace identifier (inode number)") },
++	[COL_TYPE]    = { "TYPE",    5, 0, N_("kind of namespace") },
++	[COL_PATH]    = { "PATH",    0, 0, N_("path to the namespace")},
++	[COL_NPROCS]  = { "NPROCS",  5, SCOLS_FL_RIGHT, N_("number of processes in the namespace") },
++	[COL_PID]     = { "PID",     5, SCOLS_FL_RIGHT, N_("lowest PID in the namespace") },
++	[COL_PPID]    = { "PPID",    5, SCOLS_FL_RIGHT, N_("PPID of the PID") },
++	[COL_COMMAND] = { "COMMAND", 0, SCOLS_FL_TRUNC, N_("command line of the PID")},
++	[COL_UID]     = { "UID",     0, SCOLS_FL_RIGHT, N_("UID of the PID")},
++	[COL_USER]    = { "USER",    0, 0, N_("username of the PID")}
++};
++
++static int columns[ARRAY_SIZE(infos) * 2];
++static size_t ncolumns;
++
++enum {
++	LSNS_ID_MNT = 0,
++	LSNS_ID_NET,
++	LSNS_ID_PID,
++	LSNS_ID_UTS,
++	LSNS_ID_IPC,
++	LSNS_ID_USER
++};
++
++static char *ns_names[] = {
++	[LSNS_ID_MNT] = "mnt",
++	[LSNS_ID_NET] = "net",
++	[LSNS_ID_PID] = "pid",
++	[LSNS_ID_UTS] = "uts",
++	[LSNS_ID_IPC] = "ipc",
++	[LSNS_ID_USER] = "user"
++};
++
++struct lsns_namespace {
++	ino_t id;
++	int type;			/* LSNS_* */
++	int nprocs;
++
++	struct lsns_process *proc;
++
++	struct list_head namespaces;	/* lsns->processes member */
++	struct list_head processes;	/* head of lsns_process *siblings */
++};
++
++struct lsns_process {
++	pid_t pid;		/* process PID */
++	pid_t ppid;		/* parent's PID */
++	pid_t tpid;		/* thread group */
++	char state;
++	uid_t uid;
++
++	ino_t            ns_ids[ARRAY_SIZE(ns_names)];
++	struct list_head ns_siblings[ARRAY_SIZE(ns_names)];
++
++	struct list_head processes;	/* list of processes */
++
++	struct libscols_line *outline;
++	struct lsns_process *parent;
++};
++
++struct lsns {
++	struct list_head processes;
++	struct list_head namespaces;
++
++	pid_t	fltr_pid;	/* filter out by PID */
++	ino_t	fltr_ns;	/* filter out by namespace */
++	int	fltr_types[ARRAY_SIZE(ns_names)];
++	int	fltr_ntypes;
++
++	unsigned int raw	: 1,
++		     tree	: 1,
++		     list	: 1,
++		     notrunc	: 1,
++		     no_headings: 1;
++};
++
++static void lsns_init_debug(void)
++{
++	__UL_INIT_DEBUG(lsns, LSNS_DEBUG_, 0, LSNS_DEBUG);
++}
++
++static int ns_name2type(const char *name)
++{
++	size_t i;
++
++	for (i = 0; i < ARRAY_SIZE(ns_names); i++) {
++		if (strcmp(ns_names[i], name) == 0)
++			return i;
++	}
++	return -1;
++}
++
++static int column_name_to_id(const char *name, size_t namesz)
++{
++	size_t i;
++
++	assert(name);
++
++	for (i = 0; i < ARRAY_SIZE(infos); i++) {
++		const char *cn = infos[i].name;
++
++		if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
++			return i;
++	}
++	warnx(_("unknown column: %s"), name);
++	return -1;
++}
++
++static inline int get_column_id(int num)
++{
++	assert(num >= 0);
++	assert((size_t) num < ncolumns);
++	assert(columns[num] < (int) ARRAY_SIZE(infos));
++
++	return columns[num];
++}
++
++static inline const struct colinfo *get_column_info(unsigned num)
++{
++	return &infos[ get_column_id(num) ];
++}
++
++static ino_t get_ns_ino(int dir, const char *nsname, ino_t *ino)
++{
++	struct stat st;
++	char path[16];
++
++	snprintf(path, sizeof(path), "ns/%s", nsname);
++
++	if (fstatat(dir, path, &st, 0) != 0)
++		return -errno;
++	*ino = st.st_ino;
++	return 0;
++}
++
++
++static int read_process(struct lsns *ls, pid_t pid)
++{
++	struct lsns_process *p = NULL;
++	char buf[BUFSIZ];
++	DIR *dir;
++	int rc = 0, fd;
++	FILE *f = NULL;
++	size_t i;
++	struct stat st;
++
++	DBG(PROC, ul_debug("reading %d", (int) pid));
++
++	snprintf(buf, sizeof(buf), "/proc/%d", pid);
++	dir = opendir(buf);
++	if (!dir)
++		return -errno;
++
++	p = xcalloc(1, sizeof(*p));
++	if (!p) {
++		rc = -ENOMEM;
++		goto done;
++	}
++
++	if (fstat(dirfd(dir), &st) == 0) {
++		p->uid = st.st_uid;
++		add_uid(uid_cache, st.st_uid);
++	}
++
++	fd = openat(dirfd(dir), "stat", O_RDONLY);
++	if (fd < 0) {
++		rc = -errno;
++		goto done;
++	}
++	if (!(f = fdopen(fd, "r"))) {
++		rc = -errno;
++		goto done;
++	}
++	rc = fscanf(f, "%d %*s %c %d*[^\n]", &p->pid, &p->state, &p->ppid);
++	if (rc != 3) {
++		rc = rc < 0 ? -errno : -EINVAL;
++		goto done;
++	}
++	rc = 0;
++
++	for (i = 0; i < ARRAY_SIZE(p->ns_ids); i++) {
++		INIT_LIST_HEAD(&p->ns_siblings[i]);
++
++		if (!ls->fltr_types[i])
++			continue;
++
++		rc = get_ns_ino(dirfd(dir), ns_names[i], &p->ns_ids[i]);
++		if (rc && rc != -EACCES)
++			goto done;
++		rc = 0;
++	}
++
++	INIT_LIST_HEAD(&p->processes);
++
++	DBG(PROC, ul_debugobj(p, "new pid=%d", p->pid));
++	list_add_tail(&p->processes, &ls->processes);
++done:
++	if (f)
++		fclose(f);
++	closedir(dir);
++	if (rc)
++		free(p);
++	return rc;
++}
++
++static int read_processes(struct lsns *ls)
++{
++	struct proc_processes *proc = NULL;
++	pid_t pid;
++	int rc = 0;
++
++	DBG(PROC, ul_debug("opening /proc"));
++
++	if (!(proc = proc_open_processes())) {
++		rc = -errno;
++		goto done;
++	}
++
++	while (proc_next_pid(proc, &pid) == 0) {
++		rc = read_process(ls, pid);
++		if (rc && rc != -EACCES && rc != -ENOENT)
++			break;
++		rc = 0;
++	}
++done:
++	DBG(PROC, ul_debug("closing /proc"));
++	proc_close_processes(proc);
++	return rc;
++}
++
++static struct lsns_namespace *get_namespace(struct lsns *ls, ino_t ino)
++{
++	struct list_head *p;
++
++	list_for_each(p, &ls->namespaces) {
++		struct lsns_namespace *ns = list_entry(p, struct lsns_namespace, namespaces);
++
++		if (ns->id == ino)
++			return ns;
++	}
++	return NULL;
++}
++
++static int namespace_has_process(struct lsns_namespace *ns, pid_t pid)
++{
++	struct list_head *p;
++
++	list_for_each(p, &ns->processes) {
++		struct lsns_process *proc = list_entry(p, struct lsns_process, ns_siblings[ns->type]);
++
++		if (proc->pid == pid)
++			return 1;
++	}
++	return 0;
++}
++
++static struct lsns_namespace *add_namespace(struct lsns *ls, int type, ino_t ino)
++{
++	struct lsns_namespace *ns = xcalloc(1, sizeof(*ns));
++
++	if (!ns)
++		return NULL;
++
++	DBG(NS, ul_debugobj(ns, "new %s[%ju]", ns_names[type], (uintmax_t)ino));
++
++	INIT_LIST_HEAD(&ns->processes);
++	INIT_LIST_HEAD(&ns->namespaces);
++
++	ns->type = type;
++	ns->id = ino;
++
++	list_add_tail(&ns->namespaces, &ls->namespaces);
++	return ns;
++}
++
++static int add_process_to_namespace(struct lsns *ls, struct lsns_namespace *ns, struct lsns_process *proc)
++{
++	struct list_head *p;
++
++	DBG(NS, ul_debugobj(ns, "add process [%p] pid=%d to %s[%ju]",
++		proc, proc->pid, ns_names[ns->type], (uintmax_t)ns->id));
++
++	list_for_each(p, &ls->processes) {
++		struct lsns_process *xproc = list_entry(p, struct lsns_process, processes);
++
++		if (xproc->pid == proc->ppid)		/* my parent */
++			proc->parent = xproc;
++		else if (xproc->ppid == proc->pid)	/* my child */
++			xproc->parent = proc;
++	}
++
++	list_add_tail(&proc->ns_siblings[ns->type], &ns->processes);
++	ns->nprocs++;
++
++	if (!ns->proc || ns->proc->pid > proc->pid)
++		ns->proc = proc;
++
++	return 0;
++}
++
++static int cmp_namespaces(struct list_head *a, struct list_head *b,
++			  __attribute__((__unused__)) void *data)
++{
++	struct lsns_namespace *xa = list_entry(a, struct lsns_namespace, namespaces),
++			      *xb = list_entry(b, struct lsns_namespace, namespaces);
++
++	return cmp_numbers(xa->id, xb->id);
++}
++
++static int read_namespaces(struct lsns *ls)
++{
++	struct list_head *p;
++
++	DBG(NS, ul_debug("reading namespace"));
++
++	list_for_each(p, &ls->processes) {
++		size_t i;
++		struct lsns_namespace *ns;
++		struct lsns_process *proc = list_entry(p, struct lsns_process, processes);
++
++		for (i = 0; i < ARRAY_SIZE(proc->ns_ids); i++) {
++			if (proc->ns_ids[i] == 0)
++				continue;
++			if (!(ns = get_namespace(ls, proc->ns_ids[i]))) {
++				ns = add_namespace(ls, i, proc->ns_ids[i]);
++				if (!ns)
++					return -ENOMEM;
++			}
++			add_process_to_namespace(ls, ns, proc);
++		}
++	}
++
++	list_sort(&ls->namespaces, cmp_namespaces, NULL);
++
++	return 0;
++}
++
++static void add_scols_line(struct lsns *ls, struct libscols_table *table,
++			   struct lsns_namespace *ns, struct lsns_process *proc)
++{
++	size_t i;
++	struct libscols_line *line;
++
++	assert(ns);
++	assert(table);
++
++	line = scols_table_new_line(table,
++			ls->tree && proc->parent ? proc->parent->outline : NULL);
++	if (!line) {
++		warn(_("failed to add line to output"));
++		return;
++	}
++
++	for (i = 0; i < ncolumns; i++) {
++		char *str = NULL;
++
++		switch (get_column_id(i)) {
++		case COL_NS:
++			xasprintf(&str, "%ju", (uintmax_t)ns->id);
++			break;
++		case COL_PID:
++			xasprintf(&str, "%d", (int) proc->pid);
++			break;
++		case COL_PPID:
++			xasprintf(&str, "%d", (int) proc->ppid);
++			break;
++		case COL_TYPE:
++			xasprintf(&str, "%s", ns_names[ns->type]);
++			break;
++		case COL_NPROCS:
++			xasprintf(&str, "%d", ns->nprocs);
++			break;
++		case COL_COMMAND:
++			str = proc_get_command(proc->pid);
++			if (!str)
++				str = proc_get_command_name(proc->pid);
++			break;
++		case COL_PATH:
++			xasprintf(&str, "/proc/%d/ns/%s", (int) proc->pid, ns_names[ns->type]);
++			break;
++		case COL_UID:
++			xasprintf(&str, "%d", (int) proc->uid);
++			break;
++		case COL_USER:
++			xasprintf(&str, "%s", get_id(uid_cache, proc->uid)->name);
++			break;
++		default:
++			break;
++		}
++
++		if (str)
++			scols_line_set_data(line, i, str);
++	}
++
++	proc->outline = line;
++}
++
++static struct libscols_table *init_scols_table(struct lsns *ls)
++{
++	struct libscols_table *tab;
++	size_t i;
++
++	tab = scols_new_table();
++	if (!tab) {
++		warn(_("failed to initialize output table"));
++		return NULL;
++	}
++
++	scols_table_enable_raw(tab, ls->raw);
++	scols_table_enable_noheadings(tab, ls->no_headings);
++
++	for (i = 0; i < ncolumns; i++) {
++		const struct colinfo *col = get_column_info(i);
++		int flags = col->flags;
++
++		if (ls->notrunc)
++		       flags &= ~SCOLS_FL_TRUNC;
++		if (ls->tree && get_column_id(i) == COL_COMMAND)
++			flags |= SCOLS_FL_TREE;
++
++		if (!scols_table_new_column(tab, col->name, col->whint, flags)) {
++			warnx(_("failed to initialize output column"));
++			goto err;
++		}
++	}
++
++	return tab;
++err:
++	scols_unref_table(tab);
++	return NULL;
++}
++
++static int show_namespaces(struct lsns *ls)
++{
++	struct libscols_table *tab;
++	struct list_head *p;
++	int rc = 0;
++
++	tab = init_scols_table(ls);
++	if (!tab)
++		return -ENOMEM;
++
++	list_for_each(p, &ls->namespaces) {
++		struct lsns_namespace *ns = list_entry(p, struct lsns_namespace, namespaces);
++
++		if (ls->fltr_pid != 0 && !namespace_has_process(ns, ls->fltr_pid))
++			continue;
++
++		add_scols_line(ls, tab, ns, ns->proc);
++	}
++
++	scols_print_table(tab);
++	scols_unref_table(tab);
++	return rc;
++}
++
++static void show_process(struct lsns *ls, struct libscols_table *tab,
++			 struct lsns_process *proc, struct lsns_namespace *ns)
++{
++	/*
++	 * create a tree from parent->child relation, but only if the parent is
++	 * within the same namespace
++	 */
++	if (ls->tree
++	    && proc->parent
++	    && !proc->parent->outline
++	    && proc->parent->ns_ids[ns->type] == proc->ns_ids[ns->type])
++		show_process(ls, tab, proc->parent, ns);
++
++	add_scols_line(ls, tab, ns, proc);
++}
++
++
++static int show_namespace_processes(struct lsns *ls, struct lsns_namespace *ns)
++{
++	struct libscols_table *tab;
++	struct list_head *p;
++
++	tab = init_scols_table(ls);
++	if (!tab)
++		return -ENOMEM;
++
++	list_for_each(p, &ns->processes) {
++		struct lsns_process *proc = list_entry(p, struct lsns_process, ns_siblings[ns->type]);
++
++		if (!proc->outline)
++			show_process(ls, tab, proc, ns);
++	}
++
++
++	scols_print_table(tab);
++	scols_unref_table(tab);
++	return 0;
++}
++
++static void __attribute__ ((__noreturn__)) usage(FILE * out)
++{
++	size_t i;
++
++	fputs(USAGE_HEADER, out);
++
++	fprintf(out,
++		_(" %s [options] [<namespace>]\n"), program_invocation_short_name);
++
++	fputs(USAGE_SEPARATOR, out);
++	fputs(_("List system namespaces.\n"), out);
++
++	fputs(USAGE_OPTIONS, out);
++	fputs(_(" -l, --list             use list format output\n"), out);
++	fputs(_(" -n, --noheadings       don't print headings\n"), out);
++	fputs(_(" -o, --output <list>    define which output columns to use\n"), out);
++	fputs(_(" -p, --task <pid>       print process namespaces\n"), out);
++	fputs(_(" -r, --raw              use the raw output format\n"), out);
++	fputs(_(" -u, --notruncate       don't truncate text in columns\n"), out);
++	fputs(_(" -t, --type <name>      namespace type (mnt, net, ipc, user, pid, uts)\n"), out);
++
++	fputs(USAGE_SEPARATOR, out);
++	fputs(USAGE_HELP, out);
++	fputs(USAGE_VERSION, out);
++
++	fputs(_("\nAvailable columns (for --output):\n"), out);
++
++	for (i = 0; i < ARRAY_SIZE(infos); i++)
++		fprintf(out, " %11s  %s\n", infos[i].name, _(infos[i].help));
++
++	fprintf(out, USAGE_MAN_TAIL("lsns(8)"));
++
++	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
++}
++
++
++int main(int argc, char *argv[])
++{
++	struct lsns ls;
++	int c;
++	int r = 0;
++	char *outarg = NULL;
++	static const struct option long_opts[] = {
++		{ "task",       required_argument, NULL, 'p' },
++		{ "help",	no_argument,       NULL, 'h' },
++		{ "output",     required_argument, NULL, 'o' },
++		{ "notruncate", no_argument,       NULL, 'u' },
++		{ "version",    no_argument,       NULL, 'V' },
++		{ "noheadings", no_argument,       NULL, 'n' },
++		{ "list",       no_argument,       NULL, 'l' },
++		{ "raw",        no_argument,       NULL, 'r' },
++		{ "type",       required_argument, NULL, 't' },
++		{ NULL, 0, NULL, 0 }
++	};
++
++	static const ul_excl_t excl[] = {	/* rows and cols in ASCII order */
++		{ 'J','r' },
++		{ 0 }
++	};
++	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
++
++	setlocale(LC_ALL, "");
++	bindtextdomain(PACKAGE, LOCALEDIR);
++	textdomain(PACKAGE);
++	atexit(close_stdout);
++
++	lsns_init_debug();
++	memset(&ls, 0, sizeof(ls));
++
++	INIT_LIST_HEAD(&ls.processes);
++	INIT_LIST_HEAD(&ls.namespaces);
++
++	while ((c = getopt_long(argc, argv,
++				"lp:o:nruhVt:", long_opts, NULL)) != -1) {
++
++		err_exclusive_options(c, long_opts, excl, excl_st);
++
++		switch(c) {
++		case 'l':
++			ls.list = 1;
++			break;
++		case 'o':
++			outarg = optarg;
++			break;
++		case 'V':
++			printf(UTIL_LINUX_VERSION);
++			return EXIT_SUCCESS;
++		case 'p':
++			ls.fltr_pid = strtos32_or_err(optarg, _("invalid PID argument"));
++			break;
++		case 'h':
++			usage(stdout);
++		case 'n':
++			ls.no_headings = 1;
++			break;
++		case 'r':
++			ls.raw = 1;
++			break;
++		case 'u':
++			ls.notrunc = 1;
++			break;
++		case 't':
++		{
++			int type = ns_name2type(optarg);
++			if (type < 0)
++				errx(EXIT_FAILURE, _("unknown namespace type: %s"), optarg);
++			ls.fltr_types[type] = 1;
++			ls.fltr_ntypes++;
++			break;
++		}
++		case '?':
++		default:
++			usage(stderr);
++		}
++	}
++
++	if (!ls.fltr_ntypes) {
++		size_t i;
++		for (i = 0; i < ARRAY_SIZE(ns_names); i++)
++			ls.fltr_types[i] = 1;
++	}
++
++	if (optind < argc) {
++		if (ls.fltr_pid)
++			errx(EXIT_FAILURE, _("--task is mutually exclusive with <namespace>"));
++		ls.fltr_ns = strtou64_or_err(argv[optind], _("invalid namespace argument"));
++		ls.tree = ls.list ? 0 : 1;
++
++		if (!ncolumns) {
++			columns[ncolumns++] = COL_PID;
++			columns[ncolumns++] = COL_PPID;
++			columns[ncolumns++] = COL_USER;
++			columns[ncolumns++] = COL_COMMAND;
++		}
++	}
++
++	if (!ncolumns) {
++		columns[ncolumns++] = COL_NS;
++		columns[ncolumns++] = COL_TYPE;
++		columns[ncolumns++] = COL_NPROCS;
++		columns[ncolumns++] = COL_PID;
++		columns[ncolumns++] = COL_USER;
++		columns[ncolumns++] = COL_COMMAND;
++	}
++
++	if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
++					(int *) &ncolumns, column_name_to_id) < 0)
++		return EXIT_FAILURE;
++
++	scols_init_debug(0);
++
++	uid_cache = new_idcache();
++	if (!uid_cache)
++		err(EXIT_FAILURE, _("failed to allocate UID cache"));
++
++	r = read_processes(&ls);
++	if (!r)
++		r = read_namespaces(&ls);
++	if (!r) {
++		if (ls.fltr_ns) {
++			struct lsns_namespace *ns = get_namespace(&ls, ls.fltr_ns);
++
++			if (!ns)
++				errx(EXIT_FAILURE, _("not found namespace: %ju"), (uintmax_t) ls.fltr_ns);
++			r = show_namespace_processes(&ls, ns);
++		} else
++			r = show_namespaces(&ls);
++	}
++
++	free_idcache(uid_cache);
++	return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
++}
+-- 
+2.7.4
+
diff --git a/SOURCES/0077-lib-strutils-make-strmode-more-generic.patch b/SOURCES/0077-lib-strutils-make-strmode-more-generic.patch
new file mode 100644
index 0000000..5534e62
--- /dev/null
+++ b/SOURCES/0077-lib-strutils-make-strmode-more-generic.patch
@@ -0,0 +1,76 @@
+From 7bf448fe38478b6e76824fa5bbd2d2d25a48f618 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Tue, 30 Jun 2015 12:41:13 +0200
+Subject: [PATCH 77/84] lib/strutils: make strmode() more generic
+
+Upstream: http://github.com/karelzak/util-linux/commit/7015df4936ca320a86d2916533a17499ac5e4fcf
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1153770
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ lib/strutils.c | 36 +++++++++++++++++++-----------------
+ 1 file changed, 19 insertions(+), 17 deletions(-)
+
+diff --git a/lib/strutils.c b/lib/strutils.c
+index c263b86..f9cdcbb 100644
+--- a/lib/strutils.c
++++ b/lib/strutils.c
+@@ -349,37 +349,39 @@ void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmes
+  */
+ void strmode(mode_t mode, char *str)
+ {
++	unsigned short i = 0;
++
+ 	if (S_ISDIR(mode))
+-		str[0] = 'd';
++		str[i++] = 'd';
+ 	else if (S_ISLNK(mode))
+-		str[0] = 'l';
++		str[i++] = 'l';
+ 	else if (S_ISCHR(mode))
+-		str[0] = 'c';
++		str[i++] = 'c';
+ 	else if (S_ISBLK(mode))
+-		str[0] = 'b';
++		str[i++] = 'b';
+ 	else if (S_ISSOCK(mode))
+-		str[0] = 's';
++		str[i++] = 's';
+ 	else if (S_ISFIFO(mode))
+-		str[0] = 'p';
++		str[i++] = 'p';
+ 	else if (S_ISREG(mode))
+-		str[0] = '-';
++		str[i++] = '-';
+ 
+-	str[1] = mode & S_IRUSR ? 'r' : '-';
+-	str[2] = mode & S_IWUSR ? 'w' : '-';
+-	str[3] = (mode & S_ISUID
++	str[i++] = mode & S_IRUSR ? 'r' : '-';
++	str[i++] = mode & S_IWUSR ? 'w' : '-';
++	str[i++] = (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
++	str[i++] = mode & S_IRGRP ? 'r' : '-';
++	str[i++] = mode & S_IWGRP ? 'w' : '-';
++	str[i++] = (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
++	str[i++] = mode & S_IROTH ? 'r' : '-';
++	str[i++] = mode & S_IWOTH ? 'w' : '-';
++	str[i++] = (mode & S_ISVTX
+ 		? (mode & S_IXOTH ? 't' : 'T')
+ 		: (mode & S_IXOTH ? 'x' : '-'));
+-	str[10] = '\0';
++	str[i] = '\0';
+ }
+ 
+ /*
+-- 
+2.7.4
+
diff --git a/SOURCES/0078-lsipc-backport-new-command.patch b/SOURCES/0078-lsipc-backport-new-command.patch
new file mode 100644
index 0000000..e87be77
--- /dev/null
+++ b/SOURCES/0078-lsipc-backport-new-command.patch
@@ -0,0 +1,2000 @@
+From 9f643efe377d2a39929f19cc09e8890afc74d9a4 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Fri, 24 Jun 2016 12:57:13 +0200
+Subject: [PATCH 78/84] lsipc: backport new command
+
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1153770
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ bash-completion/lsipc   |   64 +++
+ configure.ac            |    6 +
+ include/xalloc.h        |    7 +
+ sys-utils/Makemodule.am |   10 +
+ sys-utils/ipcs.c        |    2 +-
+ sys-utils/ipcutils.c    |  116 ++---
+ sys-utils/ipcutils.h    |   13 +-
+ sys-utils/lsipc.1       |  133 +++++
+ sys-utils/lsipc.c       | 1316 +++++++++++++++++++++++++++++++++++++++++++++++
+ tests/functions.sh      |   16 +-
+ tests/ts/ipcs/limits2   |    9 +-
+ 11 files changed, 1613 insertions(+), 79 deletions(-)
+ create mode 100644 bash-completion/lsipc
+ create mode 100644 sys-utils/lsipc.1
+ create mode 100644 sys-utils/lsipc.c
+
+diff --git a/bash-completion/lsipc b/bash-completion/lsipc
+new file mode 100644
+index 0000000..6a87393
+--- /dev/null
++++ b/bash-completion/lsipc
+@@ -0,0 +1,64 @@
++_lsipc_module()
++{
++	local cur prev OPTS ARG
++	COMPREPLY=()
++	cur="${COMP_WORDS[COMP_CWORD]}"
++	prev="${COMP_WORDS[COMP_CWORD-1]}"
++	case $prev in
++		'-i'|'--id')
++			COMPREPLY=( $(compgen -W "id" -- $cur) )
++			return 0
++			;;
++		'-h'|'--help'|'-V'|'--version')
++			return 0
++			;;
++		'-o'|'--output')
++			local prefix realcur OUTPUT_ALL OUTPUT
++			realcur="${cur##*,}"
++			prefix="${cur%$realcur}"
++			OUTPUT_ALL="GENERAL KEY ID OWNER PERMS CUID
++			CGID UID GID CHANGE MESSAGE USEDBYTES
++			MSGS SEND RECV LSPID LRPID SHARED BYTES
++			NATTCH STATUS ATTACH DETACH CPID LPID NSEMS
++			LASTOP"
++			for WORD in $OUTPUT_ALL; do
++				if ! [[ $prefix == *"$WORD"* ]]; then
++					OUTPUT="$WORD $OUTPUT"
++				fi
++			done
++			compopt -o nospace
++			COMPREPLY=( $(compgen -P "$prefix" -W "$OUTPUT" -S ',' -- $realcur) )
++			return 0
++			;;
++	esac
++	case $cur in
++		-*)
++			OPTS="--id
++				--help
++				--version
++				--shmems
++				--queues
++				--semaphores
++				--colon-separate
++				--creator
++				--export
++				--global
++				--newline
++				--noheadings
++				--notruncate
++				--output
++				--pid
++				--print0
++				--raw
++				--time
++				--time-format"
++			COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
++			return 0
++			;;
++	esac
++	local IFS=$'\n'
++	compopt -o filenames
++	COMPREPLY=( $(compgen -f -- $cur) )
++	return 0
++}
++complete -F _lsipc_module lsipc
+diff --git a/configure.ac b/configure.ac
+index 5d9ea39..fe0a011 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -1038,6 +1038,12 @@ UL_REQUIRES_BUILD([lsns], [libsmartcols])
+ AM_CONDITIONAL([BUILD_LSNS], [test "x$build_lsns" = xyes])
+ 
+ 
++UL_BUILD_INIT([lsipc], [check])
++UL_REQUIRES_LINUX([lsipc])
++UL_REQUIRES_BUILD([lsipc], [libsmartcols])
++AM_CONDITIONAL([BUILD_LSIPC], [test "x$build_lsipc" = xyes])
++
++
+ UL_BUILD_INIT([chcpu], [check])
+ UL_REQUIRES_LINUX([chcpu])
+ UL_REQUIRES_HAVE([chcpu], [cpu_set_t], [cpu_set_t type])
+diff --git a/include/xalloc.h b/include/xalloc.h
+index 6342793..1a1799a 100644
+--- a/include/xalloc.h
++++ b/include/xalloc.h
+@@ -19,6 +19,13 @@
+ # define XALLOC_EXIT_CODE EXIT_FAILURE
+ #endif
+ 
++static inline void __err_oom(const char *file, unsigned int line)
++{
++	err(XALLOC_EXIT_CODE, "%s: %u: cannot allocate memory", file, line);
++}
++
++#define err_oom()	__err_oom(__FILE__, __LINE__)
++
+ static inline __ul_alloc_size(1)
+ void *xmalloc(const size_t size)
+ {
+diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am
+index 9baf5a3..6badd17 100644
+--- a/sys-utils/Makemodule.am
++++ b/sys-utils/Makemodule.am
+@@ -22,6 +22,16 @@ ipcs_SOURCES =	sys-utils/ipcs.c \
+ ipcs_LDADD = $(LDADD) libcommon.la
+ 
+ 
++if BUILD_LSIPC
++usrbin_exec_PROGRAMS += lsipc
++dist_man_MANS += sys-utils/lsipc.1
++lsipc_SOURCES =	sys-utils/lsipc.c \
++		sys-utils/ipcutils.c \
++		sys-utils/ipcutils.h
++lsipc_LDADD = $(LDADD) libcommon.la libsmartcols.la
++lsipc_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir)
++endif
++
+ usrbin_exec_PROGRAMS += renice
+ dist_man_MANS += sys-utils/renice.1
+ renice_SOURCES = sys-utils/renice.c
+diff --git a/sys-utils/ipcs.c b/sys-utils/ipcs.c
+index 14f5f0b..1843cd5 100644
+--- a/sys-utils/ipcs.c
++++ b/sys-utils/ipcs.c
+@@ -201,7 +201,7 @@ static void do_shm (char format, int unit)
+ 			       _("max seg size"), lim.shmmax, "\n", 0);
+ 		ipc_print_size(unit == IPC_UNIT_DEFAULT ? IPC_UNIT_KB : unit,
+ 			       _("max total shared memory"),
+-			       lim.shmall * getpagesize(), "\n", 0);
++			       (uint64_t) lim.shmall * getpagesize(), "\n", 0);
+ 		ipc_print_size(unit == IPC_UNIT_DEFAULT ? IPC_UNIT_BYTES : unit,
+ 			       _("min seg size"), lim.shmmin, "\n", 0);
+ 		return;
+diff --git a/sys-utils/ipcutils.c b/sys-utils/ipcutils.c
+index 62d7428..51fce7b 100644
+--- a/sys-utils/ipcutils.c
++++ b/sys-utils/ipcutils.c
+@@ -1,4 +1,3 @@
+-
+ #include <inttypes.h>
+ 
+ #include "c.h"
+@@ -54,8 +53,8 @@ int ipc_sem_get_limits(struct ipc_limits *lim)
+ 
+ 	}
+ 
+-	if (rc == 4) {
+-		struct seminfo seminfo;
++	if (rc != 4) {
++		struct seminfo seminfo = { .semmni = 0 };
+ 		union semun arg = { .array = (ushort *) &seminfo };
+ 
+ 		if (semctl(0, 0, IPC_INFO, arg) < 0)
+@@ -82,12 +81,15 @@ int ipc_shm_get_limits(struct ipc_limits *lim)
+ 		lim->shmmni = path_read_u64(_PATH_PROC_IPC_SHMMNI);
+ 
+ 	} else {
+-		struct shminfo shminfo;
++		struct shminfo *shminfo;
++		struct shmid_ds shmbuf;
+ 
+-		if (shmctl(0, IPC_INFO, (struct shmid_ds *) &shminfo) < 0)
++		if (shmctl(0, IPC_INFO, &shmbuf) < 0)
+ 			return 1;
+-		lim->shmmni = shminfo.shmmni;
+-		lim->shmall = shminfo.shmall;
++		shminfo = (struct shminfo *) &shmbuf;
++		lim->shmmni = shminfo->shmmni;
++		lim->shmall = shminfo->shmall;
++		lim->shmmax = shminfo->shmmax;
+ 	}
+ 
+ 	return 0;
+@@ -97,20 +99,24 @@ int ipc_shm_get_info(int id, struct shm_data **shmds)
+ {
+ 	FILE *f;
+ 	int i = 0, maxid;
++	char buf[BUFSIZ];
+ 	struct shm_data *p;
+-	struct shm_info dummy;
++	struct shmid_ds dummy;
+ 
+ 	p = *shmds = xcalloc(1, sizeof(struct shm_data));
+ 	p->next = NULL;
+ 
+ 	f = path_fopen("r", 0, _PATH_PROC_SYSV_SHM);
+ 	if (!f)
+-		goto fallback;
++		goto shm_fallback;
+ 
+ 	while (fgetc(f) != '\n');		/* skip header */
+ 
+-	while (feof(f) == 0) {
+-		if (fscanf(f,
++	while (fgets(buf, sizeof(buf), f) != NULL) {
++		/* scan for the first 14-16 columns (e.g. Linux 2.6.32 has 14) */
++		p->shm_rss = 0xdead;
++		p->shm_swp = 0xdead;
++		if (sscanf(buf,
+ 			  "%d %d  %o %"SCNu64 " %u %u  "
+ 			  "%"SCNu64 " %u %u %u %u %"SCNi64 " %"SCNi64 " %"SCNi64
+ 			  " %"SCNu64 " %"SCNu64 "\n",
+@@ -129,8 +135,8 @@ int ipc_shm_get_info(int id, struct shm_data **shmds)
+ 			   &p->shm_dtim,
+ 			   &p->shm_ctim,
+ 			   &p->shm_rss,
+-			   &p->shm_swp) != 16)
+-			continue;
++			   &p->shm_swp) < 14)
++			continue; /* invalid line, skipped */
+ 
+ 		if (id > -1) {
+ 			/* ID specified */
+@@ -153,28 +159,20 @@ int ipc_shm_get_info(int id, struct shm_data **shmds)
+ 	return i;
+ 
+ 	/* Fallback; /proc or /sys file(s) missing. */
+-fallback:
+-	i = id < 0 ? 0 : id;
+-
+-	maxid = shmctl(0, SHM_INFO, (struct shmid_ds *) &dummy);
+-	if (maxid < 0)
+-		return 0;
++shm_fallback:
++	maxid = shmctl(0, SHM_INFO, &dummy);
+ 
+-	while (i <= maxid) {
++	for (int j = 0; j <= maxid; j++) {
+ 		int shmid;
+ 		struct shmid_ds shmseg;
+ 		struct ipc_perm *ipcp = &shmseg.shm_perm;
+ 
+-		shmid = shmctl(i, SHM_STAT, &shmseg);
+-		if (shmid < 0) {
+-			if (-1 < id) {
+-				free(*shmds);
+-				return 0;
+-			}
+-			i++;
++		shmid = shmctl(j, SHM_STAT, &shmseg);
++		if (shmid < 0 || (id > -1 && shmid != id)) {
+ 			continue;
+ 		}
+ 
++		i++;
+ 		p->shm_perm.key = ipcp->KEY;
+ 		p->shm_perm.id = shmid;
+ 		p->shm_perm.mode = ipcp->mode;
+@@ -196,11 +194,12 @@ fallback:
+ 			p->next = xcalloc(1, sizeof(struct shm_data));
+ 			p = p->next;
+ 			p->next = NULL;
+-			i++;
+ 		} else
+-			return 1;
++			break;
+ 	}
+ 
++	if (i == 0)
++		free(*shmds);
+ 	return i;
+ }
+ 
+@@ -299,30 +298,22 @@ int ipc_sem_get_info(int id, struct sem_data **semds)
+ 	return i;
+ 
+ 	/* Fallback; /proc or /sys file(s) missing. */
+- sem_fallback:
+-	i = id < 0 ? 0 : id;
+-
++sem_fallback:
+ 	arg.array = (ushort *) (void *)&dummy;
+ 	maxid = semctl(0, 0, SEM_INFO, arg);
+-	if (maxid < 0)
+-		return 0;
+ 
+-	while (i <= maxid) {
++	for (int j = 0; j <= maxid; j++) {
+ 		int semid;
+ 		struct semid_ds semseg;
+ 		struct ipc_perm *ipcp = &semseg.sem_perm;
+ 		arg.buf = (struct semid_ds *)&semseg;
+ 
+-		semid = semctl(i, 0, SEM_STAT, arg);
+-		if (semid < 0) {
+-			if (-1 < id) {
+-				free(*semds);
+-				return 0;
+-			}
+-			i++;
++		semid = semctl(j, 0, SEM_STAT, arg);
++		if (semid < 0 || (id > -1 && semid != id)) {
+ 			continue;
+ 		}
+ 
++		i++;
+ 		p->sem_perm.key = ipcp->KEY;
+ 		p->sem_perm.id = semid;
+ 		p->sem_perm.mode = ipcp->mode;
+@@ -341,10 +332,12 @@ int ipc_sem_get_info(int id, struct sem_data **semds)
+ 			i++;
+ 		} else {
+ 			get_sem_elements(p);
+-			return 1;
++			break;
+ 		}
+ 	}
+ 
++	if (i == 0)
++		free(*semds);
+ 	return i;
+ }
+ 
+@@ -398,10 +391,6 @@ int ipc_msg_get_info(int id, struct msg_data **msgds)
+ 		if (id > -1) {
+ 			/* ID specified */
+ 			if (id == p->msg_perm.id) {
+-				/*
+-				 * FIXME: q_qbytes are not in /proc
+-				 *
+-				 */
+ 				if (msgctl(id, IPC_STAT, &msgseg) != -1)
+ 					p->q_qbytes = msgseg.msg_qbytes;
+ 				i = 1;
+@@ -422,27 +411,19 @@ int ipc_msg_get_info(int id, struct msg_data **msgds)
+ 	return i;
+ 
+ 	/* Fallback; /proc or /sys file(s) missing. */
+- msg_fallback:
+-	i = id < 0 ? 0 : id;
+-
+-	maxid = msgctl(id, MSG_STAT, &dummy);
+-	if (maxid < 0)
+-		return 0;
++msg_fallback:
++	maxid = msgctl(0, MSG_INFO, &dummy);
+ 
+-	while (i <= maxid) {
++	for (int j = 0; j <= maxid; j++) {
+ 		int msgid;
+ 		struct ipc_perm *ipcp = &msgseg.msg_perm;
+ 
+-		msgid = msgctl(i, MSG_STAT, &msgseg);
+-		if (msgid < 0) {
+-			if (-1 < id) {
+-				free(*msgds);
+-				return 0;
+-			}
+-			i++;
++		msgid = msgctl(j, MSG_STAT, &msgseg);
++		if (msgid < 0 || (id > -1 && msgid != id)) {
+ 			continue;
+ 		}
+ 
++		i++;
+ 		p->msg_perm.key = ipcp->KEY;
+ 		p->msg_perm.id = msgid;
+ 		p->msg_perm.mode = ipcp->mode;
+@@ -463,11 +444,12 @@ int ipc_msg_get_info(int id, struct msg_data **msgds)
+ 			p->next = xcalloc(1, sizeof(struct msg_data));
+ 			p = p->next;
+ 			p->next = NULL;
+-			i++;
+ 		} else
+-			return 1;
++			break;
+ 	}
+ 
++	if (i == 0)
++		free(*msgds);
+ 	return i;
+ }
+ 
+@@ -508,10 +490,10 @@ void ipc_print_perms(FILE *f, struct ipc_stat *is)
+ 		fprintf(f, " %-10u\n", is->gid);
+ }
+ 
+-void ipc_print_size(int unit, char *msg, size_t size, const char *end,
++void ipc_print_size(int unit, char *msg, uint64_t size, const char *end,
+ 		    int width)
+ {
+-	char format[16];
++	char format[32];
+ 
+ 	if (!msg)
+ 		/* NULL */ ;
+@@ -527,11 +509,11 @@ void ipc_print_size(int unit, char *msg, size_t size, const char *end,
+ 	switch (unit) {
+ 	case IPC_UNIT_DEFAULT:
+ 	case IPC_UNIT_BYTES:
+-		sprintf(format, "%%%dzu", width);
++		sprintf(format, "%%%dju", width);
+ 		printf(format, size);
+ 		break;
+ 	case IPC_UNIT_KB:
+-		sprintf(format, "%%%dzu", width);
++		sprintf(format, "%%%dju", width);
+ 		printf(format, size / 1024);
+ 		break;
+ 	case IPC_UNIT_HUMAN:
+diff --git a/sys-utils/ipcutils.h b/sys-utils/ipcutils.h
+index d2e5972..444065a 100644
+--- a/sys-utils/ipcutils.h
++++ b/sys-utils/ipcutils.h
+@@ -12,6 +12,7 @@
+ #include <unistd.h>
+ #include <grp.h>
+ #include <pwd.h>
++#include <stdint.h>
+ 
+ /*
+  * SHM_DEST and SHM_LOCKED are defined in kernel headers, but inside
+@@ -34,11 +35,11 @@
+ # define SHM_INFO	14
+ struct shm_info {
+ 	int used_ids;
+-	ulong shm_tot;		/* total allocated shm */
+-	ulong shm_rss;		/* total resident shm */
+-	ulong shm_swp;		/* total swapped shm */
+-	ulong swap_attempts;
+-	ulong swap_successes;
++	unsigned long shm_tot;		/* total allocated shm */
++	unsigned long shm_rss;		/* total resident shm */
++	unsigned long shm_swp;		/* total swapped shm */
++	unsigned long swap_attempts;
++	unsigned long swap_successes;
+ };
+ #endif
+ 
+@@ -118,7 +119,7 @@ struct ipc_stat {
+ };
+ 
+ extern void ipc_print_perms(FILE *f, struct ipc_stat *is);
+-extern void ipc_print_size(int unit, char *msg, size_t size, const char *end, int width);
++extern void ipc_print_size(int unit, char *msg, uint64_t size, const char *end, int width);
+ 
+ /* See 'struct shmid_kernel' in kernel sources
+  */
+diff --git a/sys-utils/lsipc.1 b/sys-utils/lsipc.1
+new file mode 100644
+index 0000000..98449cb
+--- /dev/null
++++ b/sys-utils/lsipc.1
+@@ -0,0 +1,133 @@
++.\" Copyright 2015 Ondrej Oprala(ooprala@redhat.com)
++.\" May be distributed under the GNU General Public License
++.TH LSIPC "1" "November 2015" "util-linux" "User Commands"
++.SH NAME
++lsipc \- show information on IPC facilities currently employed in the system
++.SH SYNOPSIS
++.B lsipc
++[options]
++.SH DESCRIPTION
++.B lsipc
++shows information on the inter-process communication facilities
++for which the calling process has read access.
++.SH OPTIONS
++.TP
++\fB\-i\fR, \fB\-\-id\fR \fIid\fR
++Show full details on just the one resource element identified by
++.IR id .
++This option needs to be combined with one of the three resource options:
++.BR \-m ,
++.BR \-q " or"
++.BR \-s .
++It is possible to override the default output format for this option with the
++\fB\-\-list\fR, \fB\-\-raw\fR, \fB\-\-json\fR or \fB\-\-export\fR option.
++.TP
++\fB\-g\fR, \fB\-\-global\fR
++Show system-wide usage and limits of IPC resources.
++This option may be combined with one of the three resource options:
++.BR \-m ,
++.BR \-q " or"
++.BR \-s .
++The default is to show information about all resources.
++.TP
++\fB\-h\fR, \fB\-\-help\fR
++Display help text and exit.
++.TP
++\fB\-V\fR, \fB\-\-version\fR
++Display version information and exit.
++.SS "Resource options"
++.TP
++\fB\-m\fR, \fB\-\-shmems\fR
++Write information about active shared memory segments.
++.TP
++\fB\-q\fR, \fB\-\-queues\fR
++Write information about active message queues.
++.TP
++\fB\-s\fR, \fB\-\-semaphores\fR
++Write information about active semaphore sets.
++.SS "Output formatting"
++.TP
++\fB\-c\fR, \fB\-\-creator\fR
++Show creator and owner.
++.TP
++\fB\-e\fR, \fB\-\-export\fR
++Output data in the format of NAME=VALUE.
++.TP
++\fB\-l\fR, \fB\-\-list\fR
++Use the list output format.  This is the default, except when \fB\-\-id\fR
++is used.
++.TP
++\fB\-n\fR, \fB\-\-newline\fR
++Display each piece of information on a separate line.
++.TP
++\fB\-\-noheadings\fR
++Do not print a header line.
++.TP
++\fB\-\-notruncate\fR
++Don't truncate output.
++.TP
++\fB\-o\fR, \fB\-\-output \fIlist\fP
++Specify which output columns to print.  Use
++.B \-\-help
++to get a list of all supported columns.
++.TP
++\fB\-p\fR, \fB\-\-pid\fR
++Show PIDs of creator and last operator.
++.TP
++\fB\-r\fR, \fB\-\-raw\fR
++Raw output (no columnation).
++.TP
++\fB\-t\fR, \fB\-\-time\fR
++Write time information.  The time of the last control operation that changed
++the access permissions for all facilities, the time of the last
++.I msgsnd()
++and
++.I msgrcv()
++operations on message queues, the time of the last
++.I shmat()
++and
++.I shmdt()
++operations on shared memory, and the time of the last
++.I semop()
++operation on semaphores.
++.TP
++\fB\-\-time\-format\fR \fItype\fP
++Display dates in short, full or iso format.  The default is short, this time
++format is designed to be space efficient and human readable.
++
++.SH EXIT STATUS
++.TP
++0
++if OK,
++.TP
++1
++if incorrect arguments specified,
++.TP
++2
++if a serious error occurs.
++.SH SEE ALSO
++.BR ipcrm (1),
++.BR ipcmk (1),
++.BR msgrcv (2),
++.BR msgsnd (2),
++.BR semget (2),
++.BR semop (2),
++.BR shmat (2),
++.BR shmdt (2),
++.BR shmget (2)
++.SH HISTORY
++The \fBlsipc\fP utility is inspired by the \fBipcs\fP utility.
++.SH AUTHORS
++.MT ooprala@redhat.com
++Ondrej Oprala
++.ME
++.br
++.MT kzak@redhat.com
++Karel Zak
++.ME
++
++.SH AVAILABILITY
++The lsipc command 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/sys-utils/lsipc.c b/sys-utils/lsipc.c
+new file mode 100644
+index 0000000..0be9d91
+--- /dev/null
++++ b/sys-utils/lsipc.c
+@@ -0,0 +1,1316 @@
++/*
++ * lsipc - List information about IPC instances employed in the system
++ *
++ * Copyright (C) 2015 Ondrej Oprala <ooprala@redhat.com>
++ * Copyright (C) 2015 Karel Zak <ooprala@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.
++ *
++ *
++ * lsipc is inspired by the ipcs utility. The aim is to create
++ * a utility unencumbered by a standard to provide more flexible
++ * means of controlling the output.
++ */
++
++#include <errno.h>
++#include <getopt.h>
++#include <sys/time.h>
++#include <unistd.h>
++
++#include <libsmartcols.h>
++
++#include "c.h"
++#include "nls.h"
++#include "closestream.h"
++#include "strutils.h"
++#include "optutils.h"
++#include "xalloc.h"
++#include "procutils.h"
++#include "ipcutils.h"
++
++/*
++ * time modes
++ * */
++enum {
++	TIME_INVALID = 0,
++	TIME_SHORT,
++	TIME_FULL,
++	TIME_ISO
++};
++
++/*
++ * IDs
++ */
++enum {
++	/* generic */
++	COLDESC_IDX_GEN_FIRST = 0,
++		COL_KEY = COLDESC_IDX_GEN_FIRST,
++		COL_ID,
++		COL_OWNER,
++		COL_PERMS,
++		COL_CUID,
++		COL_CUSER,
++		COL_CGID,
++		COL_CGROUP,
++		COL_UID,
++		COL_USER,
++		COL_GID,
++		COL_GROUP,
++		COL_CTIME,
++	COLDESC_IDX_GEN_LAST = COL_CTIME,
++
++	/* msgq-specific */
++	COLDESC_IDX_MSG_FIRST,
++		COL_USEDBYTES = COLDESC_IDX_MSG_FIRST,
++		COL_MSGS,
++		COL_SEND,
++		COL_RECV,
++		COL_LSPID,
++		COL_LRPID,
++	COLDESC_IDX_MSG_LAST = COL_LRPID,
++
++	/* shm-specific */
++	COLDESC_IDX_SHM_FIRST,
++		COL_SIZE = COLDESC_IDX_SHM_FIRST,
++		COL_NATTCH,
++		COL_STATUS,
++		COL_ATTACH,
++		COL_DETACH,
++		COL_COMMAND,
++		COL_CPID,
++		COL_LPID,
++	COLDESC_IDX_SHM_LAST = COL_LPID,
++
++	/* sem-specific */
++	COLDESC_IDX_SEM_FIRST,
++		COL_NSEMS = COLDESC_IDX_SEM_FIRST,
++		COL_OTIME,
++	COLDESC_IDX_SEM_LAST = COL_OTIME,
++
++	/* summary (--global) */
++	COLDESC_IDX_SUM_FIRST,
++		COL_RESOURCE = COLDESC_IDX_SUM_FIRST,
++		COL_DESC,
++		COL_LIMIT,
++		COL_USED,
++		COL_USEPERC,
++	COLDESC_IDX_SUM_LAST = COL_USEPERC
++};
++
++/* not all columns apply to all options, so we specify a legal range for each */
++static size_t LOWER, UPPER;
++
++/*
++ * output modes
++ */
++enum {
++	OUT_EXPORT = 1,
++	OUT_NEWLINE,
++	OUT_RAW,
++	OUT_PRETTY,
++	OUT_LIST
++};
++
++struct lsipc_control {
++	int outmode;
++	unsigned int noheadings : 1,		/* don't print header line */
++		     notrunc : 1,		/* don't truncate columns */
++		     bytes : 1,			/* SIZE in bytes */
++		     numperms : 1,		/* numeric permissions */
++		     time_mode : 2;
++};
++
++struct lsipc_coldesc {
++	const char *name;
++	const char *help;
++	const char *pretty_name;
++
++	double whint;	/* width hint */
++	long flag;
++};
++
++static const struct lsipc_coldesc coldescs[] =
++{
++	/* common */
++	[COL_KEY]	= { "KEY",	N_("Resource key"), N_("Key"), 1},
++	[COL_ID]	= { "ID",	N_("Resource ID"), N_("ID"), 1},
++	[COL_OWNER]	= { "OWNER",	N_("Owner's username or UID"), N_("Owner"), 1, SCOLS_FL_RIGHT},
++	[COL_PERMS]	= { "PERMS",	N_("Permissions"), N_("Permissions"), 1, SCOLS_FL_RIGHT},
++	[COL_CUID]	= { "CUID",	N_("Creator UID"), N_("Creator UID"), 1, SCOLS_FL_RIGHT},
++	[COL_CUSER]     = { "CUSER",    N_("Creator user"), N_("Creator user"), 1 },
++	[COL_CGID]	= { "CGID",	N_("Creator GID"), N_("Creator GID"), 1, SCOLS_FL_RIGHT},
++	[COL_CGROUP]    = { "CGROUP",   N_("Creator group"), N_("Creator group"), 1 },
++	[COL_UID]	= { "UID",	N_("User ID"), N_("UID"), 1, SCOLS_FL_RIGHT},
++	[COL_USER]	= { "USER",	N_("User name"), N_("User name"), 1},
++	[COL_GID]	= { "GID",	N_("Group ID"), N_("GID"), 1, SCOLS_FL_RIGHT},
++	[COL_GROUP]	= { "GROUP",	N_("Group name"), N_("Group name"), 1},
++	[COL_CTIME]	= { "CTIME",	N_("Time of the last change"), N_("Last change"), 1, SCOLS_FL_RIGHT},
++
++	/* msgq-specific */
++	[COL_USEDBYTES]	= { "USEDBYTES",N_("Bytes used"), N_("Bytes used"), 1, SCOLS_FL_RIGHT},
++	[COL_MSGS]	= { "MSGS",	N_("Number of messages"), N_("Messages"), 1},
++	[COL_SEND]	= { "SEND",	N_("Time of last msg sent"), N_("Msg sent"), 1, SCOLS_FL_RIGHT},
++	[COL_RECV]	= { "RECV",	N_("Time of last msg received"), N_("Msg received"), 1, SCOLS_FL_RIGHT},
++	[COL_LSPID]	= { "LSPID",	N_("PID of the last msg sender"), N_("Msg sender"), 1, SCOLS_FL_RIGHT},
++	[COL_LRPID]	= { "LRPID",	N_("PID of the last msg receiver"), N_("Msg receiver"), 1, SCOLS_FL_RIGHT},
++
++	/* shm-specific */
++	[COL_SIZE]	= { "SIZE",	N_("Segment size"), N_("Segment size"), 1, SCOLS_FL_RIGHT},
++	[COL_NATTCH]	= { "NATTCH",	N_("Number of attached processes"), N_("Attached processes"), 1, SCOLS_FL_RIGHT},
++	[COL_STATUS]	= { "STATUS",	N_("Status"), N_("Status"), 1, SCOLS_FL_NOEXTREMES},
++	[COL_ATTACH]	= { "ATTACH",	N_("Attach time"), N_("Attach time"), 1, SCOLS_FL_RIGHT},
++	[COL_DETACH]	= { "DETACH",	N_("Detach time"), N_("Detach time"), 1, SCOLS_FL_RIGHT},
++	[COL_COMMAND]	= { "COMMAND",  N_("Creator command line"), N_("Creator command"), 0, SCOLS_FL_TRUNC},
++	[COL_CPID]	= { "CPID",	N_("PID of the creator"), N_("Creator PID"), 1, SCOLS_FL_RIGHT},
++	[COL_LPID]	= { "LPID",	N_("PID of last user"), N_("Last user PID"), 1, SCOLS_FL_RIGHT},
++
++	/* sem-specific */
++	[COL_NSEMS]	= { "NSEMS",	N_("Number of semaphores"), N_("Semaphores"), 1, SCOLS_FL_RIGHT},
++	[COL_OTIME]	= { "OTIME",	N_("Time of the last operation"), N_("Last operation"), 1, SCOLS_FL_RIGHT},
++
++	/* cols for summarized information */
++	[COL_RESOURCE]  = { "RESOURCE", N_("Resource name"), N_("Resource"), 1 },
++	[COL_DESC]      = { "DESCRIPTION",N_("Resource description"), N_("Description"), 1 },
++	[COL_USED]      = { "USED",     N_("Currently used"), N_("Used"), 1, SCOLS_FL_RIGHT },
++	[COL_USEPERC]	= { "USE%",     N_("Currently use percentage"), N_("Use"), 1, SCOLS_FL_RIGHT },
++	[COL_LIMIT]     = { "LIMIT",    N_("System-wide limit"), N_("Limit"), 1, SCOLS_FL_RIGHT },
++};
++
++
++/* columns[] array specifies all currently wanted output column. The columns
++ * are defined by coldescs[] array and you can specify (on command line) each
++ * column twice. That's enough, dynamically allocated array of the columns is
++ * unnecessary overkill and over-engineering in this case */
++static int columns[ARRAY_SIZE(coldescs) * 2];
++static size_t ncolumns;
++
++static inline size_t err_columns_index(size_t arysz, size_t idx)
++{
++	if (idx >= arysz)
++		errx(EXIT_FAILURE, _("too many columns specified, "
++				     "the limit is %zu columns"),
++				arysz - 1);
++	return idx;
++}
++
++#define add_column(ary, n, id)	\
++		((ary)[ err_columns_index(ARRAY_SIZE(ary), (n)) ] = (id))
++
++static int column_name_to_id(const char *name, size_t namesz)
++{
++	size_t i;
++
++	for (i = 0; i < ARRAY_SIZE(coldescs); i++) {
++		const char *cn = coldescs[i].name;
++
++		if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) {
++			if (i > COL_CTIME) {
++				if (i >= LOWER && i <= UPPER)
++					return i;
++				else {
++					warnx(_("column %s does not apply to the specified IPC"), name);
++					return -1;
++				}
++			} else
++				return i;
++		}
++	}
++	warnx(_("unknown column: %s"), name);
++	return -1;
++}
++
++static char *get_username(struct passwd **pw, uid_t id)
++{
++	if (!*pw || (*pw)->pw_uid != id)
++		*pw = getpwuid(id);
++
++	return *pw ? xstrdup((*pw)->pw_name) : NULL;
++}
++
++static char *get_groupname(struct group **gr, gid_t id)
++{
++	if (!*gr || (*gr)->gr_gid != id)
++		*gr = getgrgid(id);
++
++	return *gr ? xstrdup((*gr)->gr_name) : NULL;
++}
++
++static int parse_time_mode(const char *optarg)
++{
++	struct lsipc_timefmt {
++		const char *name;
++		const int val;
++	};
++	static const struct lsipc_timefmt timefmts[] = {
++		{"iso", TIME_ISO},
++		{"full", TIME_FULL},
++		{"short", TIME_SHORT},
++	};
++	size_t i;
++
++	for (i = 0; i < ARRAY_SIZE(timefmts); i++) {
++		if (strcmp(timefmts[i].name, optarg) == 0)
++			return timefmts[i].val;
++	}
++	errx(EXIT_FAILURE, _("unknown time format: %s"), optarg);
++}
++
++static void __attribute__ ((__noreturn__)) usage(FILE * out)
++{
++	size_t i;
++
++	fputs(USAGE_HEADER, out);
++	fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
++
++	fputs(USAGE_SEPARATOR, out);
++	fputs(_("Show information on IPC facilities.\n"), out);
++
++	fputs(USAGE_SEPARATOR, out);
++	fputs(_("Resource options:\n"), out);
++	fputs(_(" -m, --shmems      shared memory segments\n"), out);
++	fputs(_(" -q, --queues      message queues\n"), out);
++	fputs(_(" -s, --semaphores  semaphores\n"), out);
++	fputs(_(" -g, --global      info about system-wide usage (may be used with -m, -q and -s)\n"), out);
++	fputs(_(" -i, --id <id>     print details on resource identified by <id>\n"), out);
++
++	fputs(USAGE_OPTIONS, out);
++	fputs(_("     --noheadings         don't print headings\n"), out);
++	fputs(_("     --notruncate         don't truncate output\n"), out);
++	fputs(_("     --time-format=<type> display dates in short, full or iso format\n"), out);
++	fputs(_(" -b, --bytes              print SIZE in bytes rather than in human readable format\n"), out);
++	fputs(_(" -c, --creator            show creator and owner\n"), out);
++	fputs(_(" -e, --export             display in an export-able output format\n"), out);
++	fputs(_(" -n, --newline            display each piece of information on a new line\n"), out);
++	fputs(_(" -l, --list               force list output format (for example with --id)\n"), out);
++	fputs(_(" -o, --output[=<list>]    define the columns to output\n"), out);
++	fputs(_(" -P, --numeric-perms      print numeric permissions (PERMS column)\n"), out);
++	fputs(_(" -r, --raw                display in raw mode\n"), out);
++	fputs(_(" -t, --time               show attach, detach and change times\n"), out);
++
++	fputs(USAGE_SEPARATOR, out);
++	fputs(USAGE_HELP, out);
++	fputs(USAGE_VERSION, out);
++
++	fprintf(out, _("\nGeneric columns:\n"));
++	for (i = COLDESC_IDX_GEN_FIRST; i <= COLDESC_IDX_GEN_LAST; i++)
++		fprintf(out, " %14s  %s\n", coldescs[i].name, _(coldescs[i].help));
++
++	fprintf(out, _("\nShared-memory columns (--shmems):\n"));
++	for (i = COLDESC_IDX_SHM_FIRST; i <= COLDESC_IDX_SHM_LAST; i++)
++		fprintf(out, " %14s  %s\n", coldescs[i].name, _(coldescs[i].help));
++
++	fprintf(out, _("\nMessage-queue columns (--queues):\n"));
++	for (i = COLDESC_IDX_MSG_FIRST; i <= COLDESC_IDX_MSG_LAST; i++)
++		fprintf(out, " %14s  %s\n", coldescs[i].name, _(coldescs[i].help));
++
++	fprintf(out, _("\nSemaphore columns (--semaphores):\n"));
++	for (i = COLDESC_IDX_SEM_FIRST; i <= COLDESC_IDX_SEM_LAST; i++)
++		fprintf(out, " %14s  %s\n", coldescs[i].name, _(coldescs[i].help));
++
++	fprintf(out, _("\nSummary columns (--global):\n"));
++	for (i = COLDESC_IDX_SUM_FIRST; i <= COLDESC_IDX_SUM_LAST; i++)
++		fprintf(out, " %14s  %s\n", coldescs[i].name, _(coldescs[i].help));
++
++	fprintf(out, USAGE_MAN_TAIL("lsipc(1)"));
++	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
++}
++
++static struct libscols_table *new_table(struct lsipc_control *ctl)
++{
++	struct libscols_table *table = scols_new_table();
++
++	if (!table)
++		errx(EXIT_FAILURE, _("failed to initialize output table"));
++	if (ctl->noheadings)
++		scols_table_enable_noheadings(table, 1);
++
++	switch(ctl->outmode) {
++	case OUT_NEWLINE:
++		scols_table_set_column_separator(table, "\n");
++		/* fallthrough */
++	case OUT_EXPORT:
++		scols_table_enable_export(table, 1);
++		break;
++	case OUT_RAW:
++		scols_table_enable_raw(table, 1);
++		break;
++	case OUT_PRETTY:
++		scols_table_enable_noheadings(table, 1);
++		break;
++	default:
++		break;
++	}
++	return table;
++}
++
++static struct libscols_table *setup_table(struct lsipc_control *ctl)
++{
++	struct libscols_table *table = new_table(ctl);
++	size_t n;
++
++	for (n = 0; n < ncolumns; n++) {
++		int flags = coldescs[columns[n]].flag;
++
++		if (ctl->notrunc)
++			flags &= ~SCOLS_FL_TRUNC;
++
++		if (!scols_table_new_column(table,
++				coldescs[columns[n]].name,
++				coldescs[columns[n]].whint,
++				flags))
++			goto fail;
++	}
++	return table;
++fail:
++	scols_unref_table(table);
++	return NULL;
++}
++
++static int print_pretty(struct libscols_table *table)
++{
++	struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
++	struct libscols_column *col;
++	struct libscols_cell *data;
++	struct libscols_line *ln;
++	const char *hstr, *dstr;
++	int n = 0;
++
++	ln = scols_table_get_line(table, 0);
++	while (!scols_table_next_column(table, itr, &col)) {
++
++		data = scols_line_get_cell(ln, n);
++
++		hstr = N_(coldescs[columns[n]].pretty_name);
++		dstr = scols_cell_get_data(data);
++
++		if (dstr)
++			printf("%s:%*c%-36s\n", hstr, 35 - (int)strlen(hstr), ' ', dstr);
++		++n;
++	}
++
++	/* this is used to pretty-print detailed info about a semaphore array */
++	if (ln) {
++		struct libscols_table *subtab = scols_line_get_userdata(ln);
++		if (subtab) {
++			printf(_("Elements:\n\n"));
++			scols_print_table(subtab);
++		}
++	}
++
++	scols_free_iter(itr);
++	return 0;
++
++}
++
++static int print_table(struct lsipc_control *ctl, struct libscols_table *tb)
++{
++	if (ctl->outmode == OUT_PRETTY)
++		print_pretty(tb);
++	else
++		scols_print_table(tb);
++	return 0;
++}
++static struct timeval now;
++
++static int date_is_today(time_t t)
++{
++	if (now.tv_sec == 0)
++		gettimeofday(&now, NULL);
++	return t / (3600 * 24) == now.tv_sec / (3600 * 24);
++}
++
++static int date_is_thisyear(time_t t)
++{
++	if (now.tv_sec == 0)
++		gettimeofday(&now, NULL);
++	return t / (3600 * 24 * 365) == now.tv_sec / (3600 * 24 * 365);
++}
++
++static char *make_time(int mode, time_t time)
++{
++	char *s;
++	struct tm tm;
++	char buf[64] = {0};
++
++	localtime_r(&time, &tm);
++
++	switch(mode) {
++	case TIME_FULL:
++		asctime_r(&tm, buf);
++		if (*(s = buf + strlen(buf) - 1) == '\n')
++			*s = '\0';
++		break;
++	case TIME_SHORT:
++		if (date_is_today(time))
++			strftime(buf, sizeof(buf), "%H:%M", &tm);
++		else if (date_is_thisyear(time))
++			strftime(buf, sizeof(buf), "%b%d", &tm);
++		else
++			strftime(buf, sizeof(buf), "%Y-%b%d", &tm);
++		break;
++	case TIME_ISO:
++		strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", &tm);
++		break;
++	default:
++		errx(EXIT_FAILURE, _("unsupported time type"));
++	}
++	return xstrdup(buf);
++}
++
++static void global_set_data(struct libscols_table *tb, const char *resource,
++			    const char *desc, uintmax_t used, uintmax_t limit, int usage)
++{
++	struct libscols_line *ln;
++	size_t n;
++
++	ln = scols_table_new_line(tb, NULL);
++	if (!ln)
++		err_oom();
++
++	for (n = 0; n < ncolumns; n++) {
++		int rc = 0;
++		char *arg = NULL;
++
++		switch (columns[n]) {
++		case COL_RESOURCE:
++			rc = scols_line_set_data(ln, n, resource);
++			break;
++		case COL_DESC:
++			rc = scols_line_set_data(ln, n, desc);
++			break;
++		case COL_USED:
++			if (usage) {
++				xasprintf(&arg, "%ju", used);
++				rc = scols_line_refer_data(ln, n, arg);
++			} else
++				rc = scols_line_set_data(ln, n, "-");
++			break;
++		case COL_USEPERC:
++			if (usage) {
++				xasprintf(&arg, "%2.2f%%", (double) used / limit * 100);
++				rc = scols_line_refer_data(ln, n, arg);
++			} else
++				rc = scols_line_set_data(ln, n, "-");
++			break;
++		case COL_LIMIT:
++			xasprintf(&arg, "%ju", limit);
++			rc = scols_line_refer_data(ln, n, arg);
++			break;
++		}
++
++		if (rc != 0)
++			err(EXIT_FAILURE, _("failed to set data"));
++	}
++}
++
++static void setup_sem_elements_columns(struct libscols_table *tb)
++{
++	if (!scols_table_new_column(tb, "SEMNUM", 0, SCOLS_FL_RIGHT))
++		err_oom();
++	if (!scols_table_new_column(tb, "VALUE", 0, SCOLS_FL_RIGHT))
++		err_oom();
++	if (!scols_table_new_column(tb, "NCOUNT", 0, SCOLS_FL_RIGHT))
++		err_oom();
++	if (!scols_table_new_column(tb, "ZCOUNT", 0, SCOLS_FL_RIGHT))
++		err_oom();
++	if (!scols_table_new_column(tb, "PID", 0, SCOLS_FL_RIGHT))
++		err_oom();
++	if (!scols_table_new_column(tb, "COMMAND", 0, SCOLS_FL_RIGHT))
++		err_oom();
++}
++
++static void do_sem(int id, struct lsipc_control *ctl, struct libscols_table *tb)
++{
++	struct libscols_line *ln;
++	struct passwd *pw = NULL, *cpw = NULL;
++	struct group *gr = NULL, *cgr = NULL;
++	struct sem_data *semds, *semdsp;
++	char *arg = NULL;
++
++	if (ipc_sem_get_info(id, &semds) < 1) {
++		if (id > -1)
++			warnx(_("id %d not found"), id);
++		return;
++	}
++	for (semdsp = semds;  semdsp->next != NULL || id > -1; semdsp = semdsp->next) {
++		size_t n;
++		ln = scols_table_new_line(tb, NULL);
++
++		for (n = 0; n < ncolumns; n++) {
++			int rc = 0;
++			switch (columns[n]) {
++			case COL_KEY:
++				xasprintf(&arg, "0x%08x",semdsp->sem_perm.key);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_ID:
++				xasprintf(&arg, "%d",semdsp->sem_perm.id);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_OWNER:
++				arg = get_username(&pw, semdsp->sem_perm.uid);
++				if (!arg)
++					xasprintf(&arg, "%u", semdsp->sem_perm.uid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_PERMS:
++				if (ctl->numperms)
++					xasprintf(&arg, "%#o", semdsp->sem_perm.mode & 0777);
++				else {
++					arg = xmalloc(11);
++					strmode(semdsp->sem_perm.mode & 0777, arg);
++				}
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CUID:
++				xasprintf(&arg, "%u", semdsp->sem_perm.cuid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CUSER:
++				arg = get_username(&cpw, semdsp->sem_perm.cuid);
++				if (arg)
++					rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CGID:
++				xasprintf(&arg, "%u", semdsp->sem_perm.cgid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CGROUP:
++				arg = get_groupname(&cgr, semdsp->sem_perm.cgid);
++				if (arg)
++					rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_UID:
++				xasprintf(&arg, "%u", semdsp->sem_perm.uid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_USER:
++				arg = get_username(&pw, semdsp->sem_perm.uid);
++				if (arg)
++					rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_GID:
++				xasprintf(&arg, "%u", semdsp->sem_perm.gid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_GROUP:
++				arg = get_groupname(&gr, semdsp->sem_perm.gid);
++				if (arg)
++					rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CTIME:
++				if (semdsp->sem_ctime != 0) {
++					rc = scols_line_refer_data(ln, n,
++							make_time(ctl->time_mode,
++							  (time_t)semdsp->sem_ctime));
++				}
++				break;
++			case COL_NSEMS:
++				xasprintf(&arg, "%ju", semdsp->sem_nsems);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_OTIME:
++				if (semdsp->sem_otime != 0) {
++					rc = scols_line_refer_data(ln, n,
++							make_time(ctl->time_mode,
++							  (time_t)semdsp->sem_otime));
++				}
++				break;
++			}
++			if (rc != 0)
++				err(EXIT_FAILURE, _("failed to set data"));
++			arg = NULL;
++		}
++
++		if (id > -1 && semds->sem_nsems) {
++			/* Create extra table with ID specific semaphore elements */
++			struct libscols_table *sub = new_table(ctl);
++			size_t i;
++			int rc = 0;
++
++			scols_table_enable_noheadings(sub, 0);
++			setup_sem_elements_columns(sub);
++
++			for (i = 0; i < semds->sem_nsems; i++) {
++				struct sem_elem *e = &semds->elements[i];
++				struct libscols_line *sln = scols_table_new_line(sub, NULL);
++
++				/* SEMNUM */
++				xasprintf(&arg, "%zu", i);
++				rc = scols_line_refer_data(sln, 0, arg);
++				if (rc)
++					break;
++
++				/* VALUE */
++				xasprintf(&arg, "%d", e->semval);
++				rc = scols_line_refer_data(sln, 1, arg);
++				if (rc)
++					break;
++
++				/* NCOUNT */
++				xasprintf(&arg, "%d", e->ncount);
++				rc = scols_line_refer_data(sln, 2, arg);
++				if (rc)
++					break;
++
++				/* ZCOUNT */
++				xasprintf(&arg, "%d", e->zcount);
++				rc = scols_line_refer_data(sln, 3, arg);
++				if (rc)
++					break;
++
++				/* PID */
++				xasprintf(&arg, "%d", e->pid);
++				rc = scols_line_refer_data(sln, 4, arg);
++				if (rc)
++					break;
++
++				/* COMMAND */
++				arg = proc_get_command(e->pid);
++				rc = scols_line_refer_data(sln, 5, arg);
++				if (rc)
++					break;
++			}
++
++			if (rc != 0)
++				err(EXIT_FAILURE, _("failed to set data"));
++
++			scols_line_set_userdata(ln, (void *)sub);
++			break;
++		}
++	}
++	ipc_sem_free_info(semds);
++}
++
++static void do_sem_global(struct libscols_table *tb)
++{
++	struct sem_data *semds, *semdsp;
++	struct ipc_limits lim;
++	int nsems = 0, nsets = 0;
++
++	ipc_sem_get_limits(&lim);
++
++	if (ipc_sem_get_info(-1, &semds) > 0) {
++		for (semdsp = semds; semdsp->next != NULL; semdsp = semdsp->next) {
++			++nsets;
++			nsems += semds->sem_nsems;
++		}
++		ipc_sem_free_info(semds);
++	}
++
++	global_set_data(tb, "SEMMNI", _("Number of semaphore identifiers"), nsets, lim.semmni, 1);
++	global_set_data(tb, "SEMMNS", _("Total number of semaphores"), nsems, lim.semmns, 1);
++	global_set_data(tb, "SEMMSL", _("Max semaphores per semaphore set."), 0, lim.semmsl, 0);
++	global_set_data(tb, "SEMOPM", _("Max number of operations per semop(2)"), 0, lim.semopm, 0);
++	global_set_data(tb, "SEMVMX", _("Semaphore max value"), 0, lim.semvmx, 0);
++}
++
++static void do_msg(int id, struct lsipc_control *ctl, struct libscols_table *tb)
++{
++	struct libscols_line *ln;
++	struct passwd *pw = NULL;
++	struct group *gr = NULL;
++	struct msg_data *msgds, *msgdsp;
++	char *arg = NULL;
++
++	if (ipc_msg_get_info(id, &msgds) < 1) {
++		if (id > -1)
++			warnx(_("id %d not found"), id);
++		return;
++	}
++
++	for (msgdsp = msgds; msgdsp->next != NULL || id > -1 ; msgdsp = msgdsp->next) {
++		size_t n;
++		ln = scols_table_new_line(tb, NULL);
++
++		/* no need to call getpwuid() for the same user */
++		if (!(pw && pw->pw_uid == msgdsp->msg_perm.uid))
++			pw = getpwuid(msgdsp->msg_perm.uid);
++
++		/* no need to call getgrgid() for the same user */
++		if (!(gr && gr->gr_gid == msgdsp->msg_perm.gid))
++			gr = getgrgid(msgdsp->msg_perm.gid);
++
++		for (n = 0; n < ncolumns; n++) {
++			int rc = 0;
++
++			switch (columns[n]) {
++			case COL_KEY:
++				xasprintf(&arg, "0x%08x",msgdsp->msg_perm.key);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_ID:
++				xasprintf(&arg, "%d",msgdsp->msg_perm.id);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_OWNER:
++				arg = get_username(&pw, msgdsp->msg_perm.uid);
++				if (!arg)
++					xasprintf(&arg, "%u", msgdsp->msg_perm.uid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_PERMS:
++				if (ctl->numperms)
++					xasprintf(&arg, "%#o", msgdsp->msg_perm.mode & 0777);
++				else {
++					arg = xmalloc(11);
++					strmode(msgdsp->msg_perm.mode & 0777, arg);
++					rc = scols_line_refer_data(ln, n, arg);
++				}
++				break;
++			case COL_CUID:
++				xasprintf(&arg, "%u", msgdsp->msg_perm.cuid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CUSER:
++				arg = get_username(&pw, msgdsp->msg_perm.cuid);
++				if (arg)
++					rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CGID:
++				xasprintf(&arg, "%u", msgdsp->msg_perm.cuid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CGROUP:
++				arg = get_groupname(&gr, msgdsp->msg_perm.cgid);
++				if (arg)
++					rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_UID:
++				xasprintf(&arg, "%u", msgdsp->msg_perm.uid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_USER:
++				arg = get_username(&pw, msgdsp->msg_perm.uid);
++				if (arg)
++					rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_GID:
++				xasprintf(&arg, "%u", msgdsp->msg_perm.gid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_GROUP:
++				arg = get_groupname(&gr,msgdsp->msg_perm.gid);
++				if (arg)
++					rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CTIME:
++				if (msgdsp->q_ctime != 0)
++					rc = scols_line_refer_data(ln, n,
++						make_time(ctl->time_mode,
++							  (time_t)msgdsp->q_ctime));
++				break;
++			case COL_USEDBYTES:
++				xasprintf(&arg, "%ju", msgdsp->q_cbytes);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_MSGS:
++				xasprintf(&arg, "%ju", msgdsp->q_qnum);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_SEND:
++				if (msgdsp->q_stime != 0)
++					rc = scols_line_refer_data(ln, n,
++						make_time(ctl->time_mode,
++							  (time_t)msgdsp->q_stime));
++				break;
++			case COL_RECV:
++				if (msgdsp->q_rtime != 0)
++					rc = scols_line_refer_data(ln, n,
++						make_time(ctl->time_mode,
++							  (time_t)msgdsp->q_rtime));
++				break;
++			case COL_LSPID:
++				xasprintf(&arg, "%u", msgdsp->q_lspid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_LRPID:
++				xasprintf(&arg, "%u", msgdsp->q_lrpid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			}
++			if (rc != 0)
++				err(EXIT_FAILURE, _("failed to set data"));
++			arg = NULL;
++		}
++		if (id > -1)
++			break;
++	}
++	ipc_msg_free_info(msgds);
++}
++
++
++static void do_msg_global(struct libscols_table *tb)
++{
++	struct msg_data *msgds, *msgdsp;
++	struct ipc_limits lim;
++	int msgqs = 0;
++
++	ipc_msg_get_limits(&lim);
++
++	/* count number of used queues */
++	if (ipc_msg_get_info(-1, &msgds) > 0) {
++		for (msgdsp = msgds; msgdsp->next != NULL; msgdsp = msgdsp->next)
++			++msgqs;
++		ipc_msg_free_info(msgds);
++	}
++
++	global_set_data(tb, "MSGMNI", _("Number of message queues"), msgqs, lim.msgmni, 1);
++	global_set_data(tb, "MSGMAX", _("Max size of message (bytes)"),	0, lim.msgmax, 0);
++	global_set_data(tb, "MSGMNB", _("Default max size of queue (bytes)"), 0, lim.msgmnb, 0);
++}
++
++
++static void do_shm(int id, struct lsipc_control *ctl, struct libscols_table *tb)
++{
++	struct libscols_line *ln;
++	struct passwd *pw = NULL;
++	struct group *gr = NULL;
++	struct shm_data *shmds, *shmdsp;
++	char *arg = NULL;
++
++	if (ipc_shm_get_info(id, &shmds) < 1) {
++		if (id > -1)
++			warnx(_("id %d not found"), id);
++		return;
++	}
++
++	for (shmdsp = shmds; shmdsp->next != NULL || id > -1 ; shmdsp = shmdsp->next) {
++		size_t n;
++		ln = scols_table_new_line(tb, NULL);
++		if (!ln)
++			err_oom();
++
++		for (n = 0; n < ncolumns; n++) {
++			int rc = 0;
++
++			switch (columns[n]) {
++			case COL_KEY:
++				xasprintf(&arg, "0x%08x",shmdsp->shm_perm.key);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_ID:
++				xasprintf(&arg, "%d",shmdsp->shm_perm.id);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_OWNER:
++				arg = get_username(&pw, shmdsp->shm_perm.uid);
++				if (!arg)
++					xasprintf(&arg, "%u", shmdsp->shm_perm.uid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_PERMS:
++				if (ctl->numperms)
++					xasprintf(&arg, "%#o", shmdsp->shm_perm.mode & 0777);
++				else {
++					arg = xmalloc(11);
++					strmode(shmdsp->shm_perm.mode & 0777, arg);
++				}
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CUID:
++				xasprintf(&arg, "%u", shmdsp->shm_perm.cuid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CUSER:
++				arg = get_username(&pw, shmdsp->shm_perm.cuid);
++				if (arg)
++					rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CGID:
++				xasprintf(&arg, "%u", shmdsp->shm_perm.cuid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CGROUP:
++				arg = get_groupname(&gr, shmdsp->shm_perm.cgid);
++				if (arg)
++					rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_UID:
++				xasprintf(&arg, "%u", shmdsp->shm_perm.uid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_USER:
++				arg = get_username(&pw, shmdsp->shm_perm.uid);
++				if (arg)
++					rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_GID:
++				xasprintf(&arg, "%u", shmdsp->shm_perm.gid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_GROUP:
++				arg = get_groupname(&gr, shmdsp->shm_perm.gid);
++				if (arg)
++					rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_CTIME:
++				if (shmdsp->shm_ctim != 0)
++					rc = scols_line_refer_data(ln, n,
++						make_time(ctl->time_mode,
++							  (time_t)shmdsp->shm_ctim));
++				break;
++			case COL_SIZE:
++				if (ctl->bytes)
++					xasprintf(&arg, "%ju", shmdsp->shm_segsz);
++				else
++					arg = size_to_human_string(SIZE_SUFFIX_1LETTER, shmdsp->shm_segsz);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_NATTCH:
++				xasprintf(&arg, "%ju", shmdsp->shm_nattch);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_STATUS: {
++					int comma = 0;
++					size_t offt = 0;
++
++					free(arg);
++					arg = xcalloc(1, sizeof(char) * strlen(_("dest"))
++							+ strlen(_("locked"))
++							+ strlen(_("hugetlb"))
++							+ strlen(_("noreserve")) + 4);
++#ifdef SHM_DEST
++					if (shmdsp->shm_perm.mode & SHM_DEST) {
++						offt += sprintf(arg, "%s", _("dest"));
++						comma++;
++					}
++#endif
++#ifdef SHM_LOCKED
++					if (shmdsp->shm_perm.mode & SHM_LOCKED) {
++						if (comma)
++							arg[offt++] = ',';
++						offt += sprintf(arg + offt, "%s", _("locked"));
++					}
++#endif
++#ifdef SHM_HUGETLB
++					if (shmdsp->shm_perm.mode & SHM_HUGETLB) {
++						if (comma)
++							arg[offt++] = ',';
++						offt += sprintf(arg + offt, "%s", _("hugetlb"));
++					}
++#endif
++#ifdef SHM_NORESERVE
++					if (shmdsp->shm_perm.mode & SHM_NORESERVE) {
++						if (comma)
++							arg[offt++] = ',';
++						sprintf(arg + offt, "%s", _("noreserve"));
++					}
++#endif
++					rc = scols_line_refer_data(ln, n, arg);
++				}
++				break;
++			case COL_ATTACH:
++				if (shmdsp->shm_atim != 0)
++					rc = scols_line_refer_data(ln, n,
++							make_time(ctl->time_mode,
++							  (time_t)shmdsp->shm_atim));
++				break;
++			case COL_DETACH:
++				if (shmdsp->shm_dtim != 0)
++					rc = scols_line_refer_data(ln, n,
++							make_time(ctl->time_mode,
++							  (time_t)shmdsp->shm_dtim));
++				break;
++			case COL_CPID:
++				xasprintf(&arg, "%u", shmdsp->shm_cprid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_LPID:
++				xasprintf(&arg, "%u", shmdsp->shm_lprid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			case COL_COMMAND:
++				arg = proc_get_command(shmdsp->shm_cprid);
++				rc = scols_line_refer_data(ln, n, arg);
++				break;
++			}
++			if (rc != 0)
++				err(EXIT_FAILURE, _("failed to set data"));
++			arg = NULL;
++		}
++		if (id > -1)
++			break;
++	}
++	ipc_shm_free_info(shmds);
++}
++
++static void do_shm_global(struct libscols_table *tb)
++{
++	struct shm_data *shmds, *shmdsp;
++	uint64_t nsegs = 0, sum_segsz = 0;
++	struct ipc_limits lim;
++
++	ipc_shm_get_limits(&lim);
++
++	if (ipc_shm_get_info(-1, &shmds) > 0) {
++		for (shmdsp = shmds; shmdsp->next != NULL; shmdsp = shmdsp->next) {
++			++nsegs;
++			sum_segsz += shmdsp->shm_segsz;
++		}
++		ipc_shm_free_info(shmds);
++	}
++
++	global_set_data(tb, "SHMMNI", _("Shared memory segments"), nsegs, lim.shmmni, 1);
++	global_set_data(tb, "SHMALL", _("Shared memory pages"), sum_segsz / getpagesize(), lim.shmall, 1);
++	global_set_data(tb, "SHMMAX", _("Max size of shared memory segment (bytes)"), 0, lim.shmmax, 0);
++	global_set_data(tb, "SHMMIN", _("Min size of shared memory segment (bytes)"), 0, lim.shmmin, 0);
++}
++
++int main(int argc, char *argv[])
++{
++	int opt, msg = 0, sem = 0, shm = 0, id = -1;
++	int show_time = 0, show_creat = 0, global = 0;
++	size_t i;
++	struct lsipc_control *ctl = xcalloc(1, sizeof(struct lsipc_control));
++	static struct libscols_table *tb;
++	char *outarg = NULL;
++
++	/* long only options. */
++	enum {
++		OPT_NOTRUNC = CHAR_MAX + 1,
++		OPT_NOHEAD,
++		OPT_TIME_FMT
++	};
++
++	static const struct option longopts[] = {
++		{ "bytes",          no_argument,        0, 'b' },
++		{ "creator",        no_argument,	0, 'c' },
++		{ "export",         no_argument,	0, 'e' },
++		{ "global",         no_argument,	0, 'g' },
++		{ "help",           no_argument,	0, 'h' },
++		{ "id",             required_argument,	0, 'i' },
++		{ "list",           no_argument,        0, 'l' },
++		{ "newline",        no_argument,	0, 'n' },
++		{ "noheadings",     no_argument,	0, OPT_NOHEAD },
++		{ "notruncate",     no_argument,	0, OPT_NOTRUNC },
++		{ "numeric-perms",  no_argument,	0, 'P' },
++		{ "output",         required_argument,	0, 'o' },
++		{ "pid",            no_argument,	0, 'p' },
++		{ "queues",         no_argument,	0, 'q' },
++		{ "raw",            no_argument,	0, 'r' },
++		{ "semaphores",     no_argument,	0, 's' },
++		{ "shmems",         no_argument,	0, 'm' },
++		{ "time",           no_argument,	0, 't' },
++		{ "time-format",    required_argument,	0, OPT_TIME_FMT },
++		{ "version",        no_argument,	0, 'V' },
++		{NULL, 0, NULL, 0}
++	};
++
++	static const ul_excl_t excl[] = {	/* rows and cols in ASCII order */
++		{ 'J', 'e', 'l', 'n', 'r' },
++		{ 'g', 'i' },
++		{ 'c', 'o', 't' },
++		{ 'm', 'q', 's' },
++		{ 0 }
++	};
++	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
++
++	setlocale(LC_ALL, "");
++	bindtextdomain(PACKAGE, LOCALEDIR);
++	textdomain(PACKAGE);
++	atexit(close_stdout);
++
++	ctl->time_mode = 0;
++
++	scols_init_debug(0);
++
++	while ((opt = getopt_long(argc, argv, "bceghi:lmno:PqrstuV", longopts, NULL)) != -1) {
++
++		err_exclusive_options(opt, longopts, excl, excl_st);
++
++		switch (opt) {
++		case 'b':
++			ctl->bytes = 1;
++			break;
++		case 'i':
++			id = strtos32_or_err(optarg, _("failed to parse IPC identifier"));
++			break;
++		case 'e':
++			ctl->outmode = OUT_EXPORT;
++			break;
++		case 'r':
++			ctl->outmode = OUT_RAW;
++			break;
++		case 'o':
++			outarg = optarg;
++			break;
++		case 'g':
++			global = 1;
++			break;
++		case 'q':
++			msg = 1;
++			add_column(columns, ncolumns++, COL_KEY);
++			add_column(columns, ncolumns++, COL_ID);
++			add_column(columns, ncolumns++, COL_PERMS);
++			add_column(columns, ncolumns++, COL_OWNER);
++			add_column(columns, ncolumns++, COL_USEDBYTES);
++			add_column(columns, ncolumns++, COL_MSGS);
++			add_column(columns, ncolumns++, COL_LSPID);
++			add_column(columns, ncolumns++, COL_LRPID);
++			LOWER = COLDESC_IDX_MSG_FIRST;
++			UPPER = COLDESC_IDX_MSG_LAST;
++			break;
++		case 'l':
++			ctl->outmode = OUT_LIST;
++			break;
++		case 'm':
++			shm = 1;
++			add_column(columns, ncolumns++, COL_KEY);
++			add_column(columns, ncolumns++, COL_ID);
++			add_column(columns, ncolumns++, COL_PERMS);
++			add_column(columns, ncolumns++, COL_OWNER);
++			add_column(columns, ncolumns++, COL_SIZE);
++			add_column(columns, ncolumns++, COL_NATTCH);
++			add_column(columns, ncolumns++, COL_STATUS);
++			add_column(columns, ncolumns++, COL_CTIME);
++			add_column(columns, ncolumns++, COL_CPID);
++			add_column(columns, ncolumns++, COL_LPID);
++			add_column(columns, ncolumns++, COL_COMMAND);
++			LOWER = COLDESC_IDX_SHM_FIRST;
++			UPPER = COLDESC_IDX_SHM_LAST;
++			break;
++		case 'n':
++			ctl->outmode = OUT_NEWLINE;
++			break;
++		case 'P':
++			ctl->numperms = 1;
++			break;
++		case 's':
++			sem = 1;
++			add_column(columns, ncolumns++, COL_KEY);
++			add_column(columns, ncolumns++, COL_ID);
++			add_column(columns, ncolumns++, COL_PERMS);
++			add_column(columns, ncolumns++, COL_OWNER);
++			add_column(columns, ncolumns++, COL_NSEMS);
++			LOWER = COLDESC_IDX_SEM_FIRST;
++			UPPER = COLDESC_IDX_SEM_LAST;
++			break;
++		case OPT_NOTRUNC:
++			ctl->notrunc = 1;
++			break;
++		case OPT_NOHEAD:
++			ctl->noheadings = 1;
++			break;
++		case OPT_TIME_FMT:
++			ctl->time_mode = parse_time_mode(optarg);
++			break;
++		case 't':
++			show_time = 1;
++			break;
++		case 'c':
++			show_creat = 1;
++			break;
++		case 'h':
++			usage(stdout);
++		case 'V':
++			printf(UTIL_LINUX_VERSION);
++			return EXIT_SUCCESS;
++		default:
++			usage(stderr);
++		}
++	}
++
++	/* default is global */
++	if (msg + shm + sem == 0) {
++		msg = shm = sem = global = 1;
++		if (show_time || show_creat || id != -1)
++			errx(EXIT_FAILURE, _("--global is mutually exclusive with --creator, --id and --time"));
++	}
++	if (global) {
++		add_column(columns, ncolumns++, COL_RESOURCE);
++		add_column(columns, ncolumns++, COL_DESC);
++		add_column(columns, ncolumns++, COL_LIMIT);
++		add_column(columns, ncolumns++, COL_USED);
++		add_column(columns, ncolumns++, COL_USEPERC);
++		LOWER = COLDESC_IDX_SUM_FIRST;
++		UPPER = COLDESC_IDX_SUM_LAST;
++	}
++
++	/* default to pretty-print if --id specified */
++	if (id != -1 && !ctl->outmode)
++		ctl->outmode = OUT_PRETTY;
++
++	if (!ctl->time_mode)
++		ctl->time_mode = ctl->outmode == OUT_PRETTY ? TIME_FULL : TIME_SHORT;
++
++	if (ctl->outmode == OUT_PRETTY && !(optarg || show_creat || show_time)) {
++		/* all columns for lsipc --<RESOURCE> --id <ID> */
++		for (ncolumns = 0, i = 0; i < ARRAY_SIZE(coldescs); i++)
++			 columns[ncolumns++] = i;
++	} else {
++		if (show_creat) {
++			add_column(columns, ncolumns++, COL_CUID);
++			add_column(columns, ncolumns++, COL_CGID);
++			add_column(columns, ncolumns++, COL_UID);
++			add_column(columns, ncolumns++, COL_GID);
++		}
++		if (msg && show_time) {
++			add_column(columns, ncolumns++, COL_SEND);
++			add_column(columns, ncolumns++, COL_RECV);
++			add_column(columns, ncolumns++, COL_CTIME);
++		}
++		if (shm && show_time) {
++			/* keep "COMMAND" as last column */
++			size_t cmd = columns[ncolumns - 1] == COL_COMMAND;
++
++			if (cmd)
++				ncolumns--;
++			add_column(columns, ncolumns++, COL_ATTACH);
++			add_column(columns, ncolumns++, COL_DETACH);
++			if (cmd)
++				add_column(columns, ncolumns++, COL_COMMAND);
++		}
++		if (sem && show_time) {
++			add_column(columns, ncolumns++, COL_OTIME);
++			add_column(columns, ncolumns++, COL_CTIME);
++		}
++	}
++
++	if (outarg && string_add_to_idarray(outarg, columns, ARRAY_SIZE(columns),
++					 (int *) &ncolumns, column_name_to_id) < 0)
++		return EXIT_FAILURE;
++
++	tb = setup_table(ctl);
++	if (!tb)
++		return EXIT_FAILURE;
++
++	if (msg) {
++		if (global)
++			do_msg_global(tb);
++		else
++			do_msg(id, ctl, tb);
++	}
++	if (shm) {
++		if (global)
++			do_shm_global(tb);
++		else
++			do_shm(id, ctl, tb);
++	}
++	if (sem) {
++		if (global)
++			do_sem_global(tb);
++		else
++			do_sem(id, ctl, tb);
++	}
++
++	print_table(ctl, tb);
++
++	scols_unref_table(tb);
++	free(ctl);
++
++	return EXIT_SUCCESS;
++}
++
+diff --git a/tests/functions.sh b/tests/functions.sh
+index 123f6c3..b2d493c 100644
+--- a/tests/functions.sh
++++ b/tests/functions.sh
+@@ -50,16 +50,25 @@ function ts_skip_nonroot {
+ }
+ 
+ function ts_failed_subtest {
++	local msg="FAILED"
++	local ret=1
++	if [ "$TS_KNOWN_FAIL" = "yes" ]; then
++		msg="KNOWN FAILED"
++		ret=0
++	fi
++
+ 	if [ x"$1" == x"" ]; then
+-		echo " FAILED ($TS_NS)"
++		echo " $msg ($TS_NS)"
+ 	else
+-		echo " FAILED ($1)"
++		echo " $msg ($1)"
+ 	fi
++
++	return $ret
+ }
+ 
+ function ts_failed {
+ 	ts_failed_subtest "$1"
+-	exit 1
++	exit $?
+ }
+ 
+ function ts_ok_subtest {
+@@ -150,6 +159,7 @@ function ts_init_env {
+ 	TS_SUBDIR=$(dirname $TS_SCRIPT)
+ 	TS_TESTNAME=$(basename $TS_SCRIPT)
+ 	TS_COMPONENT=$(basename $TS_SUBDIR)
++	TS_KNOWN_FAIL="no"
+ 
+ 	TS_NSUBTESTS=0
+ 	TS_NSUBFAILED=0
+diff --git a/tests/ts/ipcs/limits2 b/tests/ts/ipcs/limits2
+index f99a354..63f834d 100755
+--- a/tests/ts/ipcs/limits2
++++ b/tests/ts/ipcs/limits2
+@@ -16,15 +16,20 @@
+ # GNU General Public License for more details.
+ #
+ 
+-TS_TOPDIR="$(dirname $0)/../.."
++TS_TOPDIR="${0%/*}/../.."
+ TS_DESC="basic limits"
+ 
+ . $TS_TOPDIR/functions.sh
+ ts_init "$*"
+-type bc >/dev/null 2>&1 || ts_skip "cannot find bc command"
+ 
+ . $TS_SELF/functions.sh
+ 
++# TODO https://github.com/karelzak/util-linux/issues/51
++SHMALL=$(</proc/sys/kernel/shmall)
++if [ $(bc <<<"(2^64 / $PAGE_SIZE) <= $SHMALL") -eq 1 ]; then
++	TS_KNOWN_FAIL="yes"
++fi
++
+ ts_log "check for difference between kernel and IPC"
+ ipcs_limits_check >> $TS_OUTPUT
+ 
+-- 
+2.7.4
+
diff --git a/SOURCES/0079-blkdiscard-backport-zeroout-support.patch b/SOURCES/0079-blkdiscard-backport-zeroout-support.patch
new file mode 100644
index 0000000..22edece
--- /dev/null
+++ b/SOURCES/0079-blkdiscard-backport-zeroout-support.patch
@@ -0,0 +1,278 @@
+From ca291153ff2c696696c1406aca6433aab6e412a1 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Fri, 24 Jun 2016 13:36:32 +0200
+Subject: [PATCH 79/84] blkdiscard: backport --zeroout support
+
+The patch also includes upstream cleanups.
+
+Upstream: http://github.com/karelzak/util-linux/commit/0e765365798c54d412e355798ad584a52035f228
+Upstream: http://github.com/karelzak/util-linux/commit/a3e91e26467a0f644ee568bb0b3d481591834015
+Upstream: http://github.com/karelzak/util-linux/commit/eeae448805c0eb2ef130a6ac301750706bb80420
+Upstream: http://github.com/karelzak/util-linux/commit/7154cc892688f3c58cbbcdc2055f2635c1d0ef5b
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1327886
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ bash-completion/blkdiscard |   2 +-
+ sys-utils/blkdiscard.8     |   7 +--
+ sys-utils/blkdiscard.c     | 122 ++++++++++++++++++++++++++++++++++-----------
+ 3 files changed, 98 insertions(+), 33 deletions(-)
+
+diff --git a/bash-completion/blkdiscard b/bash-completion/blkdiscard
+index 310cdfb..fb3cb1e 100644
+--- a/bash-completion/blkdiscard
++++ b/bash-completion/blkdiscard
+@@ -15,7 +15,7 @@ _blkdiscard_module()
+ 	esac
+ 	case $cur in
+ 		-*)
+-			OPTS="--offset --length --secure --verbose --help --version"
++			OPTS="--offset --length --secure --zeroout --verbose --help --version"
+ 			COMPREPLY=( $(compgen -W "${OPTS[*]}" -- $cur) )
+ 			return 0
+ 			;;
+diff --git a/sys-utils/blkdiscard.8 b/sys-utils/blkdiscard.8
+index 5e094d4..71180e9 100644
+--- a/sys-utils/blkdiscard.8
++++ b/sys-utils/blkdiscard.8
+@@ -1,15 +1,13 @@
+-.\" -*- nroff -*-
+ .TH BLKDISCARD 8 "July 2014" "util-linux" "System Administration"
+ .SH NAME
+ blkdiscard \- discard sectors on a device
+ .SH SYNOPSIS
+ .B blkdiscard
++[options]
+ .RB [ \-o
+ .IR offset ]
+ .RB [ \-l
+ .IR length ]
+-.RB [ \-s ]
+-.RB [ \-v ]
+ .I device
+ .SH DESCRIPTION
+ .B blkdiscard
+@@ -59,6 +57,9 @@ Perform a secure discard.  A secure discard is the same as a regular discard
+ except that all copies of the discarded blocks that were possibly created by
+ garbage collection must also be erased.  This requires support from the device.
+ .TP
++.BR \-z , " \-\-zeroout"
++Zero-fill rather than discard.
++.TP
+ .BR \-v , " \-\-verbose"
+ Display the aligned values of
+ .I offset
+diff --git a/sys-utils/blkdiscard.c b/sys-utils/blkdiscard.c
+index 92ca52a..0ba99ee 100644
+--- a/sys-utils/blkdiscard.c
++++ b/sys-utils/blkdiscard.c
+@@ -44,43 +44,95 @@
+ #include "closestream.h"
+ 
+ #ifndef BLKDISCARD
+-#define BLKDISCARD	_IO(0x12,119)
++# define BLKDISCARD	_IO(0x12,119)
+ #endif
+ 
+ #ifndef BLKSECDISCARD
+-#define BLKSECDISCARD	_IO(0x12,125)
++# define BLKSECDISCARD	_IO(0x12,125)
+ #endif
+ 
+-#define print_stats(path, stats) \
+-	printf(_("%s: Discarded %" PRIu64 " bytes from the " \
+-		 "offset %" PRIu64"\n"), path, stats[1], stats[0]);
++#ifndef BLKZEROOUT
++# define BLKZEROOUT	_IO(0x12,127)
++#endif
++
++enum {
++	ACT_DISCARD = 0,	/* default */
++	ACT_ZEROOUT,
++	ACT_SECURE
++};
++
++/* RHEL: backport from upstream lib/monotonic.c */
++static 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, can't 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
++}
++
++static void print_stats(int act, char *path, uint64_t stats[])
++{
++	switch (act) {
++	case ACT_ZEROOUT:
++		printf(_("%s: Zero-filled %" PRIu64 " bytes from the offset %" PRIu64"\n"), \
++			path, stats[1], stats[0]);
++		break;
++	case ACT_SECURE:
++	case ACT_DISCARD:
++		printf(_("%s: Discarded %" PRIu64 " bytes from the offset %" PRIu64"\n"), \
++			path, stats[1], stats[0]);
++		break;
++	}
++}
+ 
+ static void __attribute__((__noreturn__)) usage(FILE *out)
+ {
+ 	fputs(USAGE_HEADER, out);
+ 	fprintf(out,
+ 	      _(" %s [options] <device>\n"), program_invocation_short_name);
++
++	fputs(USAGE_SEPARATOR, out);
++	fputs(_("Discard the content of sectors on a device.\n"), out);
++
+ 	fputs(USAGE_OPTIONS, out);
+-	fputs(_(" -o, --offset <num>  offset in bytes to discard from\n"
+-		" -l, --length <num>  length of bytes to discard from the offset\n"
+-		" -p, --step <num>    size of the discard iterations within the offset\n"
+-		" -s, --secure        perform secure discard\n"
+-		" -v, --verbose       print aligned length and offset\n"),
+-		out);
++	fputs(_(" -o, --offset <num>  offset in bytes to discard from\n"), out);
++	fputs(_(" -l, --length <num>  length of bytes to discard from the offset\n"), out);
++	fputs(_(" -p, --step <num>    size of the discard iterations within the offset\n"), out);
++	fputs(_(" -s, --secure        perform secure discard\n"), out);
++	fputs(_(" -z, --zeroout       zero-fill rather than discard\n"), out);
++	fputs(_(" -v, --verbose       print aligned length and offset\n"), out);
++
+ 	fputs(USAGE_SEPARATOR, out);
+ 	fputs(USAGE_HELP, out);
+ 	fputs(USAGE_VERSION, out);
++
+ 	fprintf(out, USAGE_MAN_TAIL("blkdiscard(8)"));
+ 	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+ }
+ 
++
+ int main(int argc, char **argv)
+ {
+ 	char *path;
+-	int c, fd, verbose = 0, secure = 0, secsize;
++	int c, fd, verbose = 0, secsize;
+ 	uint64_t end, blksize, step, range[2], stats[2];
+ 	struct stat sb;
+-	struct timespec now, last;
++	struct timeval now, last;
++	int act = ACT_DISCARD;
+ 
+ 	static const struct option longopts[] = {
+ 	    { "help",      0, 0, 'h' },
+@@ -90,6 +142,7 @@ int main(int argc, char **argv)
+ 	    { "step",      1, 0, 'p' },
+ 	    { "secure",    0, 0, 's' },
+ 	    { "verbose",   0, 0, 'v' },
++	    { "zeroout",   0, 0, 'z' },
+ 	    { NULL,        0, 0, 0 }
+ 	};
+ 
+@@ -102,7 +155,7 @@ int main(int argc, char **argv)
+ 	range[1] = ULLONG_MAX;
+ 	step = 0;
+ 
+-	while ((c = getopt_long(argc, argv, "hVsvo:l:p:", longopts, NULL)) != -1) {
++	while ((c = getopt_long(argc, argv, "hVsvo:l:p:z", longopts, NULL)) != -1) {
+ 		switch(c) {
+ 		case 'h':
+ 			usage(stdout);
+@@ -123,11 +176,14 @@ int main(int argc, char **argv)
+ 					_("failed to parse step"));
+ 			break;
+ 		case 's':
+-			secure = 1;
++			act = ACT_SECURE;
+ 			break;
+ 		case 'v':
+ 			verbose = 1;
+ 			break;
++		case 'z':
++			act = ACT_ZEROOUT;
++			break;
+ 		default:
+ 			usage(stderr);
+ 			break;
+@@ -149,7 +205,7 @@ int main(int argc, char **argv)
+ 		err(EXIT_FAILURE, _("cannot open %s"), path);
+ 
+ 	if (fstat(fd, &sb) == -1)
+-		err(EXIT_FAILURE, _("stat failed %s"), path);
++		err(EXIT_FAILURE, _("stat of %s failed"), path);
+ 	if (!S_ISBLK(sb.st_mode))
+ 		errx(EXIT_FAILURE, _("%s: not a block device"), path);
+ 
+@@ -178,35 +234,43 @@ int main(int argc, char **argv)
+ 			 "to sector size %i"), path, range[1], secsize);
+ 
+ 	stats[0] = range[0], stats[1] = 0;
+-	clock_gettime(CLOCK_MONOTONIC, &last);
++	gettime_monotonic(&last);
+ 
+-	for (range[0] = range[0]; range[0] < end; range[0] += range[1]) {
++	for (/* nothing */; range[0] < end; range[0] += range[1]) {
+ 		if (range[0] + range[1] > end)
+ 			range[1] = end - range[0];
+ 
+-		if (secure) {
++		switch (act) {
++		case ACT_ZEROOUT:
++			if (ioctl(fd, BLKZEROOUT, &range))
++				 err(EXIT_FAILURE, _("%s: BLKZEROOUT ioctl failed"), path);
++			break;
++		case ACT_SECURE:
+ 			if (ioctl(fd, BLKSECDISCARD, &range))
+ 				err(EXIT_FAILURE, _("%s: BLKSECDISCARD ioctl failed"), path);
+-		} else {
++			break;
++		case ACT_DISCARD:
+ 			if (ioctl(fd, BLKDISCARD, &range))
+ 				err(EXIT_FAILURE, _("%s: BLKDISCARD ioctl failed"), path);
++			break;
+ 		}
+ 
+-		/* reporting progress */
++		stats[1] += range[1];
++
++		/* reporting progress at most once per second */
+ 		if (verbose && step) {
+-			clock_gettime(CLOCK_MONOTONIC, &now);
+-			if (last.tv_sec < now.tv_sec) {
+-				print_stats(path, stats);
+-				stats[0] = range[0], stats[1] = 0;
++			gettime_monotonic(&now);
++			if (now.tv_sec > last.tv_sec &&
++			    (now.tv_usec >= last.tv_usec || now.tv_sec > last.tv_sec + 1)) {
++				print_stats(act, path, stats);
++				stats[0] += stats[1], stats[1] = 0;
+ 				last = now;
+ 			}
+ 		}
+-
+-		stats[1] += range[1];
+ 	}
+ 
+-	if (verbose)
+-		print_stats(path, stats);
++	if (verbose && stats[1])
++		print_stats(act, path, stats);
+ 
+ 	close(fd);
+ 	return EXIT_SUCCESS;
+-- 
+2.7.4
+
diff --git a/SOURCES/0080-sulogin-and-agetty-virtual-consoles-support-xvc-and-.patch b/SOURCES/0080-sulogin-and-agetty-virtual-consoles-support-xvc-and-.patch
new file mode 100644
index 0000000..ae2bc6b
--- /dev/null
+++ b/SOURCES/0080-sulogin-and-agetty-virtual-consoles-support-xvc-and-.patch
@@ -0,0 +1,125 @@
+From 46537bc28d48acb9ae5cac76535262e6b2ec48a2 Mon Sep 17 00:00:00 2001
+From: Werner Fink <werner@suse.de>
+Date: Thu, 8 May 2014 12:09:25 +0200
+Subject: [PATCH 80/84] sulogin: (and agetty) virtual consoles support, xvc and
+ hvc device
+
+For this approach do not use the ioctl TIOCMGET anymore as this
+is for real serial lines only. But switch over to use the ioctl
+KDGKBMODE as this is unique to the virtual console lines only.
+
+Upstream: http://github.com/karelzak/util-linux/commit/b9c7390948c7850db2bee82ad64624930962cc14
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1029385
+Signed-off-by: Werner Fink <werner@suse.de>
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ login-utils/sulogin.c | 17 ++++++++++++-----
+ term-utils/agetty.c   | 23 +++++++++++------------
+ 2 files changed, 23 insertions(+), 17 deletions(-)
+
+diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c
+index 32ae9a2..bbd67b3 100644
+--- a/login-utils/sulogin.c
++++ b/login-utils/sulogin.c
+@@ -49,6 +49,11 @@
+ # include <selinux/get_context_list.h>
+ #endif
+ 
++#ifdef __linux__
++# include <sys/kd.h>
++# include <sys/param.h>
++#endif
++
+ #include "c.h"
+ #include "closestream.h"
+ #include "nls.h"
+@@ -93,10 +98,14 @@ static void tcinit(struct console *con)
+ 		return;
+ 	}
+ 
+-	/* Handle serial lines here */
+-	if (ioctl(fd, TIOCMGET, (char *) &mode) == 0) {
++	/* Handle lines other than virtual consoles here */
++#if defined(KDGKBMODE)
++	if (ioctl(fd, KDGKBMODE, &mode) < 0)
++#endif
++	{
+ 		speed_t ispeed, ospeed;
+ 		struct winsize ws;
++		errno = 0;
+ 
+ 		/* this is a modem line */
+ 		con->flags |= CON_SERIAL;
+@@ -142,9 +151,7 @@ static void tcinit(struct console *con)
+ 		goto setattr;
+ 	}
+ #if defined(IUTF8) && defined(KDGKBMODE)
+-	/* Detect mode of current keyboard setup, e.g. for UTF-8 */
+-	if (ioctl(fd, KDGKBMODE, &mode) < 0)
+-		mode = K_RAW;
++	/* Handle mode of current keyboard setup, e.g. for UTF-8 */
+ 	switch(mode) {
+ 	case K_UNICODE:
+ 		setlocale(LC_CTYPE, "C.UTF-8");
+diff --git a/term-utils/agetty.c b/term-utils/agetty.c
+index c7af154..5692126 100644
+--- a/term-utils/agetty.c
++++ b/term-utils/agetty.c
+@@ -134,6 +134,7 @@ struct options {
+ 	int nice;			/* Run login with this priority */
+ 	int numspeed;			/* number of baud rates to try */
+ 	int clocal;			/* CLOCAL_MODE_* */
++	int kbmode;			/* Keyboard mode if virtual console */
+ 	speed_t speeds[MAX_SPEED];	/* baud rates to be tried */
+ };
+ 
+@@ -886,7 +887,7 @@ static void update_utmp(struct options *op)
+ static void open_tty(char *tty, struct termios *tp, struct options *op)
+ {
+ 	const pid_t pid = getpid();
+-	int serial, closed = 0;
++	int closed = 0;
+ 
+ 	/* Set up new standard input, unless we are given an already opened port. */
+ 
+@@ -1016,15 +1017,18 @@ static void open_tty(char *tty, struct termios *tp, struct options *op)
+ 
+ 	/*
+ 	 * Detect if this is a virtual console or serial/modem line.
+-	 * In case of a virtual console the ioctl TIOCMGET fails and
+-	 * the error number will be set to EINVAL.
++	 * In case of a virtual console the ioctl KDGKBMODE succeeds
++	 * whereas on other lines it will fails.
+ 	 */
+-	if (ioctl(STDIN_FILENO, TIOCMGET, &serial) < 0 && (errno == EINVAL)) {
++	if (ioctl(STDIN_FILENO, KDGKBMODE, &op->kbmode) == 0) {
+ 		op->flags |= F_VCONSOLE;
+ 		if (!op->term)
+ 			op->term = DEFAULT_VCTERM;
+-	} else if (!op->term)
+-		op->term = DEFAULT_STERM;
++	} else {
++		op->kbmode = K_RAW;
++		if (!op->term)
++			op->term = DEFAULT_STERM;
++	}
+ 
+ 	setenv("TERM", op->term, 1);
+ }
+@@ -1037,12 +1041,7 @@ static void termio_init(struct options *op, struct termios *tp)
+ 
+ 	if (op->flags & F_VCONSOLE) {
+ #if defined(IUTF8) && defined(KDGKBMODE)
+-		int mode;
+-
+-		/* Detect mode of current keyboard setup, e.g. for UTF-8 */
+-		if (ioctl(STDIN_FILENO, KDGKBMODE, &mode) < 0)
+-			mode = K_RAW;
+-		switch(mode) {
++		switch(op->kbmode) {
+ 		case K_UNICODE:
+ 			setlocale(LC_CTYPE, "C.UTF-8");
+ 			op->flags |= F_UTF8;
+-- 
+2.7.4
+
diff --git a/SOURCES/0081-chrt-backport-DEADLINE-scheduler-support.patch b/SOURCES/0081-chrt-backport-DEADLINE-scheduler-support.patch
new file mode 100644
index 0000000..fda445b
--- /dev/null
+++ b/SOURCES/0081-chrt-backport-DEADLINE-scheduler-support.patch
@@ -0,0 +1,791 @@
+From 674769893bb8d5f1991c6a3e5d96337b37aeb86f Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Mon, 27 Jun 2016 14:14:28 +0200
+Subject: [PATCH 81/86] chrt: backport DEADLINE scheduler support
+
+Backport upstream commits:
+
+2e31d1c chrt: validate priority before trying to use it
+b3a5067 chrt: make --sched-* short options to require an argument
+a03eac5 chrt: restore removed ifdef SCHED_RESET_ON_FORK
+59e4a38 chrt: fix case SCHED_RR
+acde3a0 chrt: use sched_getattr()
+1a7e639 chrt: add support for SCHED_DEADLINE
+1516758 chrt: use sched_setattr() if available
+a6fec53 chrt: make usage more readable
+4820a73 chrt: set function refactoring
+a30cf65 chrt: output function refactoring
+7a4ea56 chrt: add control struct
+9acbe2a chrt: slice up the usage text and normalize its layout
+4e4bc0c chrt: make the usage synopsis clearer
+3fabc36 chrt: fix --help inconsistency
+451dbcf textual: add a docstring to most of the utilities
+a587cc5 textual: use manual tail usage() macro
+f627750 textual: use version printing macro everywhere
+a7560c0 textual: make the license of chrt and taskset slightly more explicit
+4ce393f textual: fix several typos and angular brackets in messages
+6f27e44 chrt: add fallback to be usable on kernels without sched_{get,set}attr
+
+* Fri Jul 01 2016 re-spin [kzak]:
+- add fallback for old glibc-headers without SYS_sched_{set,get}attr
+
+* Tue Jul 12 016 re-spin [kzak]:
+- add runtime fallback for systems without sched_{get,set}attr syscalls
+
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1298384
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ configure.ac      |  17 +-
+ m4/ul.m4          |   8 -
+ schedutils/chrt.c | 547 ++++++++++++++++++++++++++++++++++++++----------------
+ 3 files changed, 400 insertions(+), 172 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index fe0a011..266ef08 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -338,6 +338,8 @@ AC_CHECK_FUNCS([ \
+ 	scandirat \
+ 	setresgid \
+ 	setresuid \
++	sched_setattr \
++	sched_setscheduler \
+ 	sigqueue \
+ 	srandom \
+ 	strnchr \
+@@ -1383,6 +1385,20 @@ UL_REQUIRES_SYSCALL_CHECK([taskset],
+ AM_CONDITIONAL(BUILD_TASKSET, test "x$build_taskset" = xyes)
+ 
+ 
++have_schedsetter=no
++AS_IF([test "x$ac_cv_func_sched_setscheduler" = xyes], [have_schedsetter=yes],
++      [test "x$ac_cv_func_sched_setattr" = xyes], [have_schedsetter=yes])
++
++UL_BUILD_INIT([chrt], [check])
++UL_REQUIRES_BUILD([chrt], [schedutils])
++UL_REQUIRES_HAVE([chrt], [schedsetter], [sched_set functions])
++AM_CONDITIONAL([BUILD_CHRT], [test "x$build_chrt" = xyes])
++
++AS_IF([test "x$build_chrt" = xyes], [
++	UL_CHECK_SYSCALL([sched_setattr])
++])
++
++
+ AC_ARG_ENABLE([wall],
+   AS_HELP_STRING([--disable-wall], [do not build wall]),
+   [], enable_wall=yes
+@@ -1562,7 +1578,6 @@ AC_ARG_VAR([SOLIB_LDFLAGS],
+ 
+ LIBS=""
+ 
+-
+ AC_CONFIG_HEADERS(config.h)
+ 
+ #
+diff --git a/m4/ul.m4 b/m4/ul.m4
+index c0082d0..db44589 100644
+--- a/m4/ul.m4
++++ b/m4/ul.m4
+@@ -92,7 +92,6 @@ AC_DEFUN([UL_CHECK_SYSCALL], [
+       ])
+     ul_cv_syscall_$1=$syscall
+     ])
+-  AM_CONDITIONAL([HAVE_]m4_toupper($1), [test "x$ul_cv_syscall_$1" != xno])
+   case $ul_cv_syscall_$1 in #(
+   no) AC_MSG_WARN([Unable to detect syscall $1.]) ;;
+   SYS_*) ;;
+@@ -266,13 +265,6 @@ AC_DEFUN([UL_REQUIRES_SYSCALL_CHECK], [
+   m4_define([suffix], m4_default([$4],$1))
+   m4_define([callname], m4_default([$3],$1))
+ 
+-  dnl This is default, $3 will redefine the condition
+-  dnl
+-  dnl TODO: remove this junk, AM_CONDITIONAL should not be used for any HAVE_*
+-  dnl       variables, all we need is BUILD_* only.
+-  dnl
+-  AM_CONDITIONAL([HAVE_]m4_toupper(callname), [false])
+-
+   if test "x$[build_]suffix" != xno; then
+     if test "x$[enable_]suffix" = xno; then
+       [build_]suffix=no
+diff --git a/schedutils/chrt.c b/schedutils/chrt.c
+index 20df6fa..edae0d9 100644
+--- a/schedutils/chrt.c
++++ b/schedutils/chrt.c
+@@ -1,13 +1,11 @@
+ /*
+- * chrt.c - chrt
+- * Command-line utility for manipulating a task's real-time attributes 
++ * chrt.c - manipulate a task's real-time attributes
+  *
+- * Robert Love <rml@tech9.net>
+- * 27-Apr-2002: initial version
+- * 04-May-2011: make thread aware - Davidlohr Bueso <dave@gnu.org>
++ * 27-Apr-2002: initial version - Robert Love <rml@tech9.net>
++ * 04-May-2011: make it thread-aware - Davidlohr Bueso <dave@gnu.org>
+  *
+  * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License, v2, as
++ * it under the terms of the GNU General Public License, version 2, as
+  * published by the Free Software Foundation
+  *
+  * This program is distributed in the hope that it will be useful,
+@@ -50,108 +48,260 @@
+ # define SCHED_IDLE 5
+ #endif
+ 
++/* flag by sched_getscheduler() */
+ #if defined(__linux__) && !defined(SCHED_RESET_ON_FORK)
+-#define SCHED_RESET_ON_FORK 0x40000000
++# define SCHED_RESET_ON_FORK 0x40000000
+ #endif
+ 
++/* flag by sched_getattr() */
++#if defined(__linux__) && !defined(SCHED_FLAG_RESET_ON_FORK)
++# define SCHED_FLAG_RESET_ON_FORK 0x01
++#endif
+ 
+-static void __attribute__((__noreturn__)) show_usage(int rc)
+-{
+-	FILE *out = rc == EXIT_SUCCESS ? stdout : stderr;
++#if defined (__linux__) && !defined(HAVE_SCHED_SETATTR)
++# include <sys/syscall.h>
++#endif
+ 
+-	fprintf(out, _(
+-	"\nchrt - manipulate real-time attributes of a process\n"
+-	"\nSet policy:\n"
+-	"  chrt [options] [-policy] <priority> [-p <pid> | <command> <arg> ...]\n"
+-	"\nGet policy:\n"
+-	"  chrt [options] -p <pid>\n"));
+-
+-	fprintf(out, _(
+-	"\nScheduling policies:\n"
+-	"  -b | --batch         set policy to SCHED_BATCH\n"
+-	"  -f | --fifo          set policy to SCHED_FIFO\n"
+-	"  -i | --idle          set policy to SCHED_IDLE\n"
+-	"  -o | --other         set policy to SCHED_OTHER\n"
+-	"  -r | --rr            set policy to SCHED_RR (default)\n"));
++/* usable kernel-headers, but old glibc-headers */
++#if defined (__linux__) && !defined(SYS_sched_setattr) && defined(__NR_sched_setattr)
++# define SYS_sched_setattr __NR_sched_setattr
++#endif
+ 
+-#ifdef SCHED_RESET_ON_FORK
+-	fprintf(out, _(
+-	"\nScheduling flags:\n"
+-	"  -R | --reset-on-fork set SCHED_RESET_ON_FORK for FIFO or RR\n"));
++#if defined (__linux__) && !defined(SYS_sched_getattr) && defined(__NR_sched_getattr)
++# define SYS_sched_getattr __NR_sched_getattr
+ #endif
+-	fprintf(out, _(
+-	"\nOptions:\n"
+-	"  -a | --all-tasks     operate on all the tasks (threads) for a given pid\n"
+-	"  -h | --help          display this help\n"
+-	"  -m | --max           show min and max valid priorities\n"
+-	"  -p | --pid           operate on existing given pid\n"
+-	"  -v | --verbose       display status information\n"
+-	"  -V | --version       output version information\n\n"));
+ 
+-	exit(rc);
++#if defined (__linux__) && !defined(HAVE_SCHED_SETATTR) && defined(SYS_sched_setattr)
++# define HAVE_SCHED_SETATTR
++
++struct sched_attr {
++	uint32_t size;
++	uint32_t sched_policy;
++	uint64_t sched_flags;
++
++	/* SCHED_NORMAL, SCHED_BATCH */
++	int32_t sched_nice;
++
++	/* SCHED_FIFO, SCHED_RR */
++	uint32_t sched_priority;
++
++	/* SCHED_DEADLINE (nsec) */
++	uint64_t sched_runtime;
++	uint64_t sched_deadline;
++	uint64_t sched_period;
++};
++
++static int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags)
++{
++	return syscall(SYS_sched_setattr, pid, attr, flags);
+ }
+ 
+-static void show_rt_info(pid_t pid, int isnew)
++static int sched_getattr(pid_t pid, struct sched_attr *attr, unsigned int size, unsigned int flags)
+ {
+-	struct sched_param sp;
+-	int policy;
++	return syscall(SYS_sched_getattr, pid, attr, size, flags);
++}
++#endif
+ 
+-	/* don't display "pid 0" as that is confusing */
+-	if (!pid)
+-		pid = getpid();
++/* the SCHED_DEADLINE is supported since Linux 3.14
++ * commit id aab03e05e8f7e26f51dee792beddcb5cca9215a5
++ * -- sched_setattr() is required for this policy!
++ */
++#if defined (__linux__) && !defined(SCHED_DEADLINE) && defined(HAVE_SCHED_SETATTR)
++# define SCHED_DEADLINE 6
++#endif
+ 
+-	policy = sched_getscheduler(pid);
+-	if (policy == -1)
+-		err(EXIT_FAILURE, _("failed to get pid %d's policy"), pid);
++/* control struct */
++struct chrt_ctl {
++	pid_t	pid;
++	int	policy;				/* SCHED_* */
++	int	priority;
+ 
+-	if (isnew)
+-		printf(_("pid %d's new scheduling policy: "), pid);
+-	else
+-		printf(_("pid %d's current scheduling policy: "), pid);
++	uint64_t runtime;			/* --sched-* options */
++	uint64_t deadline;
++	uint64_t period;
++
++	unsigned int all_tasks : 1,		/* all threads of the PID */
++		     reset_on_fork : 1,		/* SCHED_RESET_ON_FORK */
++		     altered : 1,		/* sched_set**() used */
++		     verbose : 1;		/* verbose output */
++};
++
++static void __attribute__((__noreturn__)) show_usage(int rc)
++{
++	FILE *out = rc == EXIT_SUCCESS ? stdout : stderr;
+ 
++	fputs(_("Show or change the real-time scheduling attributes of a process.\n"), out);
++	fputs(USAGE_SEPARATOR, out);
++	fputs(_("Set policy:\n"
++	" chrt [options] <priority> <command> [<arg>...]\n"
++	" chrt [options] --pid <priority> <pid>\n"), out);
++	fputs(USAGE_SEPARATOR, out);
++	fputs(_("Get policy:\n"
++	" chrt [options] -p <pid>\n"), out);
++
++	fputs(USAGE_SEPARATOR, out);
++	fputs(_("Policy options:\n"), out);
++	fputs(_(" -b, --batch          set policy to SCHED_BATCH\n"), out);
++	fputs(_(" -d, --deadline       set policy to SCHED_DEADLINE\n"), out);
++	fputs(_(" -f, --fifo           set policy to SCHED_FIFO\n"), out);
++	fputs(_(" -i, --idle           set policy to SCHED_IDLE\n"), out);
++	fputs(_(" -o, --other          set policy to SCHED_OTHER\n"), out);
++	fputs(_(" -r, --rr             set policy to SCHED_RR (default)\n"), out);
++
++	fputs(USAGE_SEPARATOR, out);
++	fputs(_("Scheduling options:\n"), out);
++	fputs(_(" -R, --reset-on-fork       set SCHED_RESET_ON_FORK for FIFO or RR\n"), out);
++	fputs(_(" -T, --sched-runtime <ns>  runtime parameter for DEADLINE\n"), out);
++	fputs(_(" -P, --sched-period <ns>   period parameter for DEADLINE\n"), out);
++	fputs(_(" -D, --sched-deadline <ns> deadline parameter for DEADLINE\n"), out);
++
++	fputs(USAGE_SEPARATOR, out);
++	fputs(_("Other options:\n"), out);
++	fputs(_(" -a, --all-tasks      operate on all the tasks (threads) for a given pid\n"), out);
++	fputs(_(" -m, --max            show min and max valid priorities\n"), out);
++	fputs(_(" -p, --pid            operate on existing given pid\n"), out);
++	fputs(_(" -v, --verbose        display status information\n"), out);
++
++	fputs(USAGE_SEPARATOR, out);
++	fputs(USAGE_HELP, out);
++	fputs(USAGE_VERSION, out);
++
++	fprintf(out, USAGE_MAN_TAIL("chrt(1)"));
++	exit(rc);
++}
++
++static const char *get_policy_name(int policy)
++{
+ 	switch (policy) {
+ 	case SCHED_OTHER:
+-		printf("SCHED_OTHER\n");
+-		break;
++		return "SCHED_OTHER";
+ 	case SCHED_FIFO:
+-		printf("SCHED_FIFO\n");
+-		break;
+ #ifdef SCHED_RESET_ON_FORK
+ 	case SCHED_FIFO | SCHED_RESET_ON_FORK:
+-		printf("SCHED_FIFO|SCHED_RESET_ON_FORK\n");
+-		break;
+ #endif
++		return "SCHED_FIFO";
+ #ifdef SCHED_IDLE
+ 	case SCHED_IDLE:
+-		printf("SCHED_IDLE\n");
+-		break;
++		return "SCHED_IDLE";
+ #endif
+ 	case SCHED_RR:
+-		printf("SCHED_RR\n");
+-		break;
+ #ifdef SCHED_RESET_ON_FORK
+ 	case SCHED_RR | SCHED_RESET_ON_FORK:
+-		printf("SCHED_RR|SCHED_RESET_ON_FORK\n");
+-		break;
+ #endif
++		return "SCHED_RR";
+ #ifdef SCHED_BATCH
+ 	case SCHED_BATCH:
+-		printf("SCHED_BATCH\n");
+-		break;
++		return "SCHED_BATCH";
++#endif
++#ifdef SCHED_DEADLINE
++	case SCHED_DEADLINE:
++		return "SCHED_DEADLINE";
+ #endif
+ 	default:
+-		warnx(_("unknown scheduling policy"));
++		break;
++	}
++
++	return _("unknown");
++}
++
++static void show_sched_pid_info(struct chrt_ctl *ctl, pid_t pid)
++{
++	int policy, reset_on_fork = 0, prio = 0;
++#ifdef SCHED_DEADLINE
++	uint64_t deadline = 0, runtime = 0, period = 0;
++#endif
++
++	/* don't display "pid 0" as that is confusing */
++	if (!pid)
++		pid = getpid();
++
++	errno = 0;
++
++	/*
++	 * New way
++	 */
++#ifdef HAVE_SCHED_SETATTR
++	{
++		struct sched_attr sa;
++
++		if (sched_getattr(pid, &sa, sizeof(sa), 0) != 0) {
++			if (errno == ENOSYS)
++				goto fallback;
++			err(EXIT_FAILURE, _("failed to get pid %d's policy"), pid);
++		}
++
++		policy = sa.sched_policy;
++		prio = sa.sched_priority;
++		reset_on_fork = sa.sched_flags & SCHED_FLAG_RESET_ON_FORK;
++		deadline = sa.sched_deadline;
++		runtime = sa.sched_runtime;
++		period = sa.sched_period;
+ 	}
++#endif
++
++	/*
++	 * Old way
++	 */
++fallback:
++	if (errno == ENOSYS) {
++		struct sched_param sp;
+ 
+-	if (sched_getparam(pid, &sp))
+-		err(EXIT_FAILURE, _("failed to get pid %d's attributes"), pid);
++		policy = sched_getscheduler(pid);
++		if (policy == -1)
++			err(EXIT_FAILURE, _("failed to get pid %d's policy"), pid);
+ 
+-	if (isnew)
+-		printf(_("pid %d's new scheduling priority: %d\n"),
+-		       pid, sp.sched_priority);
++		if (sched_getparam(pid, &sp) != 0)
++			err(EXIT_FAILURE, _("failed to get pid %d's attributes"), pid);
++		else
++			prio = sp.sched_priority;
++# ifdef SCHED_RESET_ON_FORK
++		if (policy == (SCHED_FIFO|SCHED_RESET_ON_FORK) || policy == (SCHED_BATCH|SCHED_RESET_ON_FORK))
++			reset_on_fork = 1;
++# endif
++	}
++
++	if (ctl->altered)
++		printf(_("pid %d's new scheduling policy: %s"), pid, get_policy_name(policy));
++	else
++		printf(_("pid %d's current scheduling policy: %s"), pid, get_policy_name(policy));
++
++	if (reset_on_fork)
++		printf("|SCHED_RESET_ON_FORK");
++	putchar('\n');
++
++	if (ctl->altered)
++		printf(_("pid %d's new scheduling priority: %d\n"), pid, prio);
+ 	else
+-		printf(_("pid %d's current scheduling priority: %d\n"),
+-		       pid, sp.sched_priority);
++		printf(_("pid %d's current scheduling priority: %d\n"), pid, prio);
++
++#ifdef SCHED_DEADLINE
++	if (policy == SCHED_DEADLINE) {
++		if (ctl->altered)
++			printf(_("pid %d's new runtime/deadline/period parameters: %ju/%ju/%ju\n"),
++					pid, runtime, deadline, period);
++		else
++			printf(_("pid %d's current runtime/deadline/period parameters: %ju/%ju/%ju\n"),
++					pid, runtime, deadline, period);
++	}
++#endif
++}
++
++
++static void show_sched_info(struct chrt_ctl *ctl)
++{
++	if (ctl->all_tasks) {
++		pid_t tid;
++		struct proc_tasks *ts = proc_open_tasks(ctl->pid);
++
++		if (!ts)
++			err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
++
++		while (!proc_next_tid(ts, &tid))
++			show_sched_pid_info(ctl, tid);
++
++		proc_close_tasks(ts);
++	} else
++		show_sched_pid_info(ctl, ctl->pid);
+ }
+ 
+ static void show_min_max(void)
+@@ -167,52 +317,116 @@ static void show_min_max(void)
+ #ifdef SCHED_IDLE
+ 		SCHED_IDLE,
+ #endif
+-	};
+-	const char *names[] = {
+-		"OTHER",
+-		"FIFO",
+-		"RR",
+-#ifdef SCHED_BATCH
+-		"BATCH",
+-#endif
+-#ifdef SCHED_IDLE
+-		"IDLE",
++#ifdef SCHED_DEADLINE
++		SCHED_DEADLINE,
+ #endif
+ 	};
+ 
+ 	for (i = 0; i < ARRAY_SIZE(policies); i++) {
+-		int max = sched_get_priority_max(policies[i]);
+-		int min = sched_get_priority_min(policies[i]);
++		int plc = policies[i];
++		int max = sched_get_priority_max(plc);
++		int min = sched_get_priority_min(plc);
+ 
+ 		if (max >= 0 && min >= 0)
+-			printf(_("SCHED_%s min/max priority\t: %d/%d\n"),
+-					names[i], min, max);
++			printf(_("%s min/max priority\t: %d/%d\n"),
++					get_policy_name(plc), min, max);
+ 		else
+-			printf(_("SCHED_%s not supported?\n"), names[i]);
++			printf(_("%s not supported?\n"), get_policy_name(plc));
+ 	}
+ }
+ 
++static int set_sched_one_by_setscheduler(struct chrt_ctl *ctl, pid_t pid)
++{
++	struct sched_param sp = { .sched_priority = ctl->priority };
++	int policy = ctl->policy;
++
++# ifdef SCHED_RESET_ON_FORK
++	if (ctl->reset_on_fork)
++		policy |= SCHED_RESET_ON_FORK;
++# endif
++	return sched_setscheduler(pid, policy, &sp);
++}
++
++
++#ifndef HAVE_SCHED_SETATTR
++static int set_sched_one(struct chrt_ctl *ctl, pid_t pid)
++{
++	return set_sched_one_by_setscheduler(ctl, pid);
++}
++
++#else /* !HAVE_SCHED_SETATTR */
++static int set_sched_one(struct chrt_ctl *ctl, pid_t pid)
++{
++	/* use main() to check if the setting makes sense */
++	struct sched_attr sa = {
++		.size		= sizeof(struct sched_attr),
++		.sched_policy	= ctl->policy,
++		.sched_priority	= ctl->priority,
++		.sched_runtime  = ctl->runtime,
++		.sched_period   = ctl->period,
++		.sched_deadline = ctl->deadline
++	};
++	int rc;
++
++# ifdef SCHED_RESET_ON_FORK
++	if (ctl->reset_on_fork)
++		sa.sched_flags |= SCHED_RESET_ON_FORK;
++# endif
++	errno = 0;
++	rc = sched_setattr(pid, &sa, 0);
++
++	if (rc != 0 && errno == ENOSYS && ctl->policy != SCHED_DEADLINE)
++		/* fallback -- build with new kernel/libc, but executed on old kernels */
++		rc = set_sched_one_by_setscheduler(ctl, pid);
++
++	return rc;
++}
++#endif /* HAVE_SCHED_SETATTR */
++
++static void set_sched(struct chrt_ctl *ctl)
++{
++	if (ctl->all_tasks) {
++		pid_t tid;
++		struct proc_tasks *ts = proc_open_tasks(ctl->pid);
++
++		if (!ts)
++			err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
++
++		while (!proc_next_tid(ts, &tid))
++			if (set_sched_one(ctl, tid) == -1)
++				err(EXIT_FAILURE, _("failed to set tid %d's policy"), tid);
++
++		proc_close_tasks(ts);
++
++	} else if (set_sched_one(ctl, ctl->pid) == -1)
++		err(EXIT_FAILURE, _("failed to set pid %d's policy"), ctl->pid);
++
++	ctl->altered = 1;
++}
++
+ int main(int argc, char **argv)
+ {
+-	int i, policy = SCHED_RR, priority = 0, verbose = 0, policy_flag = 0,
+-	    all_tasks = 0;
+-	struct sched_param sp;
+-	pid_t pid = -1;
++	struct chrt_ctl _ctl = { .pid = -1 }, *ctl = &_ctl;
++	int c;
+ 
+ 	static const struct option longopts[] = {
+-		{ "all-tasks",  0, NULL, 'a' },
+-		{ "batch",	0, NULL, 'b' },
+-		{ "fifo",	0, NULL, 'f' },
+-		{ "idle",	0, NULL, 'i' },
+-		{ "pid",	0, NULL, 'p' },
+-		{ "help",	0, NULL, 'h' },
+-		{ "max",        0, NULL, 'm' },
+-		{ "other",	0, NULL, 'o' },
+-		{ "rr",		0, NULL, 'r' },
+-		{ "reset-on-fork", 0, NULL, 'R' },
+-		{ "verbose",	0, NULL, 'v' },
+-		{ "version",	0, NULL, 'V' },
+-		{ NULL,		0, NULL, 0 }
++		{ "all-tasks",  no_argument, NULL, 'a' },
++		{ "batch",	no_argument, NULL, 'b' },
++		{ "deadline",   no_argument, NULL, 'd' },
++		{ "fifo",	no_argument, NULL, 'f' },
++		{ "idle",	no_argument, NULL, 'i' },
++		{ "pid",	no_argument, NULL, 'p' },
++		{ "help",	no_argument, NULL, 'h' },
++		{ "max",        no_argument, NULL, 'm' },
++		{ "other",	no_argument, NULL, 'o' },
++		{ "rr",		no_argument, NULL, 'r' },
++		{ "sched-runtime",  required_argument, NULL, 'T' },
++		{ "sched-period",   required_argument, NULL, 'P' },
++		{ "sched-deadline", required_argument, NULL, 'D' },
++		{ "reset-on-fork",  no_argument,       NULL, 'R' },
++		{ "verbose",	no_argument, NULL, 'v' },
++		{ "version",	no_argument, NULL, 'V' },
++		{ NULL,		no_argument, NULL, 0 }
+ 	};
+ 
+ 	setlocale(LC_ALL, "");
+@@ -220,51 +434,63 @@ int main(int argc, char **argv)
+ 	textdomain(PACKAGE);
+ 	atexit(close_stdout);
+ 
+-	while((i = getopt_long(argc, argv, "+abfiphmoRrvV", longopts, NULL)) != -1)
++	while((c = getopt_long(argc, argv, "+abdD:fiphmoP:T:rRvV", longopts, NULL)) != -1)
+ 	{
+ 		int ret = EXIT_FAILURE;
+ 
+-		switch (i) {
++		switch (c) {
+ 		case 'a':
+-			all_tasks = 1;
++			ctl->all_tasks = 1;
+ 			break;
+ 		case 'b':
+ #ifdef SCHED_BATCH
+-			policy = SCHED_BATCH;
++			ctl->policy = SCHED_BATCH;
++#endif
++			break;
++
++		case 'd':
++#ifdef SCHED_DEADLINE
++			ctl->policy = SCHED_DEADLINE;
+ #endif
+ 			break;
+ 		case 'f':
+-			policy = SCHED_FIFO;
++			ctl->policy = SCHED_FIFO;
+ 			break;
+ 		case 'R':
+-#ifdef SCHED_RESET_ON_FORK
+-			policy_flag |= SCHED_RESET_ON_FORK;
+-#endif
++			ctl->reset_on_fork = 1;
+ 			break;
+ 		case 'i':
+ #ifdef SCHED_IDLE
+-			policy = SCHED_IDLE;
++			ctl->policy = SCHED_IDLE;
+ #endif
+ 			break;
+ 		case 'm':
+ 			show_min_max();
+ 			return EXIT_SUCCESS;
+ 		case 'o':
+-			policy = SCHED_OTHER;
++			ctl->policy = SCHED_OTHER;
+ 			break;
+ 		case 'p':
+ 			errno = 0;
+-			pid = strtos32_or_err(argv[argc - 1], _("invalid PID argument"));
++			ctl->pid = strtos32_or_err(argv[argc - 1], _("invalid PID argument"));
+ 			break;
+ 		case 'r':
+-			policy = SCHED_RR;
++			ctl->policy = SCHED_RR;
+ 			break;
+ 		case 'v':
+-			verbose = 1;
++			ctl->verbose = 1;
++			break;
++		case 'T':
++			ctl->runtime = strtou64_or_err(optarg, _("invalid runtime argument"));
++			break;
++		case 'P':
++			ctl->period = strtou64_or_err(optarg, _("invalid period argument"));
++			break;
++		case 'D':
++			ctl->deadline = strtou64_or_err(optarg, _("invalid deadline argument"));
+ 			break;
+ 		case 'V':
+-			printf(_("%s from %s\n"), program_invocation_short_name,
+-			       PACKAGE_STRING);
++			printf(UTIL_LINUX_VERSION);
+ 			return EXIT_SUCCESS;
+ 		case 'h':
+ 			ret = EXIT_SUCCESS;
+@@ -274,61 +500,56 @@ int main(int argc, char **argv)
+ 		}
+ 	}
+ 
+-	if (((pid > -1) && argc - optind < 1) ||
+-	    ((pid == -1) && argc - optind < 2))
++	if (((ctl->pid > -1) && argc - optind < 1) ||
++	    ((ctl->pid == -1) && argc - optind < 2))
+ 		show_usage(EXIT_FAILURE);
+ 
+-	if ((pid > -1) && (verbose || argc - optind == 1)) {
+-		if (all_tasks) {
+-			pid_t tid;
+-			struct proc_tasks *ts = proc_open_tasks(pid);
+-
+-			if (!ts)
+-				err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
+-			while (!proc_next_tid(ts, &tid))
+-				show_rt_info(tid, FALSE);
+-			proc_close_tasks(ts);
+-		} else
+-			show_rt_info(pid, FALSE);
+-
++	if ((ctl->pid > -1) && (ctl->verbose || argc - optind == 1)) {
++		show_sched_info(ctl);
+ 		if (argc - optind == 1)
+ 			return EXIT_SUCCESS;
+ 	}
+ 
+ 	errno = 0;
+-	priority = strtos32_or_err(argv[optind], _("invalid priority argument"));
++	ctl->priority = strtos32_or_err(argv[optind], _("invalid priority argument"));
+ 
+ #ifdef SCHED_RESET_ON_FORK
+-	/* sanity check */
+-	if ((policy_flag & SCHED_RESET_ON_FORK) &&
+-	    !(policy == SCHED_FIFO || policy == SCHED_RR))
+-		errx(EXIT_FAILURE, _("SCHED_RESET_ON_FORK flag is supported for "
++	if (ctl->reset_on_fork && ctl->policy != SCHED_FIFO && ctl->policy != SCHED_RR)
++		errx(EXIT_FAILURE, _("--reset-on-fork option is supported for "
+ 				     "SCHED_FIFO and SCHED_RR policies only"));
+ #endif
+-
+-	policy |= policy_flag;
+-
+-	if (pid == -1)
+-		pid = 0;
+-	sp.sched_priority = priority;
+-
+-	if (all_tasks) {
+-		pid_t tid;
+-		struct proc_tasks *ts = proc_open_tasks(pid);
+-
+-		if (!ts)
+-			err(EXIT_FAILURE, _("cannot obtain the list of tasks"));
+-		while (!proc_next_tid(ts, &tid))
+-			if (sched_setscheduler(tid, policy, &sp) == -1)
+-				err(EXIT_FAILURE, _("failed to set tid %d's policy"), tid);
+-		proc_close_tasks(ts);
+-	} else if (sched_setscheduler(pid, policy, &sp) == -1)
+-		err(EXIT_FAILURE, _("failed to set pid %d's policy"), pid);
+-
+-	if (verbose)
+-		show_rt_info(pid, TRUE);
+-
+-	if (!pid) {
++#ifdef SCHED_DEADLINE
++	if ((ctl->runtime || ctl->deadline || ctl->period) && ctl->policy != SCHED_DEADLINE)
++		errx(EXIT_FAILURE, _("--sched-{runtime,deadline,period} options "
++				     "are supported for SCHED_DEADLINE only"));
++	if (ctl->policy == SCHED_DEADLINE) {
++		/* The basic rule is runtime <= deadline <= period, so we can
++		 * make deadline and runtime optional on command line. Note we
++		 * don't check any values or set any defaults, it's kernel
++		 * responsibility.
++		 */
++		if (ctl->deadline == 0)
++			ctl->deadline = ctl->period;
++		if (ctl->runtime == 0)
++			ctl->runtime = ctl->deadline;
++	}
++#else
++	if (ctl->runtime || ctl->deadline || ctl->period)
++		errx(EXIT_FAILURE, _("SCHED_DEADLINE is unsupported"));
++#endif
++	if (ctl->pid == -1)
++		ctl->pid = 0;
++	if (ctl->priority < sched_get_priority_min(ctl->policy) ||
++	    sched_get_priority_max(ctl->policy) < ctl->priority)
++		errx(EXIT_FAILURE,
++		     _("unsupported priority value for the policy: %d: see --max for valid range"),
++		     ctl->priority);
++	set_sched(ctl);
++
++	if (ctl->verbose)
++		show_sched_info(ctl);
++
++	if (!ctl->pid) {
+ 		argv += optind + 1;
+ 		execvp(argv[0], argv);
+ 		err(EXIT_FAILURE, _("failed to execute %s"), argv[0]);
+-- 
+2.7.4
+
diff --git a/SOURCES/0082-fdisk-backport-DOS-logical-partitions-chain-reorder.patch b/SOURCES/0082-fdisk-backport-DOS-logical-partitions-chain-reorder.patch
new file mode 100644
index 0000000..23b1f47
--- /dev/null
+++ b/SOURCES/0082-fdisk-backport-DOS-logical-partitions-chain-reorder.patch
@@ -0,0 +1,211 @@
+From 28b08b639aeaadbfcc3fb66558e6b392b2b5d44c Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Tue, 28 Jun 2016 11:30:21 +0200
+Subject: [PATCH 82/86] fdisk: backport DOS logical partitions chain reorder
+
+... from the current upstream.
+
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1304246
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ fdisks/fdiskdoslabel.c | 170 +++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 116 insertions(+), 54 deletions(-)
+
+diff --git a/fdisks/fdiskdoslabel.c b/fdisks/fdiskdoslabel.c
+index fe04ac7..b7eb35a 100644
+--- a/fdisks/fdiskdoslabel.c
++++ b/fdisks/fdiskdoslabel.c
+@@ -55,6 +55,22 @@ static int MBRbuffer_changed;
+ #define cround(c, n)	(fdisk_context_use_cylinders(c) ? \
+ 				((n) / fdisk_context_get_units_per_sector(c)) + 1 : (n))
+ 
++
++static unsigned long long
++get_abs_partition_start(struct pte *pe)
++{
++	return pe->offset + get_start_sect(pe->part_table);
++}
++
++static unsigned long long
++get_abs_partition_end(struct pte *pe)
++{
++	unsigned long long size;
++
++	size = get_nr_sects(pe->part_table);
++	return get_abs_partition_start(pe) + size - (size ? 1 : 0);
++}
++
+ static void warn_alignment(struct fdisk_context *cxt)
+ {
+ 	if (nowarn)
+@@ -1254,67 +1270,113 @@ void dos_list_table_expert(struct fdisk_context *cxt, int extend)
+ 	}
+ }
+ 
+-/*
+- * Fix the chain of logicals.
+- * extended_offset is unchanged, the set of sectors used is unchanged
+- * The chain is sorted so that sectors increase, and so that
+- * starting sectors increase.
+- *
+- * After this it may still be that cfdisk doesn't like the table.
+- * (This is because cfdisk considers expanded parts, from link to
+- * end of partition, and these may still overlap.)
+- * Now
+- *   sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
+- * may help.
+- */
++
++static void print_chain_of_logicals(struct fdisk_context *cxt)
++{
++	size_t i;
++
++	fputc('\n', stdout);
++
++	for (i = 4; i < cxt->label->nparts_max; i++) {
++		struct pte *pe = &ptes[i];
++
++		fprintf(stderr, "#%02zu EBR [%10ju], "
++			"data[start=%10ju (%10ju), size=%10ju], "
++			"link[start=%10ju (%10ju), size=%10ju]\n",
++			i, (uintmax_t) pe->offset,
++			/* data */
++			(uintmax_t) get_start_sect(pe->part_table),
++			(uintmax_t) get_abs_partition_start(pe),
++			(uintmax_t) get_nr_sects(pe->part_table),
++			/* link */
++			(uintmax_t) get_start_sect(pe->ext_pointer),
++			(uintmax_t) (extended_offset + get_start_sect(pe->ext_pointer)),
++			(uintmax_t) get_nr_sects(pe->ext_pointer));
++	}
++}
++
++static int cmp_ebr_offsets(const void *a, const void *b)
++{
++	struct pte *ae = (struct pte *) a,
++		   *be = (struct pte *) b;
++
++	if (ae->offset == 0 && be->offset == 0)
++		return 0;
++	if (ae->offset == 0)
++		return 1;
++	if (be->offset == 0)
++		return -1;
++
++	return cmp_numbers(ae->offset, be->offset);
++}
++
+ static void fix_chain_of_logicals(struct fdisk_context *cxt)
+ {
+-	size_t j, oj, ojj, sj, sjj;
+-	struct partition *pj,*pjj,tmp;
+-
+-	/* Stage 1: sort sectors but leave sector of part 4 */
+-	/* (Its sector is the global extended_offset.) */
+- stage1:
+-	for (j = 5; j < cxt->label->nparts_max - 1; j++) {
+-		oj = ptes[j].offset;
+-		ojj = ptes[j+1].offset;
+-		if (oj > ojj) {
+-			ptes[j].offset = ojj;
+-			ptes[j+1].offset = oj;
+-			pj = ptes[j].part_table;
+-			set_start_sect(pj, get_start_sect(pj)+oj-ojj);
+-			pjj = ptes[j+1].part_table;
+-			set_start_sect(pjj, get_start_sect(pjj)+ojj-oj);
+-			set_start_sect(ptes[j-1].ext_pointer,
+-				       ojj-extended_offset);
+-			set_start_sect(ptes[j].ext_pointer,
+-				       oj-extended_offset);
+-			goto stage1;
++	struct pte *last;
++	size_t i;
++
++	DBG(CONTEXT, print_chain_of_logicals(cxt));
++
++	/* Sort chain by EBR offsets */
++	qsort(&ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte),
++			cmp_ebr_offsets);
++
++again:
++	/* Sort data partitions by start */
++	for (i = 4; i < cxt->label->nparts_max - 1; i++) {
++		struct pte *cur = &ptes[i],
++			   *nxt = &ptes[i + 1];
++
++		if (get_abs_partition_start(cur) >
++		    get_abs_partition_start(nxt)) {
++
++			struct partition tmp = *cur->part_table;
++			sector_t cur_start = get_abs_partition_start(cur),
++				 nxt_start = get_abs_partition_start(nxt);
++
++			/* swap data partitions */
++			*cur->part_table = *nxt->part_table;
++			*nxt->part_table = tmp;
++
++			/* Recount starts according to EBR offsets, the absolute
++			 * address still has to be the same! */
++			set_start_sect(cur->part_table, nxt_start - cur->offset);
++			set_start_sect(nxt->part_table, cur_start - nxt->offset);
++
++			cur->changed = 1;
++			nxt->changed = 1;
++			goto again;
+ 		}
+ 	}
+ 
+-	/* Stage 2: sort starting sectors */
+- stage2:
+-	for (j = 4; j < cxt->label->nparts_max - 1; j++) {
+-		pj = ptes[j].part_table;
+-		pjj = ptes[j+1].part_table;
+-		sj = get_start_sect(pj);
+-		sjj = get_start_sect(pjj);
+-		oj = ptes[j].offset;
+-		ojj = ptes[j+1].offset;
+-		if (oj+sj > ojj+sjj) {
+-			tmp = *pj;
+-			*pj = *pjj;
+-			*pjj = tmp;
+-			set_start_sect(pj, ojj+sjj-oj);
+-			set_start_sect(pjj, oj+sj-ojj);
+-			goto stage2;
+-		}
++	/* Update EBR links */
++	for (i = 4; i < cxt->label->nparts_max - 1; i++) {
++		struct pte *cur = &ptes[i],
++			   *nxt = &ptes[i + 1];
++
++		sector_t noff = nxt->offset - extended_offset,
++		         ooff = get_start_sect(cur->ext_pointer);
++
++		if (noff == ooff)
++			continue;
++
++		DBG(CONTEXT, dbgprint("DOS: fix EBR [%10ju] link %ju -> %ju",
++			(uintmax_t) cur->offset,
++			(uintmax_t) ooff, (uintmax_t) noff));
++
++		set_partition(cxt, i, 1, nxt->offset,
++				get_abs_partition_end(nxt),
++				EXTENDED);
++	}
++
++	/* always terminate the chain ! */
++	last = &ptes[cxt->label->nparts_max - 1];
++	if (last) {
++		clear_partition(last->ext_pointer);
++		last->changed = 1;
+ 	}
+ 
+-	/* Probably something was changed */
+-	for (j = 4; j < cxt->label->nparts_max; j++)
+-		ptes[j].changed = 1;
++	DBG(CONTEXT, print_chain_of_logicals(cxt));
+ }
+ 
+ void dos_fix_partition_table_order(struct fdisk_context *cxt)
+-- 
+2.7.4
+
diff --git a/SOURCES/0083-tests-cleanup-tests.patch b/SOURCES/0083-tests-cleanup-tests.patch
new file mode 100644
index 0000000..10da5f2
--- /dev/null
+++ b/SOURCES/0083-tests-cleanup-tests.patch
@@ -0,0 +1,276 @@
+From d35dfbe11f7c6b84d40a6e54611dc3a8c9c27b4d Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Thu, 30 Jun 2016 11:00:54 +0200
+Subject: [PATCH 83/86] tests: cleanup tests
+
+... mostly to be more portable to the new kernels and userspace.
+
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1153770
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ tests/commands.sh                           |  1 +
+ tests/expected/blkid/md-raid0-whole         |  2 +-
+ tests/expected/blkid/md-raid1-part          |  2 +-
+ tests/expected/blkid/md-raid1-whole         |  2 +-
+ tests/expected/fdisk/align-512-4K           |  2 +-
+ tests/expected/fdisk/align-512-4K-63        |  2 +-
+ tests/expected/fdisk/align-512-4K-md        |  4 ++--
+ tests/expected/fdisk/align-512-512          |  2 +-
+ tests/expected/fdisk/align-512-512-topology |  2 +-
+ tests/expected/ipcs/limits                  |  4 ----
+ tests/functions.sh                          | 29 +++++++++++++++++++++++------
+ tests/ts/ipcs/limits                        |  5 +----
+ tests/ts/ipcs/limits2                       |  2 ++
+ tests/ts/libmount/context-utab              | 10 ++++++++--
+ 14 files changed, 44 insertions(+), 25 deletions(-)
+
+diff --git a/tests/commands.sh b/tests/commands.sh
+index a2948c9..e769b5d 100644
+--- a/tests/commands.sh
++++ b/tests/commands.sh
+@@ -64,3 +64,4 @@ TS_CMD_UL=${TS_CMD_UL-"$top_builddir/ul"}
+ TS_CMD_UMOUNT=${TS_CMD_UMOUNT:-"$top_builddir/umount"}
+ TS_CMD_UTMPDUMP=${TS_CMD_UTMPDUMP-"$top_builddir/utmpdump"}
+ TS_CMD_WHEREIS=${TS_CMD_WHEREIS-"$top_builddir/whereis"}
++TS_CMD_WIPEFS=${TS_CMD_WIPEFS-"$top_builddir/wipefs"}
+diff --git a/tests/expected/blkid/md-raid0-whole b/tests/expected/blkid/md-raid0-whole
+index 37dabf3..30825de 100644
+--- a/tests/expected/blkid/md-raid0-whole
++++ b/tests/expected/blkid/md-raid0-whole
+@@ -26,7 +26,7 @@ Command (m for help):
+ Disk /dev/md8: 104 MB, 104726528 bytes, 204544 sectors
+ Units = sectors of 1 * 512 = 512 bytes
+ Sector size (logical/physical): 512 bytes / 512 bytes
+-I/O size (minimum/optimal): 65536 bytes / 131072 bytes
++I/O size (minimum/optimal): 65536 bytes / <removed> bytes
+ Disk label type: dos
+ 
+ 
+diff --git a/tests/expected/blkid/md-raid1-part b/tests/expected/blkid/md-raid1-part
+index f409952..3d1b38c 100644
+--- a/tests/expected/blkid/md-raid1-part
++++ b/tests/expected/blkid/md-raid1-part
+@@ -24,7 +24,7 @@ Command (m for help):
+ Disk /dev/...: 53 MB, 53477376 bytes, 104448 sectors
+ Units = sectors of 1 * 512 = 512 bytes
+ Sector size (logical/physical): 512 bytes / 512 bytes
+-I/O size (minimum/optimal): 512 bytes / 32768 bytes
++I/O size (minimum/optimal): 512 bytes / <removed> bytes
+ Disk label type: dos
+ 
+ 
+diff --git a/tests/expected/blkid/md-raid1-whole b/tests/expected/blkid/md-raid1-whole
+index 8977312..8ab1da2 100644
+--- a/tests/expected/blkid/md-raid1-whole
++++ b/tests/expected/blkid/md-raid1-whole
+@@ -25,7 +25,7 @@ Command (m for help):
+ Disk /dev/md8: 52 MB, 52363264 bytes, 102272 sectors
+ Units = sectors of 1 * 512 = 512 bytes
+ Sector size (logical/physical): 512 bytes / 512 bytes
+-I/O size (minimum/optimal): 512 bytes / 512 bytes
++I/O size (minimum/optimal): 512 bytes / <removed> bytes
+ Disk label type: dos
+ 
+ 
+diff --git a/tests/expected/fdisk/align-512-4K b/tests/expected/fdisk/align-512-4K
+index e5a28f5..7b52fd2 100644
+--- a/tests/expected/fdisk/align-512-4K
++++ b/tests/expected/fdisk/align-512-4K
+@@ -57,7 +57,7 @@ Command (m for help):
+ Disk /dev/...: 52 MB, 52428800 bytes, 102400 sectors
+ Units = sectors of 1 * 512 = 512 bytes
+ Sector size (logical/physical): 512 bytes / 4096 bytes
+-I/O size (minimum/optimal): 4096 bytes / 32768 bytes
++I/O size (minimum/optimal): 4096 bytes / <removed> bytes
+ Disk label type: dos
+ 
+ 
+diff --git a/tests/expected/fdisk/align-512-4K-63 b/tests/expected/fdisk/align-512-4K-63
+index 7e236ce..302a429 100644
+--- a/tests/expected/fdisk/align-512-4K-63
++++ b/tests/expected/fdisk/align-512-4K-63
+@@ -57,7 +57,7 @@ Command (m for help):
+ Disk /dev/...: 52 MB, 52428800 bytes, 102400 sectors
+ Units = sectors of 1 * 512 = 512 bytes
+ Sector size (logical/physical): 512 bytes / 4096 bytes
+-I/O size (minimum/optimal): 4096 bytes / 32768 bytes
++I/O size (minimum/optimal): 4096 bytes / <removed> bytes
+ Alignment offset: 3584 bytes
+ Disk label type: dos
+ 
+diff --git a/tests/expected/fdisk/align-512-4K-md b/tests/expected/fdisk/align-512-4K-md
+index dbbbc05..e8f6ba1 100644
+--- a/tests/expected/fdisk/align-512-4K-md
++++ b/tests/expected/fdisk/align-512-4K-md
+@@ -28,7 +28,7 @@ Command (m for help):
+ Disk /dev/...: 52 MB, 52428800 bytes, 102400 sectors
+ Units = sectors of 1 * 512 = 512 bytes
+ Sector size (logical/physical): 512 bytes / 4096 bytes
+-I/O size (minimum/optimal): 4096 bytes / 32768 bytes
++I/O size (minimum/optimal): 4096 bytes / <removed> bytes
+ Disk label type: dos
+ 
+ 
+@@ -72,7 +72,7 @@ Command (m for help):
+ Disk /dev/md8: 51 MB, 51249152 bytes, 100096 sectors
+ Units = sectors of 1 * 512 = 512 bytes
+ Sector size (logical/physical): 512 bytes / 4096 bytes
+-I/O size (minimum/optimal): 65536 bytes / 131072 bytes
++I/O size (minimum/optimal): 65536 bytes / <removed> bytes
+ Disk label type: dos
+ 
+ 
+diff --git a/tests/expected/fdisk/align-512-512 b/tests/expected/fdisk/align-512-512
+index 018322f..8e03d90 100644
+--- a/tests/expected/fdisk/align-512-512
++++ b/tests/expected/fdisk/align-512-512
+@@ -53,7 +53,7 @@ Command (m for help):
+ Disk /dev/loop0: 52 MB, 52428800 bytes, 102400 sectors
+ Units = sectors of 1 * 512 = 512 bytes
+ Sector size (logical/physical): 512 bytes / 512 bytes
+-I/O size (minimum/optimal): 512 bytes / 512 bytes
++I/O size (minimum/optimal): 512 bytes / <removed> bytes
+ Disk label type: dos
+ 
+ 
+diff --git a/tests/expected/fdisk/align-512-512-topology b/tests/expected/fdisk/align-512-512-topology
+index b3d3114..fd57f60 100644
+--- a/tests/expected/fdisk/align-512-512-topology
++++ b/tests/expected/fdisk/align-512-512-topology
+@@ -53,7 +53,7 @@ Command (m for help):
+ Disk /dev/...: 52 MB, 52428800 bytes, 102400 sectors
+ Units = sectors of 1 * 512 = 512 bytes
+ Sector size (logical/physical): 512 bytes / 512 bytes
+-I/O size (minimum/optimal): 512 bytes / 32768 bytes
++I/O size (minimum/optimal): 512 bytes / <removed> bytes
+ Disk label type: dos
+ 
+ 
+diff --git a/tests/expected/ipcs/limits b/tests/expected/ipcs/limits
+index 7480a1e..e08fa54 100644
+--- a/tests/expected/ipcs/limits
++++ b/tests/expected/ipcs/limits
+@@ -1,8 +1,4 @@
+ load original values
+-check for difference between kernel and IPC
+-/proc/sys/kernel/shmmni OK
+-/proc/sys/kernel/shmall OK
+-/proc/sys/kernel/shmmax OK
+ maximalize kernel setting
+ re-check for difference between kernel and IPC
+ /proc/sys/kernel/shmmni OK
+diff --git a/tests/functions.sh b/tests/functions.sh
+index b2d493c..0d1c9c8 100644
+--- a/tests/functions.sh
++++ b/tests/functions.sh
+@@ -31,6 +31,17 @@ function ts_canonicalize {
+ 	fi
+ }
+ 
++function ts_check_test_command {
++	if [ ! -x "$1" ]; then
++		ts_skip "${1##*/} not found"
++	fi
++}
++
++function ts_check_prog {
++	local cmd=$1
++	type "$cmd" >/dev/null 2>&1 || ts_skip "missing in PATH: $cmd"
++}
++
+ function ts_skip_subtest {
+ 	echo " IGNORE ($1)"
+ }
+@@ -474,14 +485,20 @@ function ts_fdisk_clean {
+ 	local DEVNAME=$(basename "$1")
+ 
+ 	# remove non comparable parts of fdisk output
+-	if [ x"${DEVNAME}" != x"" ]; then
+-	       sed -i -e "s/\/dev\/${DEVNAME}/\/dev\/.../g" $TS_OUTPUT
++	if [ -n "${DEVNAME}" ]; then
++		sed -i -e "s@${DEVNAME}@...@;" $TS_OUTPUT
+ 	fi
+ 
+-	sed -i -e 's/Disk identifier:.*//g' \
+-	       -e 's/Building a new.*//g' \
+-	       -e 's/Welcome to fdisk.*//g' \
+-	       $TS_OUTPUT
++	sed -i \
++		-e 's/Disk identifier:.*//' \
++		-e 's/Building a new.*//' \
++		-e 's/Created a new.*//' \
++		-e 's/^Device[[:blank:]]*Start/Device             Start/' \
++		-e 's/^Device[[:blank:]]*Boot/Device     Boot/' \
++		-e 's/Welcome to fdisk.*//' \
++		-e 's/typescript file.*/typescript file <removed>./' \
++		-e 's@^\(I/O size (minimum/op.* bytes /\) [1-9][0-9]* @\1 <removed> @' \
++		$TS_OUTPUT
+ }
+ 
+ function ts_scsi_debug_init {
+diff --git a/tests/ts/ipcs/limits b/tests/ts/ipcs/limits
+index b44adb1..c4717c3 100755
+--- a/tests/ts/ipcs/limits
++++ b/tests/ts/ipcs/limits
+@@ -23,7 +23,7 @@ TS_DESC="limits overflow"
+ 
+ ts_init "$*"
+ ts_skip_nonroot
+-type bc >/dev/null 2>&1 || ts_skip "cannot find bc command"
++ts_check_prog "bc"
+ 
+ . $TS_SELF/functions.sh
+ 
+@@ -32,9 +32,6 @@ for i in $IPCS_IDX; do
+ 	SHM_ORG[$i]=$(cat ${IPCS_PROCFILES[$i]})
+ done >> $TS_OUTPUT
+ 
+-ts_log "check for difference between kernel and IPC"
+-ipcs_limits_check >> $TS_OUTPUT
+-
+ ts_log "maximalize kernel setting"
+ for i in $IPCS_IDX; do
+ 	echo ${IPCS_LIMITS[$i]} >> ${IPCS_PROCFILES[$i]}
+diff --git a/tests/ts/ipcs/limits2 b/tests/ts/ipcs/limits2
+index 63f834d..2eabfd2 100755
+--- a/tests/ts/ipcs/limits2
++++ b/tests/ts/ipcs/limits2
+@@ -22,6 +22,8 @@ TS_DESC="basic limits"
+ . $TS_TOPDIR/functions.sh
+ ts_init "$*"
+ 
++ts_check_prog "bc"
++
+ . $TS_SELF/functions.sh
+ 
+ # TODO https://github.com/karelzak/util-linux/issues/51
+diff --git a/tests/ts/libmount/context-utab b/tests/ts/libmount/context-utab
+index 859fc2b..b2bfefc 100755
+--- a/tests/ts/libmount/context-utab
++++ b/tests/ts/libmount/context-utab
+@@ -87,14 +87,20 @@ ts_finalize_subtest
+ 
+ 
+ if [ -x "/sbin/mkfs.btrfs" ]; then
++
+ 	$TS_CMD_WIPEFS -a  $DEVICE &> /dev/null
+ 	ts_log "Create filesystem [btrfs]"
+ 	/sbin/mkfs.btrfs -L "$LABEL" $DEVICE &> /dev/null
+ 	udevadm settle
+ 
+ 	mount -t btrfs $DEVICE $MOUNTPOINT &> /dev/null
+-	/sbin/btrfsctl -S sub $MOUNTPOINT &> /dev/null
+-	umount $MOUNTPOINT &> /dev/null
++
++	if [ -x "/sbin/btrfsctl" ]; then
++		/sbin/btrfsctl -S sub $MOUNTPOINT &> /dev/null
++	else
++		btrfs subvolume create $MOUNTPOINT/sub &> /dev/null
++	fi
++	umount $MOUNTPOINT #&> /dev/null
+ 
+ 	udevadm settle
+ 
+-- 
+2.7.4
+
diff --git a/SOURCES/0084-libmount-add-support-for-bind-ro.patch b/SOURCES/0084-libmount-add-support-for-bind-ro.patch
new file mode 100644
index 0000000..f86edbb
--- /dev/null
+++ b/SOURCES/0084-libmount-add-support-for-bind-ro.patch
@@ -0,0 +1,160 @@
+From 2218dc0d130bb72809e2d8b26a36402bf6293727 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Mon, 17 Aug 2015 11:54:26 +0200
+Subject: [PATCH 84/86] libmount: add support for "bind,ro"
+
+Now it's necessary to use two mount(8) calls to create a read-only
+mount:
+
+  mount /foo /bar -o bind
+  mount /bar -o remount,ro,bind
+
+This patch allows to specify "bind,ro" and the remount is done
+automatically by libmount by additional mount(2) syscall. It's not
+atomic of course.
+
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1281839
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ libmount/src/context_mount.c | 46 ++++++++++++++++++++++++++++++++++++++++++++
+ sys-utils/mount.8            | 37 ++++++++++++++++++-----------------
+ 2 files changed, 65 insertions(+), 18 deletions(-)
+
+diff --git a/libmount/src/context_mount.c b/libmount/src/context_mount.c
+index d6691eb..4df2646 100644
+--- a/libmount/src/context_mount.c
++++ b/libmount/src/context_mount.c
+@@ -62,6 +62,10 @@ static int mnt_context_append_additional_mount(struct libmnt_context *cxt,
+ 	return 0;
+ }
+ 
++/*
++ * add additional mount(2) syscall requests when necessary to set propagation flags
++ * after regular mount(2).
++ */
+ static int init_propagation(struct libmnt_context *cxt)
+ {
+ 	char *name;
+@@ -102,6 +106,41 @@ static int init_propagation(struct libmnt_context *cxt)
+ }
+ 
+ /*
++ * add additional mount(2) syscall request to implement "ro,bind", the first regular
++ * mount(2) is the "bind" operation, the second is "remount,ro,bind" call.
++ *
++ * Note that we don't remove "ro" from the first syscall (kernel silently
++ * ignores this flags for bind operation) -- maybe one day kernel will support
++ * read-only binds in one step and then all will be done by the firts mount(2) and the
++ * second remount will be noop...
++ */
++static int init_robind(struct libmnt_context *cxt)
++{
++	struct libmnt_addmount *ad;
++	int rc;
++
++	assert(cxt);
++	assert(cxt->mountflags & MS_BIND);
++	assert(cxt->mountflags & MS_RDONLY);
++	assert(!(cxt->mountflags & MS_REMOUNT));
++
++	DBG(CXT, mnt_debug_h(cxt, "mount: initialize additional ro,bind mount"));
++
++	ad = mnt_new_addmount();
++	if (!ad)
++		return -ENOMEM;
++
++	ad->mountflags = MS_REMOUNT | MS_BIND | MS_RDONLY;
++	if (cxt->mountflags & MS_REC)
++		ad->mountflags |= MS_REC;
++	rc = mnt_context_append_additional_mount(cxt, ad);
++	if (rc)
++		return rc;
++
++	return 0;
++}
++
++/*
+  * this has to be called after mnt_context_evaluate_permissions()
+  */
+ static int fix_optstr(struct libmnt_context *cxt)
+@@ -174,6 +213,13 @@ static int fix_optstr(struct libmnt_context *cxt)
+ 		if (rc)
+ 			return rc;
+ 	}
++	if ((cxt->mountflags & MS_BIND)
++	    && (cxt->mountflags & MS_RDONLY)
++	    && !(cxt->mountflags & MS_REMOUNT)) {
++		rc = init_robind(cxt);
++		if (rc)
++			return rc;
++	}
+ 
+ 	next = fs->fs_optstr;
+ 
+diff --git a/sys-utils/mount.8 b/sys-utils/mount.8
+index 3648870..49cb281 100644
+--- a/sys-utils/mount.8
++++ b/sys-utils/mount.8
+@@ -388,25 +388,25 @@ or shortoption
+ .\" available since Linux 2.4.11.
+ 
+ Note that the filesystem mount options will remain the same as those
+-on the original mount point, and cannot be changed by passing the -o
+-option along with --bind/--rbind. The mount options can be
+-changed by a separate remount command, for example:
++on the original mount point.
++
++.BR mount(8)
++since v2.27 (backported to RHEL7.3) allow to change the options by passing the
++.B \-o
++option along with
++.BR \-\-bind
++for example:
+ 
+ .RS
+ .br
+-.B mount --bind
+-.I olddir newdir
+-.br
+-.B mount -o remount,ro
+-.I newdir
++.B mount \-\-bind,ro foo foo
+ .RE
+ 
+-Note that behavior of the remount operation depends on the /etc/mtab file. The
+-first command stores the 'bind' flag to the /etc/mtab file and the second
+-command reads the flag from the file.  If you have a system without the
+-/etc/mtab file or if you explicitly define source and target for the remount
+-command (then mount(8) does not read /etc/mtab), then you have to use bind flag
+-(or option) for the remount command too. For example:
++This feature is not supported by Linux kernel and it is implemented in userspace
++by additional remount mount(2) syscall. This solution is not atomic.
++
++The alternative (classic) way to create a read-only bind mount is to use remount
++operation, for example:
+ 
+ .RS
+ .br
+@@ -417,14 +417,15 @@ command (then mount(8) does not read /etc/mtab), then you have to use bind flag
+ .I olddir newdir
+ .RE
+ 
+-Note that
+-.I remount,ro,bind
+-will create a read-only mountpoint (VFS entry), but the original filesystem suberblock
+-will be still writable, it means that the
++Note that read-only bind will create a read-only mountpoint (VFS entry), but the
++original filesystem superblock will still be writable, meaning that the
+ .I olddir
+ will be writable, but the
+ .I newdir
+ will be read-only.
++
++It's impossible to change mount options recursively
++(for example with \fB -o rbind,ro\fR).
+ .RE
+ 
+ .B The move operation.
+-- 
+2.7.4
+
diff --git a/SOURCES/0085-libblkid-store-only-canonical-devnames-to-the-cache.patch b/SOURCES/0085-libblkid-store-only-canonical-devnames-to-the-cache.patch
new file mode 100644
index 0000000..0ee3aa3
--- /dev/null
+++ b/SOURCES/0085-libblkid-store-only-canonical-devnames-to-the-cache.patch
@@ -0,0 +1,176 @@
+From 75b6c0e045abb7e07773b924237c562ab9920c60 Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Thu, 26 May 2016 12:02:12 +0200
+Subject: [PATCH 85/86] libblkid: store only canonical devnames to the cache
+
+Let's try to use symlink:
+
+ # ls -la /dev/block/8\:1
+ # lrwxrwxrwx 1 root root 7 May 25 16:42 /dev/block/8:1 -> ../sda1
+
+ # blkid /dev/block/8:1
+ /dev/block/8:3: LABEL="HOME" UUID="196972ad-3b13-4bba-ac54-4cb3f7b409a4" TYPE="ext4" PARTUUID="6073277f-87bc-43ff-bcfd-724c4484a63a"
+
+unfortunately the symlink is stored to the cache:
+
+ <device DEVNO="0x0803" TIME="1464253300.715279" LABEL="HOME" UUID="196972ad-3b13-4bba-ac54-4cb3f7b409a4" TYPE="ext4" PARTUUID="6073277f-87bc-43ff-bcfd-724c4484a63a">/dev/block/8:3</device>
+
+next time if you ask for LABEL=HOME the answer will be /dev/block/8:3
+rather than /dev/sda3.
+
+It seems better to canonicalize the paths we store to the cache.
+
+Unfortunately if you ask for /dev/block/8:3 then you probably expect
+that blkid_dev_devname() returns the same path. This patch introduces
+dev->bid_xname, this is the path used by application (and never stored
+in the cache).
+
+Upstream: http://github.com/karelzak/util-linux/commit/924c93d9df118338fd54cd73b4a45ccddc4ac103
+Addresses: http://bugzilla.redhat.com/show_bug.cgi?id=1007734
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ libblkid/src/blkidP.h  |  4 +++-
+ libblkid/src/dev.c     | 11 +++++++++--
+ libblkid/src/devname.c | 43 +++++++++++++++++++++++++++++++++++++------
+ 3 files changed, 49 insertions(+), 9 deletions(-)
+
+diff --git a/libblkid/src/blkidP.h b/libblkid/src/blkidP.h
+index 7de84b4..8183c12 100644
+--- a/libblkid/src/blkidP.h
++++ b/libblkid/src/blkidP.h
+@@ -44,7 +44,7 @@ struct blkid_struct_dev
+ 	struct list_head	bid_devs;	/* All devices in the cache */
+ 	struct list_head	bid_tags;	/* All tags for this device */
+ 	blkid_cache		bid_cache;	/* Dev belongs to this cache */
+-	char			*bid_name;	/* Device inode pathname */
++	char			*bid_name;	/* Device real pathn (as used in cache) */
+ 	char			*bid_type;	/* Preferred device TYPE */
+ 	int			bid_pri;	/* Device priority */
+ 	dev_t			bid_devno;	/* Device major/minor number */
+@@ -53,6 +53,8 @@ struct blkid_struct_dev
+ 	unsigned int		bid_flags;	/* Device status bitflags */
+ 	char			*bid_label;	/* Shortcut to device LABEL */
+ 	char			*bid_uuid;	/* Shortcut to binary UUID */
++
++	char			*bid_xname;	/* Device path as used by application (maybe symlink..) */
+ };
+ 
+ #define BLKID_BID_FL_VERIFIED	0x0001	/* Device data validated from disk */
+diff --git a/libblkid/src/dev.c b/libblkid/src/dev.c
+index a4b2aea..d2fd3f4 100644
+--- a/libblkid/src/dev.c
++++ b/libblkid/src/dev.c
+@@ -60,16 +60,23 @@ void blkid_free_dev(blkid_dev dev)
+ 					   bit_tags);
+ 		blkid_free_tag(tag);
+ 	}
++	free(dev->bid_xname);
+ 	free(dev->bid_name);
+ 	free(dev);
+ }
+ 
+ /*
+- * Given a blkid device, return its name
++ * Given a blkid device, return its name. The function returns the name
++ * previously used for blkid_get_dev(). This name does not have to be canonical
++ * (real path) name, but for example symlink.
+  */
+ const char *blkid_dev_devname(blkid_dev dev)
+ {
+-	return dev ? dev->bid_name : NULL;
++	if (!dev)
++		return NULL;
++	if (dev->bid_xname)
++		return dev->bid_xname;
++	return dev->bid_name;
+ }
+ 
+ #ifdef CONFIG_BLKID_DEBUG
+diff --git a/libblkid/src/devname.c b/libblkid/src/devname.c
+index 497deaf..55b9594 100644
+--- a/libblkid/src/devname.c
++++ b/libblkid/src/devname.c
+@@ -51,28 +51,55 @@ blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
+ {
+ 	blkid_dev dev = NULL, tmp;
+ 	struct list_head *p, *pnext;
++	char *cn = NULL;
+ 
+ 	if (!cache || !devname)
+ 		return NULL;
+ 
++	/* search by name */
+ 	list_for_each(p, &cache->bic_devs) {
+ 		tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
+ 		if (strcmp(tmp->bid_name, devname))
+ 			continue;
+-
+-		DBG(DEVNAME, blkid_debug("found devname %s in cache", tmp->bid_name));
+ 		dev = tmp;
+ 		break;
+ 	}
+ 
++	/* try canonicalize the name */
++	if (!dev && (cn = canonicalize_path(devname))) {
++		if (strcmp(cn, devname) != 0) {
++			DBG(DEVNAME, blkid_debug("search cannonical %s", cn));
++			list_for_each(p, &cache->bic_devs) {
++				tmp = list_entry(p, struct blkid_struct_dev, bid_devs);
++				if (strcmp(tmp->bid_name, cn))
++					continue;
++				dev = tmp;
++
++				/* update name returned by blkid_dev_devname() */
++				free(dev->bid_xname);
++				dev->bid_xname = strdup(devname);
++				break;
++			}
++		} else {
++			free(cn);
++			cn = NULL;
++		}
++	}
++
+ 	if (!dev && (flags & BLKID_DEV_CREATE)) {
+ 		if (access(devname, F_OK) < 0)
+-			return NULL;
++			goto done;
+ 		dev = blkid_new_dev();
+ 		if (!dev)
+-			return NULL;
++			goto done;
+ 		dev->bid_time = INT_MIN;
+-		dev->bid_name = strdup(devname);
++		if (cn) {
++			dev->bid_name = cn;
++			dev->bid_xname = strdup(devname);
++			cn = NULL;	/* see free() below */
++		} else
++			dev->bid_name = strdup(devname);
++
+ 		dev->bid_cache = cache;
+ 		list_add_tail(&dev->bid_devs, &cache->bic_devs);
+ 		cache->bic_flags |= BLKID_BIC_FL_CHANGED;
+@@ -81,7 +108,7 @@ blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
+ 	if (flags & BLKID_DEV_VERIFY) {
+ 		dev = blkid_verify(cache, dev);
+ 		if (!dev || !(dev->bid_flags & BLKID_BID_FL_VERIFIED))
+-			return dev;
++			goto done;
+ 		/*
+ 		 * If the device is verified, then search the blkid
+ 		 * cache for any entries that match on the type, uuid,
+@@ -112,6 +139,10 @@ blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags)
+ 				blkid_free_dev(dev2);
+ 		}
+ 	}
++done:
++	if (dev)
++		DBG(DEVNAME, blkid_debug("%s requested, found %s in cache", devname, dev->bid_name));
++	free(cn);
+ 	return dev;
+ }
+ 
+-- 
+2.7.4
+
diff --git a/SOURCES/0086-libblkid-avoid-recursion-in-EBR.patch b/SOURCES/0086-libblkid-avoid-recursion-in-EBR.patch
new file mode 100644
index 0000000..32b7f17
--- /dev/null
+++ b/SOURCES/0086-libblkid-avoid-recursion-in-EBR.patch
@@ -0,0 +1,102 @@
+From 8125a64ff9e98d09c659dbd5adbca521d63a268b Mon Sep 17 00:00:00 2001
+From: Karel Zak <kzak@redhat.com>
+Date: Thu, 7 Jul 2016 14:22:41 +0200
+Subject: [PATCH 86/86] libblkid: avoid recursion in EBR
+
+Upstream: http://github.com/karelzak/util-linux/commit/7164a1c34d18831ac61c6744ad14ce916d389b3f
+Upstream: http://github.com/karelzak/util-linux/commit/50d1594c2e6142a3b51d2143c74027480df082e0
+Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1349536
+Signed-off-by: Karel Zak <kzak@redhat.com>
+---
+ libblkid/src/partitions/dos.c        | 21 +++++++++++++++++++--
+ libblkid/src/partitions/partitions.c | 14 ++++++++++++++
+ libblkid/src/partitions/partitions.h |  2 ++
+ 3 files changed, 35 insertions(+), 2 deletions(-)
+
+diff --git a/libblkid/src/partitions/dos.c b/libblkid/src/partitions/dos.c
+index 2d4a537..563fe9a 100644
+--- a/libblkid/src/partitions/dos.c
++++ b/libblkid/src/partitions/dos.c
+@@ -48,6 +48,12 @@ static int parse_dos_extended(blkid_probe pr, blkid_parttable tab,
+ 	int ct_nodata = 0;	/* count ext.partitions without data partitions */
+ 	int i;
+ 
++	DBG(LOWPROBE, blkid_debug("parse EBR [start=%d, size=%d]", ex_start/ssf, ex_size/ssf));
++	if (ex_start == 0) {
++		DBG(LOWPROBE, blkid_debug("Bad offset in primary extended partition -- ignore"));
++		return 0;
++	}
++
+ 	while (1) {
+ 		struct dos_partition *p, *p0;
+ 		uint32_t start, size;
+@@ -100,6 +106,13 @@ static int parse_dos_extended(blkid_probe pr, blkid_parttable tab,
+ 					continue;
+ 			}
+ 
++			/* Avoid recursive non-empty links, see ct_nodata counter */
++			if (blkid_partlist_get_partition_by_start(ls, abs_start)) {
++				DBG(LOWPROBE, blkid_debug("#%d: EBR duplicate data partition [abs start=%u] -- ignore",
++							i + 1, abs_start));
++				continue;
++			}
++
+ 			par = blkid_partlist_add_partition(ls, tab, abs_start, size);
+ 			if (!par)
+ 				return -ENOMEM;
+@@ -116,8 +129,12 @@ static int parse_dos_extended(blkid_probe pr, blkid_parttable tab,
+ 			start = dos_partition_start(p) * ssf;
+ 			size = dos_partition_size(p) * ssf;
+ 
+-			if (size && is_extended(p))
+-				break;
++			if (size && is_extended(p)) {
++				if (start == 0)
++					DBG(LOWPROBE, blkid_debug("#%d: EBR link offset is zero -- ignore", i + 1));
++				else
++					break;
++			}
+ 		}
+ 		if (i == 4)
+ 			goto leave;
+diff --git a/libblkid/src/partitions/partitions.c b/libblkid/src/partitions/partitions.c
+index 752fc95..9d846ff 100644
+--- a/libblkid/src/partitions/partitions.c
++++ b/libblkid/src/partitions/partitions.c
+@@ -928,6 +928,20 @@ blkid_partition blkid_partlist_get_partition(blkid_partlist ls, int n)
+ 	return &ls->parts[n];
+ }
+ 
++blkid_partition blkid_partlist_get_partition_by_start(blkid_partlist ls, uint64_t start)
++{
++	int i, nparts;
++	blkid_partition par;
++
++	nparts = blkid_partlist_numof_partitions(ls);
++	for (i = 0; i < nparts; i++) {
++		par = blkid_partlist_get_partition(ls, i);
++		if ((uint64_t) blkid_partition_get_start(par) == start)
++			return par;
++	}
++	return NULL;
++}
++
+ /**
+  * blkid_partlist_devno_to_partition:
+  * @ls: partitions list
+diff --git a/libblkid/src/partitions/partitions.h b/libblkid/src/partitions/partitions.h
+index 61763bf..4e99e2a 100644
+--- a/libblkid/src/partitions/partitions.h
++++ b/libblkid/src/partitions/partitions.h
+@@ -20,6 +20,8 @@ extern int blkid_partlist_increment_partno(blkid_partlist ls);
+ 
+ extern blkid_partition blkid_partlist_get_parent(blkid_partlist ls);
+ 
++extern blkid_partition blkid_partlist_get_partition_by_start(blkid_partlist ls, uint64_t start);
++
+ extern int blkid_partitions_do_subprobe(blkid_probe pr,
+ 			blkid_partition parent, const struct blkid_idinfo *id);
+ 
+-- 
+2.7.4
+
diff --git a/SOURCES/2.28-lslogins-1317953.patch b/SOURCES/2.28-lslogins-1317953.patch
deleted file mode 100644
index db7589a..0000000
--- a/SOURCES/2.28-lslogins-1317953.patch
+++ /dev/null
@@ -1,210 +0,0 @@
-diff -up util-linux-2.23.2/include/pathnames.h.kzak util-linux-2.23.2/include/pathnames.h
---- util-linux-2.23.2/include/pathnames.h.kzak	2016-03-16 17:17:02.980243390 +0100
-+++ util-linux-2.23.2/include/pathnames.h	2016-03-16 17:17:37.129015363 +0100
-@@ -36,6 +36,7 @@
- #endif
- #define	_PATH_MOTDFILE		"/etc/motd"
- #define	_PATH_NOLOGIN		"/etc/nologin"
-+#define _PATH_VAR_NOLOGIN       "/var/run/nologin"
- 
- #define _PATH_LOGIN		"/bin/login"
- #define _PATH_INITTAB		"/etc/inittab"
-diff -up util-linux-2.23.2/login-utils/lslogins.1.kzak util-linux-2.23.2/login-utils/lslogins.1
---- util-linux-2.23.2/login-utils/lslogins.1.kzak	2016-03-16 17:17:02.970243457 +0100
-+++ util-linux-2.23.2/login-utils/lslogins.1	2016-03-16 17:17:37.129015363 +0100
-@@ -5,7 +5,10 @@
- lslogins \- display information about known users in the system
- .SH SYNOPSIS
- .B lslogins
--[\fIoptions\fR] [\fB-s\fR|\fB-u\fR[=\fIUID\fR]] [\fB-g \fIgroups\fR] [\fB-l \fIlogins\fR]
-+[options]
-+.RB [ \-s | \-u [ =\fIUID ]]
-+.RB [ \-g " \fIgroups\fR]"
-+.RB [ \-l " \fIlogins\fR]"
- .SH DESCRIPTION
- .PP
- Examine the wtmp and btmp logs, /etc/shadow (if necessary) and /etc/passwd
-@@ -17,7 +20,7 @@ Mandatory arguments to long options are
- .TP
- \fB\-a\fR, \fB\-\-acc\-expiration\fR
- Display data about the date of last password change and the account expiration
--date (see \fBshadow\fR(5) for more info).  (Requires root priviliges.)
-+date (see \fBshadow\fR(5) for more info).  (Requires root privileges.)
- .TP
- \fB\-\-btmp\-file \fIpath\fP
- Alternate path for btmp.
-@@ -31,7 +34,7 @@ Output data in the format of NAME=VALUE.
- \fB\-f\fR, \fB\-\-failed\fR
- Display data about the users' last failed login attempts.
- .TP
--\fB\-G\fR, \fB\-\-groups\-info\fR
-+\fB\-G\fR, \fB\-\-supp\-groups\fR
- Show information about groups.
- .TP
- \fB\-g\fR, \fB\-\-groups\fR=\fIgroups\fR
-@@ -48,9 +51,6 @@ Display data containing information abou
- Only show data of users with a login specified in \fIlogins\fR (user names or user
- IDS).  More than one login may be specified; the list has to be comma-separated.
- .TP
--\fB\-m\fR, \fB\-\-supp\-groups\fR
--Show supplementary groups.
--.TP
- \fB\-n\fR, \fB\-\-newline\fR
- Display each piece of information on a separate line.
- .TP
-@@ -71,21 +71,21 @@ Display information related to login by
- \fB\-r\fR, \fB\-\-raw\fR
- Raw output (no columnation).
- .TP
--\fB\-s\fR, \fB\-\-system\-accs\fR[=\fIthreshold\fR]
-+\fB\-s\fR, \fB\-\-system\-accs\fR
- Show system accounts.  These are by default all accounts with a UID below 1000
--(non-inclusive), with the exception of either nobody or nfsnobody (UID 65534).  The UID
--threshold can also be specified explicitly (necessary for some distributions that
--allocate UIDs starting from 100, 500 - or an entirely different value - rather than 1000).
-+(non-inclusive), with the exception of either nobody or nfsnobody (UID 65534).
-+This hardcoded default maybe overwritten by parameters SYS_UID_MIN and SYS_UID_MAX in
-+the file /etc/login.defs.
- .TP
--\fB\-\-time-format\fR \fItype\fP
-+\fB\-\-time\-format\fR \fItype\fP
- Display dates in short, full or iso format.  The default is short, this time
- format is designed to be space efficient and human readable.
- .TP
--\fB\-u\fR, \fB\-\-user\-accs\fR[=\fIthreshold\fR]
-+\fB\-u\fR, \fB\-\-user\-accs\fR
- Show user accounts.  These are by default all accounts with UID above 1000
--(inclusive), with the exception of either nobody or nfsnobody (UID 65534).  The UID
--threshold can also be specified explicitly (necessary for some distributions that
--allocate UIDs starting from 100, 500 - or an entirely different value - rather than 1000).
-+(inclusive), with the exception of either nobody or nfsnobody (UID 65534).
-+This hardcoded default maybe overwritten by parameters UID_MIN and UID_MAX in
-+the file /etc/login.defs.
- .TP
- \fB\-V\fR, \fB\-\-version\fR
- Display version information and exit.
-diff -up util-linux-2.23.2/login-utils/lslogins.c.kzak util-linux-2.23.2/login-utils/lslogins.c
---- util-linux-2.23.2/login-utils/lslogins.c.kzak	2016-03-16 17:17:02.970243457 +0100
-+++ util-linux-2.23.2/login-utils/lslogins.c	2016-03-16 17:23:47.191484521 +0100
-@@ -396,7 +396,7 @@ again:
- 			x = snprintf(p, len, "%s,", grp->gr_name);
- 		}
- 
--		if (x < 0 || (size_t) x + 1 > len) {
-+		if (x < 0 || (size_t) x >= len) {
- 			size_t cur = p - res;
- 
- 			maxlen *= 2;
-@@ -496,21 +496,24 @@ static int parse_btmp(struct lslogins_co
- static int get_sgroups(gid_t **list, size_t *len, struct passwd *pwd)
- {
- 	size_t n = 0;
-+	int ngroups = 0;
- 
- 	*len = 0;
- 	*list = NULL;
- 
- 	/* first let's get a supp. group count */
--	getgrouplist(pwd->pw_name, pwd->pw_gid, *list, (int *) len);
--	if (!*len)
-+	getgrouplist(pwd->pw_name, pwd->pw_gid, *list, &ngroups);
-+	if (!ngroups)
- 		return -1;
- 
--	*list = xcalloc(1, *len * sizeof(gid_t));
-+	*list = xcalloc(1, ngroups * sizeof(gid_t));
- 
- 	/* now for the actual list of GIDs */
--	if (-1 == getgrouplist(pwd->pw_name, pwd->pw_gid, *list, (int *) len))
-+	if (-1 == getgrouplist(pwd->pw_name, pwd->pw_gid, *list, &ngroups))
- 		return -1;
- 
-+	*len = (size_t) ngroups;
-+
- 	/* getgroups also returns the user's primary GID - dispose of it */
- 	while (n < *len) {
- 		if ((*list)[n] == pwd->pw_gid)
-@@ -852,7 +855,7 @@ static int get_user(struct lslogins_cont
- 		    const char *username)
- {
- 	*user = get_user_info(ctl, username);
--	if (!*user && errno)
-+	if (!*user)
- 		if (IS_REAL_ERRNO(errno))
- 			return -1;
- 	return 0;
-@@ -1193,16 +1196,18 @@ static void __attribute__((__noreturn__)
- 	fputs(USAGE_HEADER, out);
- 	fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
- 
-+	fputs(USAGE_SEPARATOR, out);
-+	fputs(_("Display information about known users in the system.\n"), out);
-+
- 	fputs(USAGE_OPTIONS, out);
- 	fputs(_(" -a, --acc-expiration     display info about passwords expiration\n"), out);
- 	fputs(_(" -c, --colon-separate     display data in a format similar to /etc/passwd\n"), out);
- 	fputs(_(" -e, --export             display in an export-able output format\n"), out);
- 	fputs(_(" -f, --failed             display data about the users' last failed logins\n"), out);
--	fputs(_(" -G, --groups-info        display information about groups\n"), out);
-+	fputs(_(" -G, --supp-groups        display information about groups\n"), out);
- 	fputs(_(" -g, --groups=<groups>    display users belonging to a group in <groups>\n"), out);
- 	fputs(_(" -L, --last               show info about the users' last login sessions\n"), out);
- 	fputs(_(" -l, --logins=<logins>    display only users from <logins>\n"), out);
--	fputs(_(" -m, --supp-groups        display supplementary groups as well\n"), out);
- 	fputs(_(" -n, --newline            display each piece of information on a new line\n"), out);
- 	fputs(_("     --noheadings         don't print headings\n"), out);
- 	fputs(_("     --notruncate         don't truncate output\n"), out);
-@@ -1226,7 +1231,7 @@ static void __attribute__((__noreturn__)
- 		fprintf(out, " %14s  %s\n", coldescs[i].name,
- 				_(coldescs[i].help));
- 
--	fprintf(out, _("\nFor more details see lslogins(1).\n"));
-+	fprintf(out, USAGE_MAN_TAIL("lslogins(1)"));
- 
- 	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
- }
-@@ -1241,8 +1246,7 @@ int main(int argc, char *argv[])
- 
- 	/* long only options. */
- 	enum {
--		OPT_VER = CHAR_MAX + 1,
--		OPT_WTMP,
-+		OPT_WTMP = CHAR_MAX + 1,
- 		OPT_BTMP,
- 		OPT_NOTRUNC,
- 		OPT_NOHEAD,
-@@ -1300,7 +1304,7 @@ int main(int argc, char *argv[])
- 	add_column(columns, ncolumns++, COL_UID);
- 	add_column(columns, ncolumns++, COL_USER);
- 
--	while ((c = getopt_long(argc, argv, "acfGg:hLl:no:prsuVxzZ",
-+	while ((c = getopt_long(argc, argv, "acefGg:hLl:no:prsuVzZ",
- 				longopts, NULL)) != -1) {
- 
- 		err_exclusive_options(c, longopts, excl, excl_st);
-@@ -1397,6 +1401,7 @@ int main(int argc, char *argv[])
- 			{
- 				size_t i;
- 
-+				ctl->time_mode = TIME_INVALID;
- 				for (i = 0; i < ARRAY_SIZE(timefmts); i++) {
- 					if (strcmp(timefmts[i].name, optarg) == 0) {
- 						ctl->time_mode = timefmts[i].val;
-@@ -1404,7 +1409,7 @@ int main(int argc, char *argv[])
- 					}
- 				}
- 				if (ctl->time_mode == TIME_INVALID)
--					usage(stderr);
-+					errx(EXIT_FAILURE, _("unknown time format: %s"), optarg);
- 			}
- 			break;
- 		case 'V':
-@@ -1433,7 +1438,7 @@ int main(int argc, char *argv[])
- 		logins = argv[optind];
- 		outmode = OUT_PRETTY;
- 	} else if (argc != optind)
--		usage(stderr);
-+		errx(EXIT_FAILURE, _("Only one user may be specified. Use -l for multiple users."));
- 
- 	scols_init_debug(0);
- 
diff --git a/SOURCES/2.28-lslogins-rebase.patch b/SOURCES/2.28-lslogins-rebase.patch
new file mode 100644
index 0000000..fd0cdb4
--- /dev/null
+++ b/SOURCES/2.28-lslogins-rebase.patch
@@ -0,0 +1,460 @@
+diff -up util-linux-2.23.2/include/pathnames.h.kzak util-linux-2.23.2/include/pathnames.h
+--- util-linux-2.23.2/include/pathnames.h.kzak	2016-03-16 15:17:42.648298525 +0100
++++ util-linux-2.23.2/include/pathnames.h	2016-03-16 15:18:13.769055345 +0100
+@@ -36,6 +36,7 @@
+ #endif
+ #define	_PATH_MOTDFILE		"/etc/motd"
+ #define	_PATH_NOLOGIN		"/etc/nologin"
++#define _PATH_VAR_NOLOGIN       "/var/run/nologin"
+ 
+ #define _PATH_LOGIN		"/bin/login"
+ #define _PATH_INITTAB		"/etc/inittab"
+diff -up util-linux-2.23.2/login-utils/lslogins.1.kzak util-linux-2.23.2/login-utils/lslogins.1
+--- util-linux-2.23.2/login-utils/lslogins.1.kzak	2016-03-16 15:17:42.639298595 +0100
++++ util-linux-2.23.2/login-utils/lslogins.1	2016-03-16 15:18:13.769055345 +0100
+@@ -5,7 +5,10 @@
+ lslogins \- display information about known users in the system
+ .SH SYNOPSIS
+ .B lslogins
+-[\fIoptions\fR] [\fB-s\fR|\fB-u\fR[=\fIUID\fR]] [\fB-g \fIgroups\fR] [\fB-l \fIlogins\fR]
++[options]
++.RB [ \-s | \-u [ =\fIUID ]]
++.RB [ \-g " \fIgroups\fR]"
++.RB [ \-l " \fIlogins\fR]"
+ .SH DESCRIPTION
+ .PP
+ Examine the wtmp and btmp logs, /etc/shadow (if necessary) and /etc/passwd
+@@ -17,7 +20,7 @@ Mandatory arguments to long options are
+ .TP
+ \fB\-a\fR, \fB\-\-acc\-expiration\fR
+ Display data about the date of last password change and the account expiration
+-date (see \fBshadow\fR(5) for more info).  (Requires root priviliges.)
++date (see \fBshadow\fR(5) for more info).  (Requires root privileges.)
+ .TP
+ \fB\-\-btmp\-file \fIpath\fP
+ Alternate path for btmp.
+@@ -31,7 +34,7 @@ Output data in the format of NAME=VALUE.
+ \fB\-f\fR, \fB\-\-failed\fR
+ Display data about the users' last failed login attempts.
+ .TP
+-\fB\-G\fR, \fB\-\-groups\-info\fR
++\fB\-G\fR, \fB\-\-supp\-groups\fR
+ Show information about groups.
+ .TP
+ \fB\-g\fR, \fB\-\-groups\fR=\fIgroups\fR
+@@ -48,9 +51,6 @@ Display data containing information abou
+ Only show data of users with a login specified in \fIlogins\fR (user names or user
+ IDS).  More than one login may be specified; the list has to be comma-separated.
+ .TP
+-\fB\-m\fR, \fB\-\-supp\-groups\fR
+-Show supplementary groups.
+-.TP
+ \fB\-n\fR, \fB\-\-newline\fR
+ Display each piece of information on a separate line.
+ .TP
+@@ -71,21 +71,21 @@ Display information related to login by
+ \fB\-r\fR, \fB\-\-raw\fR
+ Raw output (no columnation).
+ .TP
+-\fB\-s\fR, \fB\-\-system\-accs\fR[=\fIthreshold\fR]
++\fB\-s\fR, \fB\-\-system\-accs\fR
+ Show system accounts.  These are by default all accounts with a UID below 1000
+-(non-inclusive), with the exception of either nobody or nfsnobody (UID 65534).  The UID
+-threshold can also be specified explicitly (necessary for some distributions that
+-allocate UIDs starting from 100, 500 - or an entirely different value - rather than 1000).
++(non-inclusive), with the exception of either nobody or nfsnobody (UID 65534).
++This hardcoded default maybe overwritten by parameters SYS_UID_MIN and SYS_UID_MAX in
++the file /etc/login.defs.
+ .TP
+-\fB\-\-time-format\fR \fItype\fP
++\fB\-\-time\-format\fR \fItype\fP
+ Display dates in short, full or iso format.  The default is short, this time
+ format is designed to be space efficient and human readable.
+ .TP
+-\fB\-u\fR, \fB\-\-user\-accs\fR[=\fIthreshold\fR]
++\fB\-u\fR, \fB\-\-user\-accs\fR
+ Show user accounts.  These are by default all accounts with UID above 1000
+-(inclusive), with the exception of either nobody or nfsnobody (UID 65534).  The UID
+-threshold can also be specified explicitly (necessary for some distributions that
+-allocate UIDs starting from 100, 500 - or an entirely different value - rather than 1000).
++(inclusive), with the exception of either nobody or nfsnobody (UID 65534).
++This hardcoded default maybe overwritten by parameters UID_MIN and UID_MAX in
++the file /etc/login.defs.
+ .TP
+ \fB\-V\fR, \fB\-\-version\fR
+ Display version information and exit.
+diff -up util-linux-2.23.2/login-utils/lslogins.c.kzak util-linux-2.23.2/login-utils/lslogins.c
+--- util-linux-2.23.2/login-utils/lslogins.c.kzak	2016-03-16 15:17:42.639298595 +0100
++++ util-linux-2.23.2/login-utils/lslogins.c	2016-03-16 15:22:49.845899268 +0100
+@@ -144,6 +144,7 @@ enum {
+ 	TIME_SHORT,
+ 	TIME_FULL,
+ 	TIME_ISO,
++	TIME_ISO_SHORT,
+ };
+ 
+ /*
+@@ -350,6 +351,9 @@ static char *make_time(int mode, time_t
+ 	case TIME_ISO:
+ 		strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", &tm);
+ 		break;
++	case TIME_ISO_SHORT:
++		strftime(buf, sizeof(buf), "%Y-%m-%d", &tm);
++		break;
+ 	default:
+ 		errx(EXIT_FAILURE, _("unsupported time type"));
+ 	}
+@@ -396,7 +400,7 @@ again:
+ 			x = snprintf(p, len, "%s,", grp->gr_name);
+ 		}
+ 
+-		if (x < 0 || (size_t) x + 1 > len) {
++		if (x < 0 || (size_t) x >= len) {
+ 			size_t cur = p - res;
+ 
+ 			maxlen *= 2;
+@@ -496,21 +500,24 @@ static int parse_btmp(struct lslogins_co
+ static int get_sgroups(gid_t **list, size_t *len, struct passwd *pwd)
+ {
+ 	size_t n = 0;
++	int ngroups = 0;
+ 
+ 	*len = 0;
+ 	*list = NULL;
+ 
+ 	/* first let's get a supp. group count */
+-	getgrouplist(pwd->pw_name, pwd->pw_gid, *list, (int *) len);
+-	if (!*len)
++	getgrouplist(pwd->pw_name, pwd->pw_gid, *list, &ngroups);
++	if (!ngroups)
+ 		return -1;
+ 
+-	*list = xcalloc(1, *len * sizeof(gid_t));
++	*list = xcalloc(1, ngroups * sizeof(gid_t));
+ 
+ 	/* now for the actual list of GIDs */
+-	if (-1 == getgrouplist(pwd->pw_name, pwd->pw_gid, *list, (int *) len))
++	if (-1 == getgrouplist(pwd->pw_name, pwd->pw_gid, *list, &ngroups))
+ 		return -1;
+ 
++	*len = (size_t) ngroups;
++
+ 	/* getgroups also returns the user's primary GID - dispose of it */
+ 	while (n < *len) {
+ 		if ((*list)[n] == pwd->pw_gid)
+@@ -520,6 +527,7 @@ static int get_sgroups(gid_t **list, siz
+ 
+ 	if (*len)
+ 		(*list)[n] = (*list)[--(*len)];
++
+ 	return 0;
+ }
+ 
+@@ -685,8 +693,8 @@ static struct lslogins_user *get_user_in
+ 			if (strstr(pwd->pw_shell, "nologin"))
+ 				user->nologin = 1;
+ 			else if (pwd->pw_uid)
+-				user->nologin = access("/etc/nologin", F_OK) == 0 ||
+-						access("/var/run/nologin", F_OK) == 0;
++				user->nologin = access(_PATH_NOLOGIN, F_OK) == 0 ||
++						access(_PATH_VAR_NOLOGIN, F_OK) == 0;
+ 			break;
+ 		case COL_PWD_WARN:
+ 			if (shadow && shadow->sp_warn >= 0)
+@@ -694,7 +702,8 @@ static struct lslogins_user *get_user_in
+ 			break;
+ 		case COL_PWD_EXPIR:
+ 			if (shadow && shadow->sp_expire >= 0)
+-				user->pwd_expire = make_time(TIME_SHORT,
++				user->pwd_expire = make_time(ctl->time_mode == TIME_ISO ?
++						TIME_ISO_SHORT : ctl->time_mode,
+ 						shadow->sp_expire * 86400);
+ 			break;
+ 		case COL_PWD_CTIME:
+@@ -702,7 +711,8 @@ static struct lslogins_user *get_user_in
+ 			 * (especially in non-GMT timezones) would only serve
+ 			 * to confuse */
+ 			if (shadow)
+-				user->pwd_ctime = make_time(TIME_SHORT,
++				user->pwd_ctime = make_time(ctl->time_mode == TIME_ISO ?
++						TIME_ISO_SHORT : ctl->time_mode,
+ 						shadow->sp_lstchg * 86400);
+ 			break;
+ 		case COL_PWD_CTIME_MIN:
+@@ -852,7 +862,7 @@ static int get_user(struct lslogins_cont
+ 		    const char *username)
+ {
+ 	*user = get_user_info(ctl, username);
+-	if (!*user && errno)
++	if (!*user)
+ 		if (IS_REAL_ERRNO(errno))
+ 			return -1;
+ 	return 0;
+@@ -887,33 +897,33 @@ static int create_usertree(struct lslogi
+ 
+ static struct libscols_table *setup_table(struct lslogins_control *ctl)
+ {
+-	struct libscols_table *tb = scols_new_table();
++	struct libscols_table *table = scols_new_table();
+ 	int n = 0;
+ 
+-	if (!tb)
++	if (!table)
+ 		errx(EXIT_FAILURE, _("failed to initialize output table"));
+ 	if (ctl->noheadings)
+-		scols_table_enable_noheadings(tb, 1);
++		scols_table_enable_noheadings(table, 1);
+ 
+ 	switch(outmode) {
+ 	case OUT_COLON:
+-		scols_table_enable_raw(tb, 1);
+-		scols_table_set_column_separator(tb, ":");
++		scols_table_enable_raw(table, 1);
++		scols_table_set_column_separator(table, ":");
+ 		break;
+ 	case OUT_NEWLINE:
+-		scols_table_set_column_separator(tb, "\n");
++		scols_table_set_column_separator(table, "\n");
+ 		/* fallthrough */
+ 	case OUT_EXPORT:
+-		scols_table_enable_export(tb, 1);
++		scols_table_enable_export(table, 1);
+ 		break;
+ 	case OUT_NUL:
+-		scols_table_set_line_separator(tb, "\0");
++		scols_table_set_line_separator(table, "\0");
+ 		/* fallthrough */
+ 	case OUT_RAW:
+-		scols_table_enable_raw(tb, 1);
++		scols_table_enable_raw(table, 1);
+ 		break;
+ 	case OUT_PRETTY:
+-		scols_table_enable_noheadings(tb, 1);
++		scols_table_enable_noheadings(table, 1);
+ 	default:
+ 		break;
+ 	}
+@@ -924,7 +934,7 @@ static struct libscols_table *setup_tabl
+ 		if (ctl->notrunc)
+ 			flags &= ~SCOLS_FL_TRUNC;
+ 
+-		if (!scols_table_new_column(tb,
++		if (!scols_table_new_column(table,
+ 				coldescs[columns[n]].name,
+ 				coldescs[columns[n]].whint,
+ 				flags))
+@@ -932,9 +942,9 @@ static struct libscols_table *setup_tabl
+ 		++n;
+ 	}
+ 
+-	return tb;
++	return table;
+ fail:
+-	scols_unref_table(tb);
++	scols_unref_table(table);
+ 	return NULL;
+ }
+ 
+@@ -1050,10 +1060,10 @@ static void fill_table(const void *u, co
+ 	return;
+ }
+ #ifdef HAVE_LIBSYSTEMD
+-static void print_journal_tail(const char *journal_path, uid_t uid, size_t len)
++static void print_journal_tail(const char *journal_path, uid_t uid, size_t len, int time_mode)
+ {
+ 	sd_journal *j;
+-	char *match, *buf;
++	char *match, *timestamp;
+ 	uint64_t x;
+ 	time_t t;
+ 	const char *identifier, *pid, *message;
+@@ -1064,7 +1074,6 @@ static void print_journal_tail(const cha
+ 	else
+ 		sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
+ 
+-	buf = xmalloc(sizeof(char) * 16);
+ 	xasprintf(&match, "_UID=%d", uid);
+ 
+ 	sd_journal_add_match(j, match, 0);
+@@ -1074,37 +1083,35 @@ static void print_journal_tail(const cha
+ 	do {
+ 		if (0 > sd_journal_get_data(j, "SYSLOG_IDENTIFIER",
+ 				(const void **) &identifier, &identifier_len))
+-			return;
++			goto done;
+ 		if (0 > sd_journal_get_data(j, "_PID",
+ 				(const void **) &pid, &pid_len))
+-			return;
++			goto done;
+ 		if (0 > sd_journal_get_data(j, "MESSAGE",
+ 				(const void **) &message, &message_len))
+-			return;
++			goto done;
+ 
+ 		sd_journal_get_realtime_usec(j, &x);
+ 		t = x / 1000000;
+-		strftime(buf, 16, "%b %d %H:%M:%S", localtime(&t));
+-
+-		fprintf(stdout, "%s", buf);
+-
++		timestamp = make_time(time_mode, t);
++		/* Get rid of journal entry field identifiers */
+ 		identifier = strchr(identifier, '=') + 1;
+-		pid = strchr(pid, '=') + 1		;
++		pid = strchr(pid, '=') + 1;
+ 		message = strchr(message, '=') + 1;
+ 
+-		fprintf(stdout, " %s", identifier);
+-		fprintf(stdout, "[%s]:", pid);
+-		fprintf(stdout, "%s\n", message);
++		fprintf(stdout, "%s %s[%s]: %s\n", timestamp, identifier, pid,
++			message);
++		free(timestamp);
+ 	} while (sd_journal_next(j));
+ 
+-	free(buf);
++done:
+ 	free(match);
+ 	sd_journal_flush_matches(j);
+ 	sd_journal_close(j);
+ }
+ #endif
+ 
+-static int print_pretty(struct libscols_table *tb)
++static int print_pretty(struct libscols_table *table)
+ {
+ 	struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
+ 	struct libscols_column *col;
+@@ -1113,8 +1120,8 @@ static int print_pretty(struct libscols_
+ 	const char *hstr, *dstr;
+ 	int n = 0;
+ 
+-	ln = scols_table_get_line(tb, 0);
+-	while (!scols_table_next_column(tb, itr, &col)) {
++	ln = scols_table_get_line(table, 0);
++	while (!scols_table_next_column(table, itr, &col)) {
+ 
+ 		data = scols_line_get_cell(ln, n);
+ 
+@@ -1142,7 +1149,7 @@ static int print_user_table(struct lslog
+ 		print_pretty(tb);
+ #ifdef HAVE_LIBSYSTEMD
+ 		fprintf(stdout, _("\nLast logs:\n"));
+-		print_journal_tail(ctl->journal_path, ctl->uid, 3);
++		print_journal_tail(ctl->journal_path, ctl->uid, 3, ctl->time_mode);
+ 		fputc('\n', stdout);
+ #endif
+ 	} else
+@@ -1175,16 +1182,25 @@ static void free_user(void *f)
+ 	free(u);
+ }
+ 
+-struct lslogins_timefmt {
+-	const char *name;
+-	int val;
+-};
++static int parse_time_mode(const char *optarg)
++{
++	struct lslogins_timefmt {
++		const char *name;
++		const int val;
++	};
++	static const struct lslogins_timefmt timefmts[] = {
++		{"iso", TIME_ISO},
++		{"full", TIME_FULL},
++		{"short", TIME_SHORT},
++	};
++	size_t i;
+ 
+-static struct lslogins_timefmt timefmts[] = {
+-	{ "short", TIME_SHORT },
+-	{ "full", TIME_FULL },
+-	{ "iso", TIME_ISO },
+-};
++	for (i = 0; i < ARRAY_SIZE(timefmts); i++) {
++		if (strcmp(timefmts[i].name, optarg) == 0)
++			return timefmts[i].val;
++	}
++	errx(EXIT_FAILURE, _("unknown time format: %s"), optarg);
++}
+ 
+ static void __attribute__((__noreturn__)) usage(FILE *out)
+ {
+@@ -1193,16 +1209,18 @@ static void __attribute__((__noreturn__)
+ 	fputs(USAGE_HEADER, out);
+ 	fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
+ 
++	fputs(USAGE_SEPARATOR, out);
++	fputs(_("Display information about known users in the system.\n"), out);
++
+ 	fputs(USAGE_OPTIONS, out);
+ 	fputs(_(" -a, --acc-expiration     display info about passwords expiration\n"), out);
+ 	fputs(_(" -c, --colon-separate     display data in a format similar to /etc/passwd\n"), out);
+ 	fputs(_(" -e, --export             display in an export-able output format\n"), out);
+ 	fputs(_(" -f, --failed             display data about the users' last failed logins\n"), out);
+-	fputs(_(" -G, --groups-info        display information about groups\n"), out);
++	fputs(_(" -G, --supp-groups        display information about groups\n"), out);
+ 	fputs(_(" -g, --groups=<groups>    display users belonging to a group in <groups>\n"), out);
+ 	fputs(_(" -L, --last               show info about the users' last login sessions\n"), out);
+ 	fputs(_(" -l, --logins=<logins>    display only users from <logins>\n"), out);
+-	fputs(_(" -m, --supp-groups        display supplementary groups as well\n"), out);
+ 	fputs(_(" -n, --newline            display each piece of information on a new line\n"), out);
+ 	fputs(_("     --noheadings         don't print headings\n"), out);
+ 	fputs(_("     --notruncate         don't truncate output\n"), out);
+@@ -1226,7 +1244,7 @@ static void __attribute__((__noreturn__)
+ 		fprintf(out, " %14s  %s\n", coldescs[i].name,
+ 				_(coldescs[i].help));
+ 
+-	fprintf(out, _("\nFor more details see lslogins(1).\n"));
++	fprintf(out, USAGE_MAN_TAIL("lslogins(1)"));
+ 
+ 	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+ }
+@@ -1241,8 +1259,7 @@ int main(int argc, char *argv[])
+ 
+ 	/* long only options. */
+ 	enum {
+-		OPT_VER = CHAR_MAX + 1,
+-		OPT_WTMP,
++		OPT_WTMP = CHAR_MAX + 1,
+ 		OPT_BTMP,
+ 		OPT_NOTRUNC,
+ 		OPT_NOHEAD,
+@@ -1300,7 +1317,7 @@ int main(int argc, char *argv[])
+ 	add_column(columns, ncolumns++, COL_UID);
+ 	add_column(columns, ncolumns++, COL_USER);
+ 
+-	while ((c = getopt_long(argc, argv, "acfGg:hLl:no:prsuVxzZ",
++	while ((c = getopt_long(argc, argv, "acefGg:hLl:no:prsuVzZ",
+ 				longopts, NULL)) != -1) {
+ 
+ 		err_exclusive_options(c, longopts, excl, excl_st);
+@@ -1394,18 +1411,7 @@ int main(int argc, char *argv[])
+ 			ctl->noheadings = 1;
+ 			break;
+ 		case OPT_TIME_FMT:
+-			{
+-				size_t i;
+-
+-				for (i = 0; i < ARRAY_SIZE(timefmts); i++) {
+-					if (strcmp(timefmts[i].name, optarg) == 0) {
+-						ctl->time_mode = timefmts[i].val;
+-						break;
+-					}
+-				}
+-				if (ctl->time_mode == TIME_INVALID)
+-					usage(stderr);
+-			}
++			ctl->time_mode = parse_time_mode(optarg);
+ 			break;
+ 		case 'V':
+ 			printf(UTIL_LINUX_VERSION);
+@@ -1433,7 +1439,7 @@ int main(int argc, char *argv[])
+ 		logins = argv[optind];
+ 		outmode = OUT_PRETTY;
+ 	} else if (argc != optind)
+-		usage(stderr);
++		errx(EXIT_FAILURE, _("Only one user may be specified. Use -l for multiple users."));
+ 
+ 	scols_init_debug(0);
+ 
diff --git a/SPECS/util-linux.spec b/SPECS/util-linux.spec
index dd22223..1299dea 100644
--- a/SPECS/util-linux.spec
+++ b/SPECS/util-linux.spec
@@ -2,7 +2,7 @@
 Summary: A collection of basic system utilities
 Name: util-linux
 Version: 2.23.2
-Release: 26%{?dist}.3
+Release: 33%{?dist}
 License: GPLv2 and GPLv2+ and LGPLv2+ and BSD with advertising and Public Domain
 Group: System Environment/Base
 URL: http://en.wikipedia.org/wiki/Util-linux
@@ -199,14 +199,67 @@ Patch56: 2.25-libblkid-return-codes.patch
 Patch57: 2.26-libblkid-fat.patch
 
 #
-# RHEL 7.2.Z
+# RHEL7.3
 #
-# 1302332 - [libblkid] Failed to get offset of the xfs_external_log signature
+# 1301091 - [libblkid] Failed to get offset of the xfs_external_log signature
 Patch58: 2.27-libblkid-xfs-log.patch
-# 1317953 - lslogins crash when executed with buggy username
-Patch59: 2.28-lslogins-1317953.patch
-# 1350777 - fdisk 'f' subcommand updates partition ranges wrongly
-Patch60: 0060-fdisk-backport-DOS-logical-partitions-chain-reorder.patch
+# 1291554 - lslogins crash when executed with buggy username
+Patch59: 2.28-lslogins-rebase.patch
+# 1092520 - util-linux - PIE and RELRO check
+Patch60: 0060-build-sys-add-CFLAGS-and-LDFLAGS-for-daemons-and-sha.patch
+# 1248003 - mount only parses <param>=<value> lines from fstab fs_spec field available from blkid block device
+Patch61: 0061-libmount-be-more-restrictive-about-valid-tag-names.patch
+# 1271850 - mount -a doesn't catch a typo in /etc/fstab and a typo in /etc/fstab can make a system not reboot properly
+Patch62: 0062-mount-umount-swapon-fsck-lsblk-findmnt-ignore-malfor.patch
+# 1290689 - util-linux: /bin/login does not retry getpwnam_r with larger buffers, leading to login failure
+Patch63: 0063-login-mount-fix-__SC_GETPW_R_SIZE_MAX-usage.patch
+# 1296366 - Bash completion for more(1) handles file names with spaces incorrectly
+Patch64: 0064-bash-completion-use-n-as-IFS-when-ask-for-filenames.patch
+# 1296521 - RHEL7: update audit event in hwclock
+Patch65: 0065-hwclock-change-audit-message.patch
+# 1304426 - [rfe] /bin/su should be improved to reduce stack use
+Patch66: 0066-su-clean-up-groups-initialization.patch
+# 1326615 - util-linux/lscpu: Fix model and model name on Power Systems
+Patch67: 0067-lscpu-Fix-model-and-model-name-on-Power-Systems.patch
+# 1326615 - util-linux/lscpu: Fix model and model name on Power Systems
+Patch68: 0068-lscpu-use-cpu-and-revision-tag-if-available.patch
+# 1335671 - extra quotes around UUID confuses findfs in RHEL (but not in Fedora)
+Patch69: 0069-findfs-add-ability-to-work-with-PART-UUID-LABEL-too.patch
+# 1335671 - extra quotes around UUID confuses findfs in RHEL (but not in Fedora)
+Patch70: 0070-libblkid-fix-memory-leak-in-blkid_parse_tag_string.patch
+# 587393 - [RFE] Make sure util-linux is ready for writable overlays
+Patch71: 0071-findmnt-don-t-rely-on-st_dev-for-target.patch
+# 587393 - [RFE] Make sure util-linux is ready for writable overlays
+Patch72: 0072-libmount-cleanup-fs-root-detection-code.patch
+# 587393 - [RFE] Make sure util-linux is ready for writable overlays
+Patch73: 0073-libmount-mark-overlay-as-pseudo-FS.patch
+# 1344222 - logger port option in help is misleading
+Patch74: 0074-logger-be-more-precise-about-port-description.patch
+# 1344482 - util-linux fails valid_pmbr() size checks if device is > 2.14TB, Device label type: dos instead of gpt
+Patch75: 0075-libfdisk-gpt-be-more-careful-with-64bit-constants.patch
+# 1332084 - [RFE] Inclusion of lsns command in util-linux Package
+Patch76: 0076-lsns-backport-new-command.patch
+# 1153770 - backport lsipc
+Patch77: 0077-lib-strutils-make-strmode-more-generic.patch
+# 1153770 - backport lsipc
+Patch78: 0078-lsipc-backport-new-command.patch
+# 1327886 - Backport blkdiscard's "-z" flag to RHEL
+Patch79: 0079-blkdiscard-backport-zeroout-support.patch
+# 1029385 - lack of non-ascii support
+Patch80: 0080-sulogin-and-agetty-virtual-consoles-support-xvc-and-.patch
+# 1298384 - RFE: add SCHED_DEADLINE support to chrt
+Patch81: 0081-chrt-backport-DEADLINE-scheduler-support.patch
+# 1304246 - fdisk 'f' subcommand updates partition ranges wrongly
+Patch82: 0082-fdisk-backport-DOS-logical-partitions-chain-reorder.patch
+# 1153770 - backport lsipc
+Patch83: 0083-tests-cleanup-tests.patch
+# 1281839 - [RFE]Bind mounts should be handled gracefully by the operating system
+Patch84: 0084-libmount-add-support-for-bind-ro.patch
+# 1007734 - blkid shows devices as /dev/block/$MAJOR:$MINOR
+Patch85: 0085-libblkid-store-only-canonical-devnames-to-the-cache.patch
+# 1349536 - Extended partition loop in MBR partition table leads to DOS
+Patch86: 0086-libblkid-avoid-recursion-in-EBR.patch
+
 
 %description
 The util-linux package contains a large variety of low-level system
@@ -331,6 +384,8 @@ unset LINGUAS || :
 export CFLAGS="-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 $RPM_OPT_FLAGS"
 export SUID_CFLAGS="-fpie"
 export SUID_LDFLAGS="-pie -Wl,-z,relro -Wl,-z,now"
+export DAEMON_CFLAGS="$SUID_CFLAGS"
+export DAEMON_LDFLAGS="$SUID_LDFLAGS"
 %configure \
 	--with-systemdsystemunitdir=%{_unitdir} \
 	--disable-silent-rules \
@@ -595,6 +650,8 @@ fi
 %{_bindir}/ipcs
 %{_bindir}/isosize
 %{_bindir}/kill
+%{_bindir}/lsipc
+%{_bindir}/lsns
 %{_bindir}/logger
 %{_bindir}/look
 %{_bindir}/lsblk
@@ -649,6 +706,7 @@ fi
 %{_mandir}/man1/look.1*
 %{_mandir}/man1/lscpu.1*
 %{_mandir}/man1/lslogins.1*
+%{_mandir}/man1/lsipc.1*
 %{_mandir}/man1/mcookie.1*
 %{_mandir}/man1/more.1*
 %{_mandir}/man1/mountpoint.1*
@@ -694,7 +752,8 @@ fi
 %{_mandir}/man8/ldattach.8*
 %{_mandir}/man8/losetup.8*
 %{_mandir}/man8/lsblk.8*
-%{_mandir}/man8/lslocks.8.gz
+%{_mandir}/man8/lslocks.8*
+%{_mandir}/man8/lsns.8*
 %{_mandir}/man8/mkfs.8*
 %{_mandir}/man8/mkfs.cramfs.8*
 %{_mandir}/man8/mkfs.minix.8*
@@ -926,14 +985,47 @@ fi
 %{_libdir}/pkgconfig/uuid.pc
 
 %changelog
-* Thu Jun 30 2016 Karel Zak <kzak@redhat.com> 2.23.2-26.el7_2.3
-- fix #1350777 - fdisk 'f' subcommand updates partition ranges wrongly
-
-* Wed Mar 16 2016 Karel Zak <kzak@redhat.com> 2.23.2-26.el7_2.2
-- fix #1317953 - lslogins crash when executed with buggy username
-
-* Thu Jan 28 2016 Karel Zak <kzak@redhat.com> 2.23.2-26.el7_2.1
-- fix #1302332 - [libblkid] Failed to get offset of the xfs_external_log signature
+* Tue Jul 12 2016 Karel Zak <kzak@redhat.com> 2.23.2-33
+- improve patch for #1007734 (libblkid realpaths)
+
+* Tue Jul 12 2016 Karel Zak <kzak@redhat.com> 2.23.2-32
+- improve patch for chrt(1) deadline support #1298384
+- fix #1007734 - blkid shows devices as /dev/block/$MAJOR:$MINOR
+- fix #1349536 - Extended partition loop in MBR partition table leads to DOS
+
+* Mon Jul 04 2016 Karel Zak <kzak@redhat.com> 2.23.2-31
+- improve spec file for #1092520
+
+* Fri Jul 01 2016 Karel Zak <kzak@redhat.com> 2.23.2-30
+- improve patch for chrt(1) deadline support #1298384
+- improve regression tests
+
+* Thu Jun 30 2016 Karel Zak <kzak@redhat.com> 2.23.2-29
+- fix #1029385 - lack of non-ascii support
+- fix #1092520 - util-linux - PIE and RELRO check
+- fix #1153770 - backport lsipc
+- fix #1248003 - mount only parses <param>=<value> lines from fstab fs_spec field available from blkid block device
+- fix #1271850 - mount -a doesn't catch a typo in /etc/fstab and a typo in /etc/fstab can make a system not reboot properly
+- fix #1281839 - [RFE]Bind mounts should be handled gracefully by the operating system
+- fix #1290689 - util-linux: /bin/login does not retry getpwnam_r with larger buffers, leading to login failure
+- fix #1296366 - Bash completion for more(1) handles file names with spaces incorrectly
+- fix #1296521 - RHEL7: update audit event in hwclock
+- fix #1298384 - RFE: add SCHED_DEADLINE support to chrt
+- fix #1304246 - fdisk 'f' subcommand updates partition ranges wrongly
+- fix #1304426 - [rfe] /bin/su should be improved to reduce stack use
+- fix #1326615 - util-linux/lscpu: Fix model and model name on Power Systems
+- fix #1327886 - Backport blkdiscard's "-z" flag to RHEL
+- fix #1332084 - [RFE] Inclusion of lsns command in util-linux Package
+- fix #1335671 - extra quotes around UUID confuses findfs in RHEL (but not in Fedora)
+- fix #1344222 - logger port option in help is misleading
+- fix #1344482 - util-linux fails valid_pmbr() size checks if device is > 2.14TB, Device label type: dos instead of gpt
+- fix #587393 - [RFE] Make sure util-linux is ready for writable overlays
+
+* Wed Mar 16 2016 Karel Zak <kzak@redhat.com> 2.23.2-28
+- fix #1291554 - lslogins crash when executed with buggy username
+
+* Thu Jan 28 2016 Karel Zak <kzak@redhat.com> 2.23.2-27
+- fix #1301091 - [libblkid] Failed to get offset of the xfs_external_log signature
 
 * Fri Aug 21 2015 Karel Zak <kzak@redhat.com> 2.23.2-26
 - fix #1182831 - blkid incorrectly detects boot sec + MBR as FAT