[lxc-devel] [lxc/master] add functions to mmap() file to \0-terminated str

brauner on Github lxc-bot at linuxcontainers.org
Sat Mar 26 09:15:15 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 383 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160326/585d1790/attachment.bin>
-------------- next part --------------
From d9593e28c2775a7bd49899275071ec7b22015a12 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at mailbox.org>
Date: Sat, 26 Mar 2016 10:12:48 +0100
Subject: [PATCH] add functions to mmap() file to \0-terminated str

Tests included.

Signed-off-by: Christian Brauner <christian.brauner at mailbox.org>
---
 src/lxc/utils.c                         |  45 ++++++-
 src/lxc/utils.h                         |  31 +++++
 src/tests/lxc-test-utils-lxc_mmap_str.c | 217 ++++++++++++++++++++++++++++++++
 3 files changed, 292 insertions(+), 1 deletion(-)
 create mode 100644 src/tests/lxc-test-utils-lxc_mmap_str.c

diff --git a/src/lxc/utils.c b/src/lxc/utils.c
index 5f15d94..b7fb59e 100644
--- a/src/lxc/utils.c
+++ b/src/lxc/utils.c
@@ -69,7 +69,7 @@ struct prctl_mm_map {
         uint64_t   *auxv;
         uint32_t   auxv_size;
         uint32_t   exe_fd;
-};              
+};
 #endif
 
 #ifndef O_PATH
@@ -1811,3 +1811,46 @@ int lxc_count_file_lines(const char *fn)
 	fclose(f);
 	return n;
 }
+
+extern void *lxc_mmap_str(void *addr, size_t len, int flags, int fildes,
+			  off_t off, bool *wrote_zero)
+{
+	long pagesize = sysconf(_SC_PAGESIZE);
+	if (pagesize <= 0)
+		return NULL;
+
+	/* mmap()ed memory is only \0-terminated when len is a multiple of
+	 * pagesize. If it is not we write a terminating \0-byte to the
+	 * mapping. */
+	if ((len > 0) && ((len % pagesize) == 0)) {
+		if (pwrite(fildes, "", 1, len + 1) <= 0)
+			return NULL;
+		*wrote_zero = true;
+	} else {
+		*wrote_zero = false;
+	}
+
+	return mmap(addr, len + 1, PROT_READ | PROT_WRITE, flags, fildes, off);
+}
+
+extern int lxc_munmap_str(void *addr, size_t len, int fildes, bool wrote_zero)
+{
+	int ret, saved_errno;
+
+	/* If we didn't write a terminating \0-byte to the underlying file
+	 * before, we're done now. */
+	if (!wrote_zero)
+		return munmap(addr, len);
+
+	ret = munmap(addr, len + 1);
+
+	saved_errno = errno;
+again:
+	if (ftruncate(fildes, len) < 0) {
+		if (errno == EINTR)
+			goto again;
+		//SYSERROR("Failed to truncate file.");
+	}
+	errno = saved_errno;
+	return ret;
+}
diff --git a/src/lxc/utils.h b/src/lxc/utils.h
index a8f0905..e59e603 100644
--- a/src/lxc/utils.h
+++ b/src/lxc/utils.h
@@ -216,6 +216,37 @@ extern int sha1sum_file(char *fnam, unsigned char *md_value);
 extern int lxc_write_to_file(const char *filename, const void* buf, size_t count, bool add_newline);
 extern int lxc_read_from_file(const char *filename, void* buf, size_t count);
 
