| #include "internal.h" |
| /* |
| * mkswap.c - set up a linux swap device |
| * |
| * (C) 1991 Linus Torvalds. This file may be redistributed as per |
| * the Linux copyright. |
| */ |
| |
| /* |
| * 20.12.91 - time began. Got VM working yesterday by doing this by hand. |
| * |
| * Usage: mkswap [-c] device [size-in-blocks] |
| * |
| * -c for readablility checking (use it unless you are SURE!) |
| * |
| * The device may be a block device or a image of one, but this isn't |
| * enforced (but it's not much fun on a character device :-). |
| * |
| * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the |
| * size-in-blocks parameter optional added Wed Feb 8 10:33:43 1995. |
| */ |
| |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <stdlib.h> |
| #include <sys/stat.h> |
| #include <sys/ioctl.h> |
| |
| #include <asm/page.h> |
| #include <linux/fs.h> |
| |
| #ifndef __linux__ |
| # define volatile |
| #endif |
| |
| #define TEST_BUFFER_PAGES 8 |
| |
| const char mkswap_usage[] = "mkswap [-c] partition [block-count]\n" |
| "\n" |
| "\tPrepare a disk partition to be used as a swap partition.\n" |
| "\tThe default block count is the size of the entire partition.\n" |
| "\n" |
| "\t-c:\tCheck for read-ability.\n" |
| "\tblock-count\tUse only this many blocks.\n"; |
| |
| static const char * program_name = "mkswap"; |
| static const char * device_name = NULL; |
| static int DEV = -1; |
| static long PAGES = 0; |
| static int do_check = 0; |
| static int badpages = 0; |
| |
| |
| static long bit_test_and_set (unsigned int *addr, unsigned int nr) |
| { |
| unsigned int r, m; |
| |
| addr += nr / (8 * sizeof(int)); |
| r = *addr; |
| m = 1 << (nr & (8 * sizeof(int) - 1)); |
| *addr = r | m; |
| return (r & m) != 0; |
| } |
| |
| static int bit_test_and_clear (unsigned int *addr, unsigned int nr) |
| { |
| unsigned int r, m; |
| |
| addr += nr / (8 * sizeof(int)); |
| r = *addr; |
| m = 1 << (nr & (8 * sizeof(int) - 1)); |
| *addr = r & ~m; |
| return (r & m) != 0; |
| } |
| |
| /* |
| * Volatile to let gcc know that this doesn't return. When trying |
| * to compile this under minix, volatile gives a warning, as |
| * exit() isn't defined as volatile under minix. |
| */ |
| volatile void fatal_error(const char * fmt_string) |
| { |
| fprintf(stderr,fmt_string,program_name,device_name); |
| exit(FALSE); |
| } |
| |
| #define die(str) fatal_error("%s: " str "\n") |
| |
| static void check_blocks(int * signature_page) |
| { |
| unsigned int current_page; |
| int do_seek = 1; |
| char buffer[PAGE_SIZE]; |
| |
| current_page = 0; |
| while (current_page < PAGES) { |
| if (!do_check) { |
| bit_test_and_set(signature_page,current_page++); |
| continue; |
| } else { |
| printf("\r%d", current_page); |
| } |
| if (do_seek && lseek(DEV,current_page*PAGE_SIZE,SEEK_SET) != |
| current_page*PAGE_SIZE) |
| die("seek failed in check_blocks"); |
| if ( (do_seek = (PAGE_SIZE != read(DEV, buffer, PAGE_SIZE))) ) { |
| bit_test_and_clear(signature_page,current_page++); |
| badpages++; |
| continue; |
| } |
| bit_test_and_set(signature_page,current_page++); |
| } |
| if (do_check) |
| printf("\n"); |
| if (badpages) |
| printf("%d bad page%s\n",badpages,(badpages>1)?"s":""); |
| } |
| |
| static long valid_offset (int fd, int offset) |
| { |
| char ch; |
| |
| if (lseek (fd, offset, 0) < 0) |
| return 0; |
| if (read (fd, &ch, 1) < 1) |
| return 0; |
| return 1; |
| } |
| |
| static int count_blocks (int fd) |
| { |
| int high, low; |
| |
| low = 0; |
| for (high = 1; valid_offset (fd, high); high *= 2) |
| low = high; |
| while (low < high - 1) |
| { |
| const int mid = (low + high) / 2; |
| |
| if (valid_offset (fd, mid)) |
| low = mid; |
| else |
| high = mid; |
| } |
| valid_offset (fd, 0); |
| return (low + 1); |
| } |
| |
| static int get_size(const char *file) |
| { |
| int fd; |
| int size; |
| |
| fd = open(file, O_RDWR); |
| if (fd < 0) { |
| perror(file); |
| exit(1); |
| } |
| if (ioctl(fd, BLKGETSIZE, &size) >= 0) { |
| close(fd); |
| return (size * 512); |
| } |
| |
| size = count_blocks(fd); |
| close(fd); |
| return size; |
| } |
| |
| int |
| mkswap(const char *device_name, int pages, int check) |
| { |
| struct stat statbuf; |
| int goodpages; |
| int signature_page[PAGE_SIZE/sizeof(int)]; |
| |
| PAGES = pages; |
| do_check = check; |
| |
| memset(signature_page,0,PAGE_SIZE); |
| |
| if (device_name && !PAGES) { |
| PAGES = get_size(device_name) / PAGE_SIZE; |
| } |
| if (!device_name || PAGES<10) { |
| fprintf(stderr, |
| "%s: error: swap area needs to be at least %ldkB\n", |
| program_name, 10 * PAGE_SIZE / 1024); |
| /* usage(mkswap_usage); */ |
| exit(1); |
| } |
| if (PAGES > 8 * (PAGE_SIZE - 10)) { |
| PAGES = 8 * (PAGE_SIZE - 10); |
| fprintf(stderr, "%s: warning: truncating swap area to %ldkB\n", |
| program_name, PAGES * PAGE_SIZE / 1024); |
| } |
| DEV = open(device_name,O_RDWR); |
| if (DEV < 0 || fstat(DEV, &statbuf) < 0) { |
| perror(device_name); |
| exit(1); |
| } |
| if (!S_ISBLK(statbuf.st_mode)) |
| do_check=0; |
| else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) |
| die("Will not try to make swapdevice on '%s'"); |
| check_blocks(signature_page); |
| if (!bit_test_and_clear(signature_page,0)) |
| die("fatal: first page unreadable"); |
| goodpages = PAGES - badpages - 1; |
| if (goodpages <= 0) |
| die("Unable to set up swap-space: unreadable"); |
| printf("Setting up swapspace, size = %ld bytes\n",goodpages*PAGE_SIZE); |
| strncpy((char*)signature_page+PAGE_SIZE-10,"SWAP-SPACE",10); |
| if (lseek(DEV, 0, SEEK_SET)) |
| die("unable to rewind swap-device"); |
| if (PAGE_SIZE != write(DEV, signature_page, PAGE_SIZE)) |
| die("unable to write signature page"); |
| |
| close(DEV); |
| return (TRUE); |
| } |
| |
| int mkswap_main(int argc, char ** argv) |
| { |
| char * tmp; |
| long int pages=0; |
| int check=0; |
| |
| if (argc && *argv) |
| program_name = *argv; |
| while (argc > 1) { |
| argv++; |
| argc--; |
| if (argv[0][0] != '-') |
| if (device_name) { |
| pages = strtol(argv[0],&tmp,0)>>(PAGE_SHIFT-10); |
| if (*tmp) { |
| usage (mkswap_usage); |
| } |
| } else |
| device_name = argv[0]; |
| else while (*++argv[0]) |
| switch (argv[0][0]) { |
| case 'c': check=1; break; |
| default: usage (mkswap_usage); |
| exit( TRUE); |
| } |
| } |
| exit( mkswap(device_name, pages, check)); |
| } |