[PATCH] swapon: add swap detection

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



As swap format depends on the pagesize being used, it may happen
that the pagesize of the swap space and the current pagesize differ,
resulting in swapon to fail when trying to enable such a swap space.
In such a case swapon should rather reinitialize the swap space.

Co-Author: Olaf Hering <olh@xxxxxxx>
Signed-off-by: Matthias Koenig <mkoenig@xxxxxxx>
---

 mount/swapon.c |  145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 145 insertions(+), 0 deletions(-)

diff --git a/mount/swapon.c b/mount/swapon.c
index eb0eb4a..ce1f71a 100644
--- a/mount/swapon.c
+++ b/mount/swapon.c
@@ -13,6 +13,8 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <fcntl.h>
+#include <stdint.h>
+#include <byteswap.h>
 #include "xmalloc.h"
 #include "swap_constants.h"
 #include "nls.h"
@@ -39,6 +41,33 @@
 #define QUIET	1
 #define CANONIC	1
 
+#define MAX_PAGESIZE	(64 * 1024)
+static unsigned int page_size[] = {  4 * 1024,
+				     8 * 1024,
+				    16 * 1024,
+				    64 * 1024,
+				    0 };
+
+
+struct swap_info {
+	char		bootbits[1024];	/* Space for disklabel etc. */
+	uint32_t	version;
+	uint32_t	last_page;
+	uint32_t	nr_badpages;
+	unsigned char	sws_uuid[16];
+	unsigned char	sws_volume[16];
+	uint32_t	padding[117];
+	uint32_t	badpages[1];
+};
+
+enum {
+	SWAP_V0 = 1,
+	SWAP_V1,
+	SWAP_SUSPEND,
+	SWAP_ULSUSPEND,
+	SWAP_OOTSUSPEND
+};
+
 int all = 0;
 int priority = -1;	/* non-prioritized swap by default */
 
@@ -238,11 +267,114 @@ swap_reinitialize(const char *device) {
 	return -1; /* error */
 }
 
+int
+swap_detect_signature(const char *buf)
+{
+	if (memcmp(buf, "SWAP-SPACE", 10) == 0)
+		return SWAP_V0;
+	else if (memcmp(buf, "SWAPSPACE2", 10) == 0)
+		return SWAP_V1;
+	else if	(memcmp(buf, "S1SUSPEND", 9) == 0)
+		return SWAP_SUSPEND;
+	else if	(memcmp(buf, "ULSUSPEND", 9) == 0)
+		return SWAP_ULSUSPEND;
+	else if (memcmp(buf, "\xed\xc3\x02\xe9\x98\x56\xe5\x0c", 8) == 0)
+		return SWAP_OOTSUSPEND;
+
+	return 0;
+}
+
+/* return the pagesize the swap format has been built with
+ * as swap metadata depends on the pagesize, we have to
+ * reinitialize if it does not match with the current pagesize
+ * returns 0 if not a valid swap format
+ */
+unsigned int
+swap_get_pagesize(const char *dev)
+{
+	int fd;
+	char *buf;
+	unsigned int *page, last_page;
+	unsigned int pagesize = 0;
+	off_t size, swap_size;
+	int swap_version;
+	int flip = 0;
+	struct swap_info *s;
+
+	fd = open(dev, O_RDONLY);
+	if (fd == -1) {
+		perror("open");
+		return 0;
+	}
+
+	/* get size */
+	size = lseek(fd, 0, SEEK_END);
+	if (size == (off_t)-1) {
+		perror("lseek");
+		goto err;
+	}
+	/* rewind */
+	if (lseek(fd, 0, SEEK_SET)) {
+		perror("lseek");
+		goto err;
+	}
+
+	buf = malloc(MAX_PAGESIZE);
+	if (!buf) {
+		perror("malloc");
+		goto err;
+	}
+
+	if (read(fd, buf, MAX_PAGESIZE) == (ssize_t)-1) {
+		perror("read");
+		goto err1;
+	}
+
+	for (page = page_size; *page; ++page) {
+		char *off = buf + *page - 10;
+		if (swap_detect_signature(off)) {
+			pagesize = *page;
+			break;
+		}
+	}
+	if (pagesize) {
+		s = (struct swap_info *)buf;
+		if (s->version == 1)
+			last_page = s->last_page;
+		else if (bswap_32(s->version) == 1) {
+			flip = 1;
+			last_page = bswap_32(s->last_page);
+		}
+		if (verbose)
+			fprintf(stderr, _("found %sswap v%d signature string"
+					" for %d KiB PAGE_SIZE\n"),
+				flip ? "other-endian " : "", swap_version - 1,
+				pagesize / 1024);
+		swap_size = (last_page + 1) * pagesize;
+		if (swap_size > size) {
+			if (verbose)
+				fprintf(stderr, _("last_page 0x%08llx is larger"
+					" than actual size of swapspace\n"),
+					(unsigned long long)swap_size);
+			pagesize = 0;
+		}
+	}
+
+err1:
+	free(buf);
+err:
+	close(fd);
+	return pagesize;
+}
+
 static int
 do_swapon(const char *orig_special, int prio, int canonic) {
 	int status;
+	int reinitialize = 0;
 	struct stat st;
 	const char *special = orig_special;
+	unsigned int pagesize = sysconf(_SC_PAGESIZE);
+	unsigned int swap_pagesize = 0;
 
 	if (verbose)
 		printf(_("%s on %s\n"), progname, orig_special);
@@ -260,6 +392,15 @@ do_swapon(const char *orig_special, int prio, int canonic) {
 		return -1;
 	}
 
+	swap_pagesize = swap_get_pagesize(special);
+	if (swap_pagesize && (pagesize != swap_pagesize)) {
+		if (verbose)
+			fprintf(stderr, _("swap format pagesize does not match."
+				" Reinitializing the swap.\n"),
+				progname, special);
+		reinitialize = 1;
+	}
+
 	/* We have to reinitialize swap with old (=useless) software suspend
 	 * data. The problem is that if we don't do it, then we get data
 	 * corruption the next time an attempt at unsuspending is made.
@@ -268,6 +409,10 @@ do_swapon(const char *orig_special, int prio, int canonic) {
 		fprintf(stdout, _("%s: %s: software suspend data detected. "
 					"Reinitializing the swap.\n"),
 			progname, special);
+		reinitialize = 1;
+	}
+
+	if (reinitialize) {
 		if (swap_reinitialize(special) < 0)
 			return -1;
 	}

--
To unsubscribe from this list: send the line "unsubscribe util-linux-ng" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux