summaryrefslogtreecommitdiff
path: root/mkswap.c
diff options
context:
space:
mode:
Diffstat (limited to 'mkswap.c')
-rw-r--r--mkswap.c253
1 files changed, 253 insertions, 0 deletions
diff --git a/mkswap.c b/mkswap.c
new file mode 100644
index 0000000..f797d13
--- /dev/null
+++ b/mkswap.c
@@ -0,0 +1,253 @@
+#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(1);
+}
+
+#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(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 0;
+}
+
+int mkswap_main(struct FileInfo * unnecessary, 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);
+ exit(1);
+ }
+ } else
+ device_name = argv[0];
+ else while (*++argv[0])
+ switch (argv[0][0]) {
+ case 'c': check=1; break;
+ default: usage(mkswap_usage);
+ exit(1);
+ }
+ }
+ return mkswap(device_name, pages, check);
+}