Re: [PATCH] swapon: add swap detection

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

 



 Hi Matthias,

On Fri, Oct 24, 2008 at 06:56:26PM +0200, Matthias Koenig wrote:
> As swap format depends on the pagesize being used, it may happen
> that the pagesize of the swap space and the current pagesize differ,

 when? Kernel update, move a disk between machines?

> resulting in swapon to fail when trying to enable such a swap space.
> In such a case swapon should rather reinitialize the swap space.

 OK.

> +#include <byteswap.h>

 see include/bitops.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 };

 you needn't this array

> +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];
> +};

 see disk-utils/swapheader.h

 Maybe we need to move this header file to the include/ directory.

> +enum {
> +	SWAP_V0 = 1,
> +	SWAP_V1,
> +	SWAP_SUSPEND,
> +	SWAP_ULSUSPEND,
> +	SWAP_OOTSUSPEND
> +};

 It seems you needn't this enum, your code checks for true/false of
 swap_detect_signature().

> +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;

 You needn't to check for suspends. It's already detected by
 swap_is_suspend().

> +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;
> +	}

 The lseek() is overkill for standard block devices. See
 disk-utils/mkswap.c and lib/blkdev.c.

> +	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) {

 for (page = 0x1000; page <= MAX_PAGESIZE; page <<= 1) {

 it's simpler that the page_size array

> +		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);
> +		}

 {le,be}32_to_cpu() from bitops.h

> +		if (verbose)
> +			fprintf(stderr, _("found %sswap v%d signature string"
> +					" for %d KiB PAGE_SIZE\n"),
> +				flip ? "other-endian " : "", swap_version - 1,
> +				pagesize / 1024);

 where do you initialize the "swap_version" variable?

> +		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);

 Please use getpagesize(). It should be reliable (now) and more portable
 to non-glibc environments.

> +	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;
> +	}

 .. one question comes to mind: is it safe? Are you really sure that
 there is a swap space on the device? Maybe I'm too paranoid, but I
 I think it would be better to ask libblkid/volume_id.

 main() {
 ...
    type = fsprobe_get_fstype_by_devname(device);

    if (strcmp(type, "swap") == 0) {
        if (!swap_pagesize_is_valid(device))
            reinitialize = 1;
     } else if (strcmp(type, "suspend") == 0 ||
                strcmp(type, "swsuspend") == 0)
        reinitialize = 1;
 ...
 }

    Karel

-- 
 Karel Zak  <kzak@xxxxxxxxxx>
--
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