+/*
+ * mmap() a file to a \0-terminated string.
+ * The function will write a terminating \0-byte to the underlying file. Hence,
+ * @fildes must refer to a writeable file. lxc_munmap_str() must be used to
+ * unmap the file.
+ *
+ * \param addr             : see mmap()
+ * \param len              : see mmap()
+ * \param flags            : see mmap()
+ * \param fildes           : see mmap()
+ * \param off              : see mmap()
+ * \param[out] wrote_zero  : Whether a terminating \0-byte was written to the
+ *			     underlying file. Must be passed to
+ *			     lxc_munmap_str().
+ */
+extern void *lxc_mmap_str(void *addr, size_t len, int flags, int fildes,
+			  off_t off, bool *wrote_zero);
+
+/*
+ * munmap() a file that has been mmap()ed with lxc_mmap_str().
+ * If a terminating \0-byte has been written to the file underlying the mapping
+ * it will be truncated back to its original size.
+ *
+ * \param addr        : see munmap()
+ * \param len         : see munmap()
+ * \param fildes      : file descriptor of the file underlying the mapping
+ * \param wrote_zero  : Whether a terminating \0-byte has been written to the
+ *		        underlying file
+ */
+extern int lxc_munmap_str(void *addr, size_t len, int fildes, bool wrote_zero);
+
 /* convert variadic argument lists to arrays (for execl type argument lists) */
 extern char** lxc_va_arg_list_to_argv(va_list ap, size_t skip, int do_strdup);
 extern const char** lxc_va_arg_list_to_argv_const(va_list ap, size_t skip);
