Index: util-linux-ng-2.14.1/mount/swapon.c =================================================================== --- util-linux-ng-2.14.1.orig/mount/swapon.c 2008-09-10 11:02:43.000000000 +0200 +++ util-linux-ng-2.14.1/mount/swapon.c 2008-11-07 14:34:30.000000000 +0100 @@ -13,6 +13,8 @@ #include #include #include +#include +#include #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,116 @@ 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 = 0; + unsigned int pagesize = 0; + off_t size, swap_size; + int swap_version = 0; + 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) { + swap_version = 1; + last_page = s->last_page; + } else if (bswap_32(s->version) == 1) { + flip = 1; + swap_version = 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, + 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 +394,15 @@ do_swapon(const char *orig_special, int 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 +411,10 @@ do_swapon(const char *orig_special, int 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; }