2.34-6: mountinfo issue
This commit is contained in:
		
							parent
							
								
									83243ee93c
								
							
						
					
					
						commit
						707076abd5
					
				
							
								
								
									
										383
									
								
								libmount-improve-mountinfo-reliability.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										383
									
								
								libmount-improve-mountinfo-reliability.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,383 @@ | ||||
| From 32fe4f1dd8fbc104bd848e24de613122947f095a Mon Sep 17 00:00:00 2001 | ||||
| From: Karel Zak <kzak@redhat.com> | ||||
| Date: Wed, 28 Aug 2019 15:47:16 +0200 | ||||
| Subject: [PATCH] libmount: improve mountinfo reliability | ||||
| MIME-Version: 1.0 | ||||
| Content-Type: text/plain; charset=UTF-8 | ||||
| Content-Transfer-Encoding: 8bit | ||||
| 
 | ||||
| The standard way how we read mount table is not reliable because | ||||
| during the read() syscalls the table may be modified by some another | ||||
| process. The changes in the table is possible to detect by poll() | ||||
| event, and in this case it seems better to lseek to the begin of the file | ||||
| and read it again. It's expensive, but better than races... | ||||
| 
 | ||||
| This patch does not modify mountinfo parser, but it reads all file to | ||||
| memory (by read()+poll()) and than it creates memory stream | ||||
| from the buffer and use it rather than a regular file stream. | ||||
| 
 | ||||
| It means the parser is still possible to use for normal files | ||||
| (e.g. fstab) as well as for mountinfo and it's also portable to | ||||
| systems where for some reason is no fmemopen(). | ||||
| 
 | ||||
| Addresses: https://github.com/systemd/systemd/issues/10872 | ||||
| Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1751447 | ||||
| Upstream: http://github.com/karelzak/util-linux/commit/e4925f591c1bfb83719418b56b952830d15b77eb | ||||
| Upstream: http://github.com/karelzak/util-linux/commit/ee551c909f95437fd9fcd162f398c069d0ce9720 | ||||
| Reported-by: Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | ||||
| Signed-off-by: Karel Zak <kzak@redhat.com> | ||||
| ---
 | ||||
| diff -up util-linux-2.34/configure.ac.kzak util-linux-2.34/configure.ac
 | ||||
| --- util-linux-2.34/configure.ac.kzak	2019-06-14 12:34:02.892209368 +0200
 | ||||
| +++ util-linux-2.34/configure.ac	2019-09-13 09:19:54.155419473 +0200
 | ||||
