/* * mkswap.c - set up a linux swap device * * (C) 1991 Linus Torvalds. This file may be redistributed as per * the Linux copyright. * * (C) 1999 Red Hat Software - Modified by Matt Wilson for Red Hat Software * still GPLed, of course */ /* * 20.12.91 - time began. Got VM working yesterday by doing this by hand. * * Usuage: mkswap [-c] [-vN] [-f] device [size-in-blocks] * * -c for readability checking. (Use it unless you are SURE!) * -vN for swap areas version N. (Only N=0,1 known today.) * -f for forcing swap creation even if it would smash partition table. * * The device may be a block device or an 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. * * Version 1 swap area code (for kernel 2.1.117), aeb, 981010. * * Sparc fixes, jj@ultra.linux.cz (Jakub Jelinek), 981201 - mangled by aeb. */ #include #include #include #include #include #include /* for _IO */ #include #include #include /* for PAGE_SIZE and PAGE_SHIFT */ #include #include #include "devices.h" #include "install.h" #include "intl.h" #include "log.h" #include "mkswap.h" #include "newt.h" #include "windows.h" #ifndef _IO /* pre-1.3.45 */ #define BLKGETSIZE 0x1260 #else /* same on i386, m68k, arm; different on alpha, mips, sparc, ppc */ #define BLKGETSIZE _IO(0x12,96) #endif int canEnableSwap = 1; static char * program_name = "mkswap"; static int DEV = -1; static long PAGES = 0; static int check = 0; static int badpages = 0; static int version = -1; #ifdef __sparc__ static int sparc64 = 0; #endif #define MAKE_VERSION(p,q,r) (65536*(p) + 256*(q) + (r)) static int linux_version_code(void) { struct utsname my_utsname; int p, q, r; if (uname(&my_utsname) == 0) { #ifdef __sparc__ if (!strcmp(my_utsname.machine, "sparc64")) sparc64 = 1; #endif p = atoi(strtok(my_utsname.release, ".")); q = atoi(strtok(NULL, ".")); r = atoi(strtok(NULL, ".")); return MAKE_VERSION(p,q,r); } return 0; } /* * The definition of the union swap_header uses the constant PAGE_SIZE. * Unfortunately, on some architectures this depends on the hardware model, * and can only be found at run time -- we use getpagesize(). */ static int pagesize; static int *signature_page; struct swap_header_v1 { char bootbits[1024]; /* Space for disklabel etc. */ unsigned int version; unsigned int last_page; unsigned int nr_badpages; unsigned int padding[125]; unsigned int badpages[1]; } *p; static void init_signature_page() { pagesize = getpagesize(); #ifdef PAGE_SIZE if (pagesize != PAGE_SIZE) logMessage( "Assuming pages of size %d\n", pagesize); #endif signature_page = (int *) malloc(pagesize); memset(signature_page,0,pagesize); p = (struct swap_header_v1 *) signature_page; } static void write_signature(char *sig) { char *sp = (char *) signature_page; strncpy(sp+pagesize-10, sig, 10); } /* Maximum allowable number of pages in one swap. From 2.2.0 onwards, this depends on how many offset bits the architectures can actually store into the page tables and on 32bit architectures it is limited to 2GB at the same time. Old swap format keeps the limit of 8*pagesize*(pagesize - 10) */ #define V0_MAX_PAGES (8 * (pagesize - 10)) #define V1_OLD_MAX_PAGES ((0x7fffffff / pagesize) - 1) #if defined(__alpha__) #define V1_MAX_PAGES ((1 << 24) - 1) #elif defined(__arm__) #define V1_MAX_PAGES V1_OLD_MAX_PAGES /* ((1 << 23) - 1) */ #elif defined(__i386__) #define V1_MAX_PAGES V1_OLD_MAX_PAGES /* ((1 << 24) - 1) */ #elif defined(__mc68000__) #define V1_MAX_PAGES V1_OLD_MAX_PAGES /* ((1 << 20) - 1) */ #elif defined(__mips__) #define V1_MAX_PAGES ((1 << 17) - 1) #elif defined(__powerpc__) #define V1_MAX_PAGES V1_OLD_MAX_PAGES /* ((1 << 24) - 1) */ #elif defined(__sparc_v9__) #define V1_MAX_PAGES ((3 << 29) - 1) /* ((1L << 43) - 1) */ #elif defined(__sparc__) #define V1_MAX_PAGES (sparc64 ? ((3 << 29) - 1) : ((1 << 18) - 1)) #else #define V1_MAX_PAGES V1_OLD_MAX_PAGES #endif #define MAX_BADPAGES ((pagesize-1024-128*sizeof(int)-10)/sizeof(int)) static void bit_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; } 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; } void fatal_error(const char * fmt_string) { logMessage(fmt_string, program_name); } #define usage() return INST_ERROR #define die(str) fatal_error("%s: " str "\n"); return INST_ERROR void page_ok(int page) { if (version==0) bit_set(signature_page, page); } int page_bad(int page) { if (version == 0) bit_test_and_clear(signature_page, page); else { if (badpages == MAX_BADPAGES) { die("too many bad pages"); } p->badpages[badpages] = page; } badpages++; return 0; } static int check_blocks(int * signature_page, char * file) { unsigned int current_page; int do_seek = 1; char *buffer; char buf[128]; newtComponent form = NULL, scale = NULL; newtGrid grid; int rc; if (check) { form = newtForm(NULL, NULL, 0); sprintf(buf, _("Formatting swap space on device %s..."), file); scale = newtScale(-1, -1, 58, PAGES); grid = newtCreateGrid(1, 2); newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, newtLabel(-1, -1, buf), 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); newtGridSetField(grid, 0, 1, NEWT_GRID_COMPONENT, scale, 0, 1, 0, 0, 0, 0); newtGridAddComponentsToForm(grid, form, 1); newtGridWrappedWindow(grid, _("Formatting")); newtDrawForm(form); newtRefresh(); } buffer = malloc(pagesize); if (!buffer) { die("Out of memory"); } current_page = 0; while (current_page < PAGES) { if (!check) { page_ok(current_page++); continue; } newtScaleSet(scale, current_page); newtRefresh(); if (do_seek && lseek(DEV,current_page*pagesize,SEEK_SET) != current_page*pagesize) { die("seek failed in check_blocks"); } if ((do_seek = (pagesize != read(DEV, buffer, pagesize)))) { if ((rc = page_bad(current_page++))) return rc; continue; } page_ok(current_page++); } if (badpages) logMessage("%d bad page%s\n",badpages,(badpages>1)?"s":""); if (check) { newtPopWindow(); newtFormDestroy(form); } return 0; } 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 find_size (int fd) { unsigned int high, low; low = 0; for (high = 1; high > 0 && 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; } return (low + 1); } /* return size in pages, to avoid integer overflow */ static int get_size(const char *file) { int fd; int size; fd = open(file, O_RDONLY); if (fd < 0) { logMessage("open %s: %s", file, strerror(errno)); return -1; } if (ioctl(fd, BLKGETSIZE, &size) >= 0) { int sectors_per_page = pagesize/512; size /= sectors_per_page; } else { size = find_size(fd) / pagesize; } close(fd); return size; } int enableswap(char * devicename, int size, int checkBlocks) { struct stat statbuf; int maxpages; int goodpages; int offset; char device_name[100]; int version_code = linux_version_code(); check = checkBlocks; if (testing) { return 0; } if (*devicename == '/') { strcpy(device_name, devicename); } else { sprintf(device_name, "/tmp/%s", devicename); if (devMakeInode(devicename, device_name)) { return INST_ERROR; } } init_signature_page(); /* get pagesize */ if (!device_name) { logMessage( "%s: error: Nowhere to set up swap on?\n", program_name); usage(); } if (!PAGES) { if ((PAGES = get_size(device_name)) < 0) return INST_ERROR; } if (version == -1) { if (PAGES <= V0_MAX_PAGES) version = 0; else if (version_code < MAKE_VERSION(2,1,117)) version = 0; else if (pagesize < 2048) version = 0; else version = 1; } if (version != 0 && version != 1) { logMessage( "%s: error: unknown version %d\n", program_name, version); usage(); } if (PAGES < 10) { logMessage( "%s: error: swap area needs to be at least %ldkB\n", program_name, (long)(10 * pagesize / 1024)); usage(); } DEV = open(device_name,O_RDWR); if (DEV < 0 || fstat(DEV, &statbuf) < 0) { logMessage("open %s: %s", device_name, strerror(errno)); } if (!S_ISBLK(statbuf.st_mode)) check=0; else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340) { die("Will not try to make swapdevice"); } #ifdef __sparc__ if (version == 0) { unsigned char *buffer = (unsigned char *)signature_page; unsigned short *q, sum; if (read(DEV, buffer, 512) != 512) { die("fatal: first page unreadable"); } if (buffer[508] == 0xDA && buffer[509] == 0xBE) { q = (unsigned short *)(buffer + 510); for (sum = 0; q >= (unsigned short *) buffer;) sum ^= *q--; if (!sum) { if (version_code >= MAKE_VERSION(2,1,117)) { logMessage( "\ Device '%s' contains a valid Sun disklabel.\n\ This probably means creating v0 swap would destroy your partition table.\n\ Using v1 swap format, which is compatible with 2.1.117 and later kernels.\n", device_name); version = 1; } else { logMessage( "\ Device '%s' contains a valid Sun disklabel.\n\ This probably means creating v0 swap would destroy your partition table\n\ No swap created. If you really want to create swap v0 on that device, use\n\ the -f option of mkswap to force it.\n", device_name); return INST_ERROR; } } } } #endif if (!version) maxpages = V0_MAX_PAGES; else if (version_code >= MAKE_VERSION(2,2,1)) maxpages = V1_MAX_PAGES; else { maxpages = V1_OLD_MAX_PAGES; if (maxpages > V1_MAX_PAGES) maxpages = V1_MAX_PAGES; } if (PAGES > maxpages) { PAGES = maxpages; logMessage( "%s: warning: truncating swap area to %ldkB\n", program_name, PAGES * pagesize / 1024); } if (version == 0 || check) if (check_blocks(signature_page, device_name)) { close(DEV); unlink(device_name); newtPopWindow(); return INST_ERROR; } if (version == 0 && !bit_test_and_clear(signature_page,0)) { die("fatal: first page unreadable"); } if (version == 1) { p->version = version; p->last_page = PAGES-1; p->nr_badpages = badpages; } goodpages = PAGES - badpages - 1; if (goodpages <= 0) { die("Unable to set up swap-space: unreadable"); } logMessage("Setting up swapspace on %s version %d, size = %ld bytes\n", device_name, version, (long)(goodpages*pagesize)); write_signature((version == 0) ? "SWAP-SPACE" : "SWAPSPACE2"); offset = ((version == 0) ? 0 : 1024); if (lseek(DEV, offset, SEEK_SET) != offset) { die("unable to rewind swap-device"); } if (write(DEV,(char*)signature_page+offset, pagesize-offset) != pagesize-offset) { die("unable to write signature page"); } /* * A subsequent swapon() will fail if the signature * is not actually on disk. (This is a kernel bug.) */ if (fsync(DEV)) { die("fsync failed"); } close(DEV); if (swapon(device_name, 0)) { logMessage("mkswap: swapon() failed: %s\n", strerror(errno)); } if (*devicename != '/') unlink(device_name); return 0; } int activeSwapSpace(struct partitionTable * table, struct fstab * finalFstab, int forceFormat) { newtComponent form, checkList, okay, cancel, sb, text, answer, label; newtComponent check, checkbox, blank; newtGrid grid, subgrid, buttons, checkgrid; char * states = NULL; char buf[80]; int i, j, rc, row; struct fstabEntry entry; struct fstabEntry ** entries = NULL; struct fstab fstab; static int chkBadBlocks = 0; char doCheck = ' '; if (!canEnableSwap) return INST_NOP; fstab = copyFstab(finalFstab); form = newtForm(NULL, NULL, 0); for (i = j = 0; i < table->count; i++) if (table->parts[i].type == PART_SWAP) j++; if (!j) { rc = newtWinChoice(_("No Swap Space"), _("Repartition"), _("Continue"), _("You don't have any swap space defined. Would " "you like to continue, or repartition your disk?")); if (rc != 2) return INST_CANCEL; return 0; } if (!forceFormat) { if (j > 3) { sb = newtVerticalScrollbar(47, 7, 3, 9, 10); } else sb = NULL; checkList = newtForm(sb, NULL, 0); if (sb) newtFormSetHeight(checkList, j > 3); text = newtTextboxReflowed(-1, -1, _("What partitions would you like " "to use for swap space? This will destroy any " "information already on the partition."), 52, 0, 15, 0); label = newtLabel(-1, -1, " Device Size (k)"); states = alloca(sizeof(char) * table->count); entries = alloca(sizeof(*entries) * table->count); for (i = 0, row = 0; i < table->count; i++) { if (table->parts[i].type != PART_SWAP) continue; for (j = 0; j < fstab.numEntries; j++) if (!strcmp(table->parts[i].device, fstab.entries[j].device)) break; if ((j < fstab.numEntries && fstab.entries[j].mntpoint) || !testing) states[i] = '*'; else states[i] = ' '; if (j < fstab.numEntries) entries[i] = fstab.entries + j; else entries[i] = NULL; sprintf(buf, "/dev/%-11s %9d", table->parts[i].device, table->parts[i].size); check = newtCheckbox(-1, row++, buf, states[i], NULL, &states[i]); newtFormAddComponent(checkList, check); } if (row > 3) { blank = newtForm(NULL, NULL, 0); newtFormSetWidth(blank, 2); newtFormSetHeight(blank, 3); newtFormSetBackground(blank, NEWT_COLORSET_CHECKBOX); checkgrid = newtGridHCloseStacked( NEWT_GRID_COMPONENT, checkList, NEWT_GRID_COMPONENT, blank, NEWT_GRID_COMPONENT, sb, NULL); } else { checkgrid = newtGridHCloseStacked(NEWT_GRID_COMPONENT, checkList, NULL); } checkbox = newtCheckbox(-1, -1, _("Check for bad blocks during format"), chkBadBlocks ? '*' : ' ', NULL, &doCheck); buttons = newtButtonBar(_("Ok"), &okay, _("Back"), &cancel, NULL); subgrid = newtCreateGrid(1, 3); newtGridSetField(subgrid, 0, 0, NEWT_GRID_COMPONENT, label, 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); newtGridSetField(subgrid, 0, 1, NEWT_GRID_SUBGRID, checkgrid, 0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0); newtGridSetField(subgrid, 0, 2, NEWT_GRID_COMPONENT, checkbox, 0, 1, 0, 0, NEWT_ANCHOR_LEFT, 0); grid = newtGridBasicWindow(text, subgrid, buttons); newtGridAddComponentsToForm(grid, form, 1); newtGridWrappedWindow(grid, _("Active Swap Space")); newtGridFree(grid, 1); answer = newtRunForm(form); newtFormDestroy(form); newtPopWindow(); chkBadBlocks = (doCheck != ' '); if (answer == cancel) { freeFstab(fstab); return INST_CANCEL; } } for (i = 0; i < table->count; i++) { if (table->parts[i].type != PART_SWAP) continue; if (forceFormat || states[i] != ' ') { if (!forceFormat && entries[i]) entries[i]->mntpoint = strdup("swap"); else { initFstabEntry(&entry); entry.device = strdup(table->parts[i].device); entry.size = table->parts[i].size; entry.type = table->parts[i].type; entry.tagName = table->parts[i].tagName; entry.mntpoint = strdup("swap"); addFstabEntry(&fstab, entry); } } else if (entries[i]) { free(entries[i]->mntpoint); entries[i]->mntpoint = NULL; } } if (canEnableSwap) { for (i = 0; i < fstab.numEntries; i++) { if (fstab.entries[i].type == PART_SWAP && fstab.entries[i].mntpoint) { enableswap(fstab.entries[i].device, 0, chkBadBlocks); canEnableSwap = 0; } } } freeFstab(*finalFstab); *finalFstab = copyFstab(&fstab); freeFstab(fstab); return 0; }