From e8be80dd3829b4fe31ea0335ead1af9bd5d0d65c Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Mon, 2 Mar 2009 13:00:49 +0100 Subject: [PATCH] swapon: add generic swap_get_header() We need to proper swap header detection for swsuspend data and for swap PAGE_SIZE checks. It's better to reuse the code in both cases. The patch removes duplicate stat() call too. Signed-off-by: Karel Zak --- mount/swapon.c | 227 ++++++++++++++++++++++++++----------------------- 1 file changed, 121 insertions(+), 106 deletions(-) diff --git a/mount/swapon.c b/mount/swapon.c index d68e3965..83c0404f 100644 --- a/mount/swapon.c +++ b/mount/swapon.c @@ -45,6 +45,11 @@ #define MAX_PAGESIZE (64 * 1024) +enum { + SIG_SWAPSPACE = 1, + SIG_SWSUSPEND +}; + int all = 0; int priority = -1; /* non-prioritized swap by default */ @@ -177,22 +182,6 @@ display_summary(void) return 0 ; } -static int -swap_is_suspend(const char *device) { - const char *type = fsprobe_get_fstype_by_devname(device); - - /* S1SUSPEND/S2SUSPEND = - * - * "swsuspend" in libblkid - * "suspend" in libvolume_id - */ - if (type && (strcmp(type, "suspend") == 0 || - strcmp(type, "swsuspend") == 0)) - return 1; - - return 0; -} - /* calls mkswap */ static int swap_reinitialize(const char *device) { @@ -244,65 +233,41 @@ swap_reinitialize(const char *device) { return -1; /* error */ } -int -swap_detect_signature(const char *buf) +static int +swap_detect_signature(const char *buf, int *sig) { - if ((memcmp(buf, "SWAP-SPACE", 10) == 0) || - (memcmp(buf, "SWAPSPACE2", 10) == 0)) - return 1; + if (memcmp(buf, "SWAP-SPACE", 10) == 0 || + memcmp(buf, "SWAPSPACE2", 10) == 0) + *sig = SIG_SWAPSPACE; + + else if (memcmp(buf, "S1SUSPEND", 9) == 0 || + memcmp(buf, "S2SUSPEND", 9) == 0 || + memcmp(buf, "ULSUSPEND", 9) == 0 || + memcmp(buf, "\xed\xc3\x02\xe9\x98\x56\xe5\x0c", 8) == 0) + *sig = SIG_SWSUSPEND; + else + return 0; - return 0; + return 1; } -/* 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) +static char * +swap_get_header(int fd, int *sig, unsigned int *pagesize) { - int fd; char *buf; - unsigned int page, last_page = 0; - unsigned int pagesize = 0; - unsigned long long size, swap_size; - int swap_version = 0; - int flip = 0; - int datasz; - struct swap_header_v1_2 *s; - struct stat sb; - - fd = open(dev, O_RDONLY); - if (fd == -1) { - perror("open"); - return 0; - } + ssize_t datasz; + unsigned int page; - /* get size */ - if (fstat(fd, &sb)) { - perror("fstat"); - goto err; - } - if (S_ISBLK(sb.st_mode)) { - if (blkdev_get_size(fd, &size)) { - perror("blkdev_get_size"); - goto err; - } - } else - size = sb.st_size; + *pagesize = 0; + *sig = 0; buf = malloc(MAX_PAGESIZE); - if (!buf) { - perror("malloc"); - goto err; - } + if (!buf) + return NULL; datasz = read(fd, buf, MAX_PAGESIZE); - if (datasz == (ssize_t) -1) { - perror("read"); - goto err1; - } + if (datasz == (ssize_t) -1) + goto err; for (page = 0x1000; page <= MAX_PAGESIZE; page <<= 1) { /* skip 32k pagesize since this does not seem to @@ -313,42 +278,47 @@ swap_get_pagesize(const char *dev) * 40k, that's less than MAX_PAGESIZE */ if (datasz < (page - 10)) break; - if (swap_detect_signature(buf + page - 10)) { - pagesize = page; + if (swap_detect_signature(buf + page - 10, sig)) { + *pagesize = page; break; } } - if (pagesize) { - s = (struct swap_header_v1_2 *)buf; - if (s->version == 1) { - swap_version = 1; - last_page = s->last_page; - } else if (swab32(s->version) == 1) { - flip = 1; - swap_version = 1; - last_page = swab32(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; - } - } + if (*pagesize) + return buf; -err1: - free(buf); err: - close(fd); - return pagesize; + free(buf); + return NULL; +} + +/* returns real size of swap space */ +unsigned long long +swap_get_size(const char *hdr, const char *devname, unsigned int pagesize) +{ + unsigned int last_page = 0; + int swap_version = 0; + int flip = 0; + struct swap_header_v1_2 *s; + + s = (struct swap_header_v1_2 *) hdr; + if (s->version == 1) { + swap_version = 1; + last_page = s->last_page; + } else if (swab32(s->version) == 1) { + flip = 1; + swap_version = 1; + last_page = swab32(s->last_page); + } + if (verbose) + fprintf(stderr, _("%s: found %sswap v%d signature string" + " for %d KiB PAGE_SIZE\n"), + devname, + flip ? "other-endian " : "", + swap_version, + pagesize / 1024); + + return (last_page + 1) * pagesize; } static int @@ -357,7 +327,10 @@ do_swapon(const char *orig_special, int prio, int canonic) { int reinitialize = 0; struct stat st; const char *special = orig_special; - unsigned int swap_pagesize = 0; + int fd, sig; + char *hdr; + unsigned int pagesize; + unsigned long long devsize = 0; if (verbose) printf(_("%s on %s\n"), progname, orig_special); @@ -398,28 +371,70 @@ do_swapon(const char *orig_special, int prio, int canonic) { progname, special); return -1; } + devsize = st.st_size; + } + + fd = open(special, O_RDONLY); + if (fd == -1) { + int errsv = errno; + fprintf(stderr, "%s: %s: open failed: %s\n", + progname, orig_special, strerror(errsv)); + return -1; + } + + if (S_ISBLK(st.st_mode)) { + if (blkdev_get_size(fd, &devsize)) { + int errsv = errno; + fprintf(stderr, "%s: %s: get size failed: %s\n", + progname, orig_special, strerror(errsv)); + close(fd); + return -1; + } + } + + hdr = swap_get_header(fd, &sig, &pagesize); + if (!hdr) { + int errsv = errno; + fprintf(stderr, "%s: %s: failed to read swap header: %s\n", + progname, orig_special, strerror(errsv)); + close(fd); + return -1; } - swap_pagesize = swap_get_pagesize(special); - if (swap_pagesize && (getpagesize() != swap_pagesize)) { + close(fd); + + if (sig == SIG_SWAPSPACE && pagesize) { + unsigned long long swapsize = + swap_get_size(hdr, special, pagesize); if (verbose) + fprintf(stderr, + _("%s: pagesize=%d, swapsize=%llu, devsize=%llu\n"), + special, pagesize, swapsize, devsize); + + if (swapsize > devsize) { + if (verbose) + fprintf(stderr, _("%s: last_page 0x%08llx is larger" + " than actual size of swapspace\n"), + special, swapsize); + } else if (getpagesize() != pagesize) { fprintf(stderr, _("%s: %s: swap format pagesize does not match." - " Reinitializing the swap.\n"), + " 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. - */ - if (swap_is_suspend(special)) { + reinitialize = 1; + } + } else if (sig == SIG_SWSUSPEND) { + /* 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. + */ fprintf(stdout, _("%s: %s: software suspend data detected. " "Reinitializing the swap.\n"), progname, special); reinitialize = 1; } + free(hdr); + if (reinitialize) { if (swap_reinitialize(special) < 0) return -1; -- 2.39.5