| @@ -466,6 +466,7 @@ AC_CHECK_FUNCS([ \
 | ||||
|  	err \ | ||||
|  	errx \ | ||||
|  	explicit_bzero \ | ||||
| +	fmemopen \
 | ||||
|  	fsync \ | ||||
|  	utimensat \ | ||||
|  	getdomainname \ | ||||
| diff -up util-linux-2.34/libmount/src/mountP.h.kzak util-linux-2.34/libmount/src/mountP.h
 | ||||
| --- util-linux-2.34/libmount/src/mountP.h.kzak	2019-05-09 14:15:40.637552762 +0200
 | ||||
| +++ util-linux-2.34/libmount/src/mountP.h	2019-09-13 09:19:54.155419473 +0200
 | ||||
| @@ -97,6 +97,7 @@ extern int mnt_valid_tagname(const char
 | ||||
|  extern int append_string(char **a, const char *b); | ||||
|   | ||||
|  extern const char *mnt_statfs_get_fstype(struct statfs *vfs); | ||||
| +extern int is_procfs_fd(int fd);
 | ||||
|  extern int is_file_empty(const char *name); | ||||
|   | ||||
|  extern int mnt_is_readonly(const char *path) | ||||
| @@ -121,6 +122,7 @@ extern void mnt_free_filesystems(char **
 | ||||
|   | ||||
|  extern char *mnt_get_kernel_cmdline_option(const char *name); | ||||
|  extern int mnt_stat_mountpoint(const char *target, struct stat *st); | ||||
| +extern FILE *mnt_get_procfs_memstream(int fd, char **membuf);
 | ||||
|   | ||||
|  /* tab.c */ | ||||
|  extern int is_mountinfo(struct libmnt_table *tb); | ||||
| diff -up util-linux-2.34/libmount/src/tab_parse.c.kzak util-linux-2.34/libmount/src/tab_parse.c
 | ||||
| --- util-linux-2.34/libmount/src/tab_parse.c.kzak	2019-05-09 14:15:40.638552755 +0200
 | ||||
| +++ util-linux-2.34/libmount/src/tab_parse.c	2019-09-13 09:19:54.156419468 +0200
 | ||||
| @@ -698,15 +698,7 @@ static int kernel_fs_postparse(struct li
 | ||||
|  	return rc; | ||||
|  } | ||||
|   | ||||
| -/**
 | ||||
| - * mnt_table_parse_stream:
 | ||||
| - * @tb: tab pointer
 | ||||
| - * @f: file stream
 | ||||
| - * @filename: filename used for debug and error messages
 | ||||
| - *
 | ||||
| - * Returns: 0 on success, negative number in case of error.
 | ||||
| - */
 | ||||
| -int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filename)
 | ||||
| +static int __table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filename)
 | ||||
|  { | ||||
|  	int rc = -1; | ||||
|  	int flags = 0; | ||||
| @@ -781,6 +773,40 @@ err:
 | ||||
|  } | ||||
|   | ||||
|  /** | ||||
| + * mnt_table_parse_stream:
 | ||||
| + * @tb: tab pointer
 | ||||
| + * @f: file stream
 | ||||
| + * @filename: filename used for debug and error messages
 | ||||
| + *
 | ||||
| + * Returns: 0 on success, negative number in case of error.
 | ||||
| + */
 | ||||
| +int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filename)
 | ||||
| +{
 | ||||
| +	int fd, rc;
 | ||||
| +	FILE *memf = NULL;
 | ||||
| +	char *membuf = NULL;
 | ||||
| +
 | ||||
| +	/*
 | ||||
| +	 * For /proc/#/{mountinfo,mount} we read all file to memory and use it
 | ||||
| +	 * as memory stream. For more details see mnt_read_procfs_file().
 | ||||
| +	 */
 | ||||
| +	if ((fd = fileno(f)) >= 0
 | ||||
| +	    && (tb->fmt == MNT_FMT_GUESS ||
 | ||||
| +		tb->fmt == MNT_FMT_MOUNTINFO ||
 | ||||
| +		tb->fmt == MNT_FMT_MTAB)
 | ||||
| +	    && is_procfs_fd(fd)
 | ||||
| +	    && (memf = mnt_get_procfs_memstream(fd, &membuf))) {
 | ||||
| +
 | ||||
| +		rc = __table_parse_stream(tb, memf, filename);
 | ||||
| +		fclose(memf);
 | ||||
| +		free(membuf);
 | ||||
| +	} else
 | ||||
| +		rc = __table_parse_stream(tb, f, filename);
 | ||||
| +
 | ||||
| +	return rc;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/**
 | ||||
|   * mnt_table_parse_file: | ||||
|   * @tb: tab pointer | ||||
|   * @filename: file | ||||
| @@ -795,18 +821,49 @@ err:
 | ||||
|  int mnt_table_parse_file(struct libmnt_table *tb, const char *filename) | ||||
|  { | ||||
|  	FILE *f; | ||||
| -	int rc;
 | ||||
| +	int rc, fd = -1;
 | ||||
|   | ||||
|  	if (!filename || !tb) | ||||
|  		return -EINVAL; | ||||
|   | ||||
| -	f = fopen(filename, "r" UL_CLOEXECSTR);
 | ||||
| +	/*
 | ||||
| +	 * Try to use read()+poll() to realiably read all
 | ||||
| +	 * /proc/#/{mount,mountinfo} file to memory
 | ||||
| +	 */
 | ||||
| +	if (tb->fmt != MNT_FMT_SWAPS
 | ||||
| +	    && strncmp(filename, "/proc/", 6) == 0) {
 | ||||
| +
 | ||||
| +		FILE *memf;
 | ||||
| +		char *membuf = NULL;
 | ||||
| +
 | ||||
| +		fd = open(filename, O_RDONLY|O_CLOEXEC);
 | ||||
| +		if (fd < 0) {
 | ||||
| +			rc = -errno;
 | ||||
| +			goto done;
 | ||||
| +		}
 | ||||
| +		memf = mnt_get_procfs_memstream(fd, &membuf);
 | ||||
| +		if (memf) {
 | ||||
| +			rc = __table_parse_stream(tb, memf, filename);
 | ||||
| +
 | ||||
| +			fclose(memf);
 | ||||
| +			free(membuf);
 | ||||
| +			close(fd);
 | ||||
| +			goto done;
 | ||||
| +		}
 | ||||
| +		/* else fallback to fopen/fdopen() */
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	if (fd >= 0)
 | ||||
| +		f = fdopen(fd, "r" UL_CLOEXECSTR);
 | ||||
| +	else
 | ||||
| +		f = fopen(filename, "r" UL_CLOEXECSTR);
 | ||||
| +
 | ||||
|  	if (f) { | ||||
| -		rc = mnt_table_parse_stream(tb, f, filename);
 | ||||
| +		rc = __table_parse_stream(tb, f, filename);
 | ||||
|  		fclose(f); | ||||
|  	} else | ||||
|  		rc = -errno; | ||||
| -
 | ||||
| +done:
 | ||||
|  	DBG(TAB, ul_debugobj(tb, "parsing done [filename=%s, rc=%d]", filename, rc)); | ||||
|  	return rc; | ||||
|  } | ||||
| @@ -863,7 +920,7 @@ static int __mnt_table_parse_dir(struct
 | ||||
|   | ||||
|  		f = fopen_at(dd, d->d_name, O_RDONLY|O_CLOEXEC, "r" UL_CLOEXECSTR); | ||||
|  		if (f) { | ||||
| -			mnt_table_parse_stream(tb, f, d->d_name);
 | ||||
| +			__table_parse_stream(tb, f, d->d_name);
 | ||||
|  			fclose(f); | ||||
|  		} | ||||
|  	} | ||||
| @@ -904,7 +961,7 @@ static int __mnt_table_parse_dir(struct
 | ||||
|  		f = fopen_at(dirfd(dir), d->d_name, | ||||
|  				O_RDONLY|O_CLOEXEC, "r" UL_CLOEXECSTR); | ||||
|  		if (f) { | ||||
| -			mnt_table_parse_stream(tb, f, d->d_name);
 | ||||
| +			__table_parse_stream(tb, f, d->d_name);
 | ||||
|  			fclose(f); | ||||
|  		} | ||||
|  	} | ||||
| diff -up util-linux-2.34/libmount/src/utils.c.kzak util-linux-2.34/libmount/src/utils.c
 | ||||
| --- util-linux-2.34/libmount/src/utils.c.kzak	2019-06-13 13:26:04.946525187 +0200
 | ||||
| +++ util-linux-2.34/libmount/src/utils.c	2019-09-13 09:19:54.157419462 +0200
 | ||||
| @@ -19,6 +19,7 @@
 | ||||
|  #include <fcntl.h> | ||||
|  #include <pwd.h> | ||||
|  #include <grp.h> | ||||
| +#include <poll.h>
 | ||||
|  #include <blkid.h> | ||||
|   | ||||
|  #include "strutils.h" | ||||
| @@ -424,6 +425,12 @@ const char *mnt_statfs_get_fstype(struct
 | ||||
|  	return NULL; | ||||
|  } | ||||
|   | ||||
| +int is_procfs_fd(int fd)
 | ||||
| +{
 | ||||
| +	struct statfs sfs;
 | ||||
| +
 | ||||
| +	return fstatfs(fd, &sfs) == 0 && sfs.f_type == STATFS_PROC_MAGIC;
 | ||||
| +}
 | ||||
|   | ||||
|  /** | ||||
|   * mnt_match_fstype: | ||||
| @@ -1140,8 +1147,158 @@ done:
 | ||||
|  	return 1; | ||||
|  } | ||||
|   | ||||
| +#if defined(HAVE_FMEMOPEN) || defined(TEST_PROGRAM)
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * This function tries to minimize possible races when we read
 | ||||
| + * /proc/#/{mountinfo,mount} files.
 | ||||
| + *
 | ||||
| + * The idea is to minimize number of read()s and check by poll() that during
 | ||||
| + * the read the mount table has not been modified. If yes, than re-read it
 | ||||
| + * (with some limitations to avoid never ending loop).
 | ||||
| + *
 | ||||
| + * Returns: <0 error, 0 success, 1 too many attempts
 | ||||
| + */
 | ||||
| +static int read_procfs_file(int fd, char **buf, size_t *bufsiz)
 | ||||
| +{
 | ||||
| +	size_t bufmax = 0;
 | ||||
| +	int rc = 0, tries = 0, ninters = 0;
 | ||||
| +	char *bufptr = NULL;;
 | ||||
| +
 | ||||
| +	assert(buf);
 | ||||
| +	assert(bufsiz);
 | ||||
| +
 | ||||
| +	*bufsiz = 0;
 | ||||
| +	*buf = NULL;
 | ||||
| +
 | ||||
| +	do {
 | ||||
| +		ssize_t ret;
 | ||||
| +
 | ||||
| +		if (!bufptr || bufmax == *bufsiz) {
 | ||||
| +			char *tmp;
 | ||||
| +
 | ||||
| +			bufmax = bufmax ? bufmax * 2 : (16 * 1024);
 | ||||
| +			tmp = realloc(*buf, bufmax);
 | ||||
| +			if (!tmp)
 | ||||
| +				break;
 | ||||
| +			*buf = tmp;
 | ||||
| +			bufptr = tmp + *bufsiz;
 | ||||
| +		}
 | ||||
| +
 | ||||
| +		errno = 0;
 | ||||
| +		ret = read(fd, bufptr, bufmax - *bufsiz);
 | ||||
| +
 | ||||
| +		if (ret < 0) {
 | ||||
| +			/* error */
 | ||||
| +			if ((errno == EAGAIN || errno == EINTR) && (ninters++ < 5)) {
 | ||||
| +				xusleep(200000);
 | ||||
| +				continue;
 | ||||
| +			}
 | ||||
| +			break;
 | ||||
| +
 | ||||
| +		} else if (ret > 0) {
 | ||||
| +			/* success -- verify no event during read */
 | ||||
| +			struct pollfd fds[] = {
 | ||||
| +				{ .fd = fd, .events = POLLPRI }
 | ||||
| +			};
 | ||||
| +
 | ||||
| +			rc = poll(fds, 1, 0);
 | ||||
| +			if (rc < 0)
 | ||||
| +				break;		/* poll() error */
 | ||||
| +			if (rc > 0) {
 | ||||
| +				/* event -- read all again */
 | ||||
| +				if (lseek(fd, 0, SEEK_SET) != 0)
 | ||||
| +					break;
 | ||||
| +				*bufsiz = 0;
 | ||||
| +				bufptr = *buf;
 | ||||
| +				tries++;
 | ||||
| +
 | ||||
| +				if (tries > 10)
 | ||||
| +					/* busy system? -- wait */
 | ||||
| +					xusleep(10000);
 | ||||
| +				continue;
 | ||||
| +			}
 | ||||
| +
 | ||||
| +			/* successful read() without active poll() */
 | ||||
| +			(*bufsiz) += (size_t) ret;
 | ||||
| +			bufptr += ret;
 | ||||
| +			tries = ninters = 0;
 | ||||
| +		} else {
 | ||||
| +			/* end-of-file */
 | ||||
| +			goto success;
 | ||||
| +		}
 | ||||
| +	} while (tries <= 100);
 | ||||
| +
 | ||||
| +	rc = errno ? -errno : 1;
 | ||||
| +	free(*buf);
 | ||||
| +	return rc;
 | ||||
| +
 | ||||
| +success:
 | ||||
| +	return 0;
 | ||||
| +}
 | ||||
| +
 | ||||
| +/*
 | ||||
| + * Create FILE stream for data from read_procfs_file()
 | ||||
| + */
 | ||||
| +FILE *mnt_get_procfs_memstream(int fd, char **membuf)
 | ||||
| +{
 | ||||
| +	FILE *memf;
 | ||||
| +	size_t sz = 0;
 | ||||
| +	off_t cur;
 | ||||
| +
 | ||||
| +	/* in case of error, rewind to the original position */
 | ||||
| +	cur = lseek(fd, 0, SEEK_CUR);
 | ||||
| +
 | ||||
| +	if (read_procfs_file(fd, membuf, &sz) == 0
 | ||||
| +	    && sz > 0
 | ||||
| +	    && (memf = fmemopen(*membuf, sz, "r")))
 | ||||
| +		return memf;
 | ||||
| +
 | ||||
| +	/* error */
 | ||||
| +	lseek(fd, cur, SEEK_SET);
 | ||||
| +	return NULL;
 | ||||
| +}
 | ||||
| +#else
 | ||||
| +FILE *mnt_get_procfs_memstream(int fd __attribute((__unused__)),
 | ||||
| +		               char **membuf __attribute((__unused__)))
 | ||||
| +{
 | ||||
| +	return NULL;
 | ||||
| +}
 | ||||
| +#endif /* HAVE_FMEMOPEN */
 | ||||
| +
 | ||||
|   | ||||
|  #ifdef TEST_PROGRAM | ||||
| +static int test_proc_read(struct libmnt_test *ts, int argc, char *argv[])
 | ||||
| +{
 | ||||
| +	char *buf = NULL;
 | ||||
| +	char *filename = argv[1];
 | ||||
| +	size_t bufsiz = 0;
 | ||||
| +	int rc = 0, fd = open(filename, O_RDONLY);
 | ||||
| +
 | ||||
| +	if (fd <= 0) {
 | ||||
| +		warn("%s: cannot open", filename);
 | ||||
| +		return -errno;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	rc = read_procfs_file(fd, &buf, &bufsiz);
 | ||||
| +	close(fd);
 | ||||
| +
 | ||||
| +	switch (rc) {
 | ||||
| +	case 0:
 | ||||
| +		fwrite(buf, 1, bufsiz, stdout);
 | ||||
| +		free(buf);
 | ||||
| +		break;
 | ||||
| +	case 1:
 | ||||
| +		warnx("too many attempts");
 | ||||
| +		break;
 | ||||
| +	default:
 | ||||
| +		warn("%s: cannot read", filename);
 | ||||
| +		break;
 | ||||
| +	}
 | ||||
| +
 | ||||
| +	return rc;
 | ||||
| +}
 | ||||
| +
 | ||||
|  static int test_match_fstype(struct libmnt_test *ts, int argc, char *argv[]) | ||||
|  { | ||||
|  	char *type = argv[1]; | ||||
| @@ -1323,6 +1480,7 @@ int main(int argc, char *argv[])
 | ||||
|  	{ "--guess-root",    test_guess_root,      "[<maj:min>]" }, | ||||
|  	{ "--mkdir",         test_mkdir,           "<path>" }, | ||||
|  	{ "--statfs-type",   test_statfs_type,     "<path>" }, | ||||
| +	{ "--read-procfs",   test_proc_read,       "<path>" },
 | ||||
|   | ||||
|  	{ NULL } | ||||
|  	}; | ||||
| @ -2,7 +2,7 @@ | ||||
| Summary: A collection of basic system utilities | ||||
| Name: util-linux | ||||
| Version: 2.34 | ||||
| Release: 5%{?dist} | ||||
| Release: 6%{?dist} | ||||
| License: GPLv2 and GPLv2+ and LGPLv2+ and BSD with advertising and Public Domain | ||||
| URL: http://en.wikipedia.org/wiki/Util-linux | ||||
| 
 | ||||
| @ -41,6 +41,10 @@ BuildRequires: gcc | ||||
| %ifarch ppc64le | ||||
| BuildRequires: librtas-devel | ||||
| %endif | ||||
| BuildRequires: autoconf | ||||
| BuildRequires: automake | ||||
| BuildRequires: libtool | ||||
| BuildRequires: bison | ||||
| 
 | ||||
| ### Sources | ||||
| Source0: ftp://ftp.kernel.org/pub/linux/utils/util-linux/v%{upstream_major}/util-linux-%{upstream_version}.tar.xz | ||||
| @ -104,6 +108,8 @@ Patch0: 2.28-login-lastlog-create.patch | ||||
| 
 | ||||
| # 1751290 - regression: lsblk not showing PKNAME in f31+ | ||||
| Patch1: lsblk-force-to-print-PKNAME-for-partition.patch | ||||
| # https://github.com/systemd/systemd/issues/10872 | ||||
| Patch2: libmount-improve-mountinfo-reliability.patch | ||||
| 
 | ||||
| %description | ||||
| The util-linux package contains a large variety of low-level system | ||||
| @ -273,6 +279,12 @@ chfn and chsh utilities with dependence on libuser | ||||
| %build | ||||
| unset LINGUAS || : | ||||
| 
 | ||||
| # unfortunately, we did changes to build-system | ||||
| ./autogen.sh | ||||
| 
 | ||||
| # we modify .po files by RHEL patches | ||||
| rm -f po/stamp* | ||||
| 
 | ||||
| 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" | ||||
| @ -922,6 +934,9 @@ fi | ||||
| %{_libdir}/python*/site-packages/libmount/ | ||||
| 
 | ||||
| %changelog | ||||
| * Fri Sep 13 2019 Karel Zak <kzak@redhat.com> - 2.34-6 | ||||
| - fix https://github.com/systemd/systemd/issues/10872 | ||||
| 
 | ||||
| * Thu Sep 12 2019 Karel Zak <kzak@redhat.com> - 2.34-5 | ||||
| - fix #1751290 - regression: lsblk not showing PKNAME in f31+ | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user