diff --git a/src/tests/lxc-test-utils-lxc_mmap_str.c b/src/tests/lxc-test-utils-lxc_mmap_str.c
new file mode 100644
index 0000000..c2398fb
--- /dev/null
+++ b/src/tests/lxc-test-utils-lxc_mmap_str.c
@@ -0,0 +1,217 @@
+/*
+ *
+ * Copyright © 2016 Christian Brauner <christian.brauner at mailbox.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * 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,
+ * 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.
+ */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "utils.h"
+
+#define TSTERR(fmt, ...) do { \
+	fprintf(stderr, "%s:%d " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); fflush(NULL); \
+} while (0)
+
+int test_pagesize_1(long pagesize)
+{
+	int ret = -1;
+	char buf[1];
+	char tmp[PATH_MAX] = "tmp_XXXXXX";
+	struct stat f;
+
+	// create unique temporary file
+	int fd = mkstemp(tmp);
+	if (fd < 0) {
+		TSTERR("Failed to create temporary file.");
+		exit(EXIT_FAILURE);
+	}
+
+	/* First test: Resize the file to exactly the size of a single page on
+	 * the system. */
+	if (ftruncate(fd, pagesize) < 0) {
+		TSTERR("Failed to resize file.");
+		goto out;
+	}
+
+	// Write a dummy letter as the last byte of the file.
+	if (pwrite(fd, "A", 1, pagesize - 1) <= 0) {
+		TSTERR("Failed to write dummy byte to file.");
+		goto out;
+	}
+
+	if (fstat(fd, &f) < 0) {
+		TSTERR("Failed to fstat file descriptor %d of temporary file", fd);
+		goto out;
+	}
+
+	if (f.st_size != pagesize) {
+		TSTERR("Size of the file was not equal to one page.");
+		goto out;
+	}
+
+	// Read the last byte of the file.
+	if (pread(fd, &buf, 1, pagesize - 1) <= 0) {
+		TSTERR("Failed to read last byte from file.");
+		goto out;
+	}
+
+	// Check if it is really the dummy letter we read.
+	if (*buf != 'A') {
+		TSTERR("Last byte of file was not equal to 'A'.");
+		goto out;
+	}
+
+	/* First test: We have just created a file exactly the size of a single
+	 * page. That means mmap_file_to_str() should write a terminating
+	 * \0-byte to the underlying file. */
+	bool wrote_zero;
+	char *map = lxc_mmap_str(NULL, pagesize, MAP_PRIVATE, fd, 0, &wrote_zero);
+	if (!map) {
+		TSTERR("Could not establish a mapping for the underlying file.");
+		goto out;
+	}
+
+	/* If the last byte is not \0 then mmap_file_to_str() is not behaving as
+	 * advertised. */
+	if (map[pagesize] != '\0') {
+		TSTERR("Last byte was not '\\0'.");
+		goto out;
+	}
+
+	if (!wrote_zero) {
+		TSTERR("We falsely reported that we did write not write a terminating '\\0'-byte to the underlying file.");
+		goto out;
+	}
+
+	if (lxc_munmap_str(map, pagesize, fd, wrote_zero) < 0) {
+		TSTERR("Could not unmap the underlying file.");
+		goto out;
+	}
+
+	ret = 0;
+out:
+	unlink(tmp);
+	close(fd);
+	return ret;
+}
+
+int test_pagesize_2(long pagesize)
+{
+	int ret = -1;
+	char buf[1];
+	char tmp[PATH_MAX] = "tmp_XXXXXX";
+	struct stat f;
+
+	// create unique temporary file
+	int fd = mkstemp(tmp);
+	if (fd < 0) {
+		TSTERR("Failed to create temporary file.");
+		exit(EXIT_FAILURE);
+	}
+
+	/* Second test: Resize the file to exactly one byte less than the page
+	 * size. */
+	if (ftruncate(fd, pagesize - 1) < 0) {
+		TSTERR("Failed to resize file.");
+		goto out;
+	}
+
+	// Write a dummy letter as the last byte of the file.
+	if (pwrite(fd, "A", 1, pagesize - 2) < 0) {
+		TSTERR("Failed to write dummy byte to file.");
+		goto out;
+	}
+
+	if (fstat(fd, &f) < 0) {
+		TSTERR("Failed to fstat file descriptor %d of temporary file", fd);
+		goto out;
+	}
+
+	if (f.st_size != pagesize - 1) {
+		TSTERR("Size of the file was not exactly one byte less than a single page.");
+		goto out;
+	}
+
+	// Read the last byte of the file.
+	if (pread(fd, &buf, 1, pagesize - 2) <= 0) {
+		TSTERR("Failed to read last byte from file.");
+		goto out;
+	}
+
+	// Check if it is really the dummy letter we read.
+	if (*buf != 'A') {
+		TSTERR("Last byte of file was not equal to 'A'.");
+		goto out;
+	}
+
+	/* Second test: We have just created a file whose size is exactly one
+	 * byte less than the size of a single page. That means
+	 * mmap_file_to_str() should no write a terminating \0-byte to the
+	 * underlying file. */
+	bool wrote_zero;
+	char *map = lxc_mmap_str(NULL, pagesize - 1, MAP_PRIVATE, fd, 0, &wrote_zero);
+	if (!map) {
+		TSTERR("Could not establish a mapping for the underlying file.");
+		goto out;
+	}
+
+	/* If the last byte is not \0 then mmap_file_to_str() is not behaving as
+	 * advertised. */
+	if (map[pagesize - 1] != '\0') {
+		TSTERR("Last byte was not '\\0'.");
+		goto out;
+	}
+
+	if (wrote_zero) {
+		TSTERR("We falsely reported that we did write a terminating '\\0'-byte to the underlying file.");
+		goto out;
+	}
+
+	if (lxc_munmap_str(map, pagesize - 1, fd, wrote_zero) < 0) {
+		TSTERR("Could not unmap the underlying file.");
+		goto out;
+	}
+
+	ret = 0;
+out:
+	unlink(tmp);
+	close(fd);
+	return ret;
+}
+
+int main(int argc, char *argv[])
+{
+	long pagesize = sysconf(_SC_PAGESIZE);
+	if (pagesize <= 0)
+		exit(EXIT_FAILURE);
+
+	if (test_pagesize_1(pagesize) < 0)
+		exit(EXIT_FAILURE);
+
+	if (test_pagesize_2(pagesize) < 0)
+		exit(EXIT_FAILURE);
+
+	exit(EXIT_SUCCESS);
+}


More information about the lxc-devel mailing list