From ae9c2b67b62f2910d8b4340551a38254fc432a17 Mon Sep 17 00:00:00 2001 From: Neil Brown Date: Wed, 24 Mar 2010 17:22:36 +1100 Subject: [PATCH] Add a number of tests which had not been commited to 'git' previously. Signed-off-by: NeilBrown --- Makefile | 2 +- demo.orig/Makefile | 53 + demo.orig/README | 49 + demo.orig/vpatch.c | 666 +++ demo.orig/wiggle.c | 643 +++ demo.patched/Makefile | 56 + demo.patched/README | 58 + demo.patched/vpatch.c | 667 +++ demo.patched/wiggle.c | 643 +++ demo/383MdBlocked | 271 ++ demo/Makefile | 52 + demo/README | 47 + demo/md.c | 5769 ++++++++++++++++++++++++++ demo/vpatch.c | 668 +++ demo/wiggle.c | 643 +++ tests/contrib/mod_tbill/merge | 36 + tests/contrib/mod_tbill/orig | 35 + tests/contrib/mod_tbill/patch | 16 + tests/contrib/nmi.c/merge | 471 +++ tests/contrib/nmi.c/orig | 470 +++ tests/contrib/nmi.c/patch | 8 + tests/contrib/pfkey_v2_parse.c/merge | 1789 ++++++++ tests/contrib/pfkey_v2_parse.c/orig | 1778 ++++++++ tests/contrib/pfkey_v2_parse.c/patch | 57 + tests/simple/bothadd/lmerge | 4 + tests/simple/bothadd/merge | 4 + tests/simple/bothadd/new | 3 + tests/simple/bothadd/new2 | 4 + tests/simple/bothadd/orig | 4 + tests/simple/trivial-conflict/merge | 9 + tests/simple/trivial-conflict/orig | 1 + tests/simple/trivial-conflict/patch | 5 + 32 files changed, 14980 insertions(+), 1 deletion(-) create mode 100644 demo.orig/Makefile create mode 100644 demo.orig/README create mode 100644 demo.orig/vpatch.c create mode 100644 demo.orig/wiggle.c create mode 100644 demo.patched/Makefile create mode 100644 demo.patched/README create mode 100644 demo.patched/vpatch.c create mode 100644 demo.patched/wiggle.c create mode 100644 demo/383MdBlocked create mode 100644 demo/Makefile create mode 100644 demo/README create mode 100644 demo/md.c create mode 100644 demo/vpatch.c create mode 100644 demo/wiggle.c create mode 100644 tests/contrib/mod_tbill/merge create mode 100644 tests/contrib/mod_tbill/orig create mode 100644 tests/contrib/mod_tbill/patch create mode 100644 tests/contrib/nmi.c/merge create mode 100644 tests/contrib/nmi.c/orig create mode 100644 tests/contrib/nmi.c/patch create mode 100644 tests/contrib/pfkey_v2_parse.c/merge create mode 100644 tests/contrib/pfkey_v2_parse.c/orig create mode 100644 tests/contrib/pfkey_v2_parse.c/patch create mode 100644 tests/simple/bothadd/lmerge create mode 100644 tests/simple/bothadd/merge create mode 100644 tests/simple/bothadd/new create mode 100644 tests/simple/bothadd/new2 create mode 100644 tests/simple/bothadd/orig create mode 100644 tests/simple/trivial-conflict/merge create mode 100644 tests/simple/trivial-conflict/orig create mode 100644 tests/simple/trivial-conflict/patch diff --git a/Makefile b/Makefile index bbf7ed6..6725969 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ wiggle.man : wiggle.1 nroff -man wiggle.1 > wiggle.man clean: - rm -f *.o *.man wiggle .version* + rm -f *.o *.man wiggle .version* demo.patch find . -name core -o -name '*.tmp*' -o -name .tmp | xargs rm -f install : wiggle wiggle.1 diff --git a/demo.orig/Makefile b/demo.orig/Makefile new file mode 100644 index 0000000..831723c --- /dev/null +++ b/demo.orig/Makefile @@ -0,0 +1,53 @@ + +# Note on my Mobile Pentium II, -march=pentium2 delivers twice the performance of i386 +#OptDbg=-O3 +#OptDbg=-O3 -march=pentium2 +OptDbg=-ggdb +CFLAGS=$(OptDbg) -Wall -Werror + +# STRIP = -s +INSTALL = /usr/bin/install +DESTDIR = +BINDIR = /usr/bin +MANDIR = /usr/share/man +MAN1DIR = $(MANDIR)/man1 +MAN5DIR = $(MANDIR)/man5 +LDLIBS=-lncurses + +all: wiggle wiggle.man test + +vpatch : vpatch.o extract.o split.o diff.o + + +wiggle : wiggle.o load.o split.o extract.o diff.o bestmatch.o ReadMe.o merge.o +wiggle.o load.o split.o extract.o diff.o bestmatch.o ReadMe.o merge.o : wiggle.h + +test: wiggle dotest + sh dotest + +wiggle.man : wiggle.1 + nroff -man wiggle.1 > wiggle.man + +clean: + rm -f *.o *.man wiggle .version* version + find . -name core -o -name '*.tmp*' -o -name .tmp | xargs rm -f + +install : wiggle wiggle.1 + $(INSTALL) -D $(STRIP) -m 755 wiggle $(DESTDIR)$(BINDIR)/wiggle + $(INSTALL) -D -m 644 wiggle.1 $(DESTDIR)$(MAN1DIR)/wiggle.1 + +version : ReadMe.c wiggle.1 + @rm -f version + @sed -n -e 's/.*wiggle - v\([0-9.]*\) - .*/\1/p' ReadMe.c > .version-readme + @sed -n -e 's/.*WIGGLE 1 "" v\([0-9.]*\)$$/\1/p' wiggle.1 > .version-man + @cmp -s .version-readme .version-man && cat .version-man > version || { echo Inconsistant versions.; exit 1;} + +dist : test clean version + mkdir -p DIST + rm -f DIST/wiggle-`cat version` + ln -s .. DIST/wiggle-`cat version` + tar czvf DIST/wiggle-`cat version`.tar.gz -h -C DIST --exclude RCS --exclude DIST wiggle-`cat version` + rm -f DIST/wiggle-`cat version` + +v : version + cat version diff --git a/demo.orig/README b/demo.orig/README new file mode 100644 index 0000000..ccc629e --- /dev/null +++ b/demo.orig/README @@ -0,0 +1,49 @@ + +This is demo file for wiggles --browse mode. + +Browse mode is intended to let you look through a patch +to see how it will apply to a set of files. It is +possible that the patch will have some conflicts. +i.e. the patch was created from a different version of +the source to the version that you are applying the patch +to. This is what makes it interesting. + +You can use normal cursor motion to scroll around, +both vertially and horizontally. +From the initial file-list patch, use space +or to open/close a directory or file. +Form the file-view mode, use 'q' to get back to +to file list. + +Differences applied by the patch are shown as +RED for removal and BLUE for addition. +Text with a pink background was not matched - +maybe it has been changed since the patch was +created. + +green-background text is text that the patch wants +to change, but the exact correct change has already +been made. + +Captial-N might go to the next interesting chunk +of the file. + + +--------------------------------------- +This demonstrates where a diff on one line +applies to text that is now split + +The swift brown fox jumps over the lazy dog. + +------------ + +This demonstrates a diff which contains some +extraneous lines. +(this is an extra line) + +The swift brown fox jumps over the lazy dog. + +There will be extra lines in the diff +(as is this) + +-------------- diff --git a/demo.orig/vpatch.c b/demo.orig/vpatch.c new file mode 100644 index 0000000..d3bd584 --- /dev/null +++ b/demo.orig/vpatch.c @@ -0,0 +1,666 @@ + +/* + * vpatch - visual front end for wiggle + * + * "files" display, lists all files with statistics + * - can hide various lines including subdirectories + * and files without wiggles or conflicts + * "diff" display shows merged file with different parts + * in different colours + * - untouched are pale A_DIM + * - matched/remaining are regular A_NORMAL + * - matched/removed are red/underlined A_UNDERLINE + * - unmatched in file are A_STANDOUT + * - unmatched in patch are A_STANDOUT|A_UNDERLINE ??? + * - inserted are inverse/green ?? A_REVERSE + * + * The window can be split horiz or vert and two different + * views displayed. They will have different parts missing + * + * So a display of NORMAL, underline, standout|underline reverse + * should show a normal patch. + * + */ + +#include "wiggle.h" +#include +#include +#include +#include +#include +#include + +#define assert(x) do { if (!(x)) abort(); } while (0) + +struct plist { + char *file; + unsigned int start, end; + int parent; + int next, prev, last; + int open; + int chunks, wiggles, conflicts; +}; + +struct plist *patch_add_file(struct plist *pl, int *np, char *file, + unsigned int start, unsigned int end) +{ + /* size of pl is 0, 16, n^2 */ + int n = *np; + int asize; + +/* printf("adding %s at %d: %u %u\n", file, n, start, end); */ + if (n==0) asize = 0; + else if (n<=16) asize = 16; + else if ((n&(n-1))==0) asize = n; + else asize = n+1; /* not accurate, but not too large */ + if (asize <= n) { + /* need to extend array */ + struct plist *npl; + if (asize < 16) asize = 16; + else asize += asize; + npl = realloc(pl, asize * sizeof(struct plist)); + if (!npl) { + fprintf(stderr, "malloc failed - skipping %s\n", file); + return pl; + } + pl = npl; + } + pl[n].file = file; + pl[n].start = start; + pl[n].end = end; + pl[n].last = pl[n].next = pl[n].prev = pl[n].parent = -1; + pl[n].chunks = pl[n].wiggles = pl[n].conflicts = 0; + pl[n].open = 1; + *np = n+1; + return pl; +} + + + +struct plist *parse_patch(FILE *f, FILE *of, int *np) +{ + /* read a multi-file patch from 'f' and record relevant + * details in a plist. + * if 'of' >= 0, fd might not be seekable so we write + * to 'of' and use lseek on 'of' to determine position + */ + struct plist *plist = NULL; + + while (!feof(f)) { + /* first, find the start of a patch: "\n+++ " + * grab the file name and scan to the end of a line + */ + char *target="\n+++ "; + char *target2="\n--- "; + char *pos = target; + int c; + char name[1024]; + unsigned start, end; + + while (*pos && (c=fgetc(f)) != EOF ) { + if (of) fputc(c, of); + if (c == *pos) + pos++; + else pos = target; + } + if (c == EOF) + break; + assert(c == ' '); + /* now read a file name */ + pos = name; + while ((c=fgetc(f)) != EOF && c != '\t' && c != '\n' && c != ' ' && + pos - name < 1023) { + *pos++ = c; + if (of) fputc(c, of); + } + *pos = 0; + if (c == EOF) + break; + if (of) fputc(c, of); + while (c != '\n' && (c=fgetc(f)) != EOF) { + if (of) fputc(c, of); + } + start = of ? ftell(of) : ftell(f); + + if (c == EOF) break; + + /* now skip to end - "\n--- " */ + pos = target2+1; + + while (*pos && (c=fgetc(f)) != EOF) { + if (of) fputc(c, of); + if (c == *pos) + pos++; + else pos = target2; + } + if (pos > target2) { + end = of ? ftell(of) : ftell(f); + end -= (pos - target2) - 1; + plist = patch_add_file(plist, np, + strdup(name), start, end); + } + } + return plist; +} +void die() +{ + fprintf(stderr,"vpatch: fatal error\n"); + abort(); + exit(3); +} + + +static struct stream load_segment(FILE *f, + unsigned int start, unsigned int end) +{ + struct stream s; + s.len = end - start; + s.body = malloc(s.len); + if (s.body) { + fseek(f, start, 0); + if (fread(s.body, 1, s.len, f) != s.len) { + free(s.body); + s.body = NULL; + } + } else + die(); + return s; +} + + +void catch(int sig) +{ + if (sig == SIGINT) { + signal(sig, catch); + return; + } + nocbreak();nl();endwin(); + printf("Died on signal %d\n", sig); + exit(2); +} + +int pl_cmp(const void *av, const void *bv) +{ + const struct plist *a = av; + const struct plist *b = bv; + return strcmp(a->file, b->file); +} + +int common_depth(char *a, char *b) +{ + /* find number of patch segments that these two have + * in common + */ + int depth = 0; + while(1) { + char *c; + int al, bl; + c = strchr(a, '/'); + if (c) al = c-a; else al = strlen(a); + c = strchr(b, '/'); + if (c) bl = c-b; else bl = strlen(b); + if (al == 0 || al != bl || strncmp(a,b,al) != 0) + return depth; + a+= al; + while (*a=='/') a++; + b+= bl; + while(*b=='/') b++; + + depth++; + } +} + +struct plist *add_dir(struct plist *pl, int *np, char *file, char *curr) +{ + /* any parent of file that is not a parent of curr + * needs to be added to pl + */ + int d = common_depth(file, curr); + char *buf = curr; + while (d) { + char *c = strchr(file, '/'); + int l; + if (c) l = c-file; else l = strlen(file); + file += l; + curr += l; + while (*file == '/') file++; + while (*curr == '/') curr++; + d--; + } + while (*file) { + if (curr > buf && curr[-1] != '/') + *curr++ = '/'; + while (*file && *file != '/') + *curr++ = *file++; + while (*file == '/') *file++; + *curr = '\0'; + if (*file) + pl = patch_add_file(pl, np, strdup(buf), + 0, 0); + } + return pl; +} + +struct plist *sort_patches(struct plist *pl, int *np) +{ + /* sort the patches, add directory names, and re-sort */ + char curr[1024]; + char *prev; + int parents[100]; + int prevnode[100]; + int i, n; + qsort(pl, *np, sizeof(struct plist), pl_cmp); + curr[0] = 0; + n = *np; + for (i=0; i -1) + pl[pl[i].prev].next = i; + prev = pl[i].file; + parents[d] = i; + prevnode[d] = i; + prevnode[d+1] = -1; + } + return pl; +} + +int get_prev(int pos, struct plist *pl, int n) +{ + if (pos == -1) return pos; + if (pl[pos].prev == -1) + return pl[pos].parent; + pos = pl[pos].prev; + while (pl[pos].open && + pl[pos].last >= 0) + pos = pl[pos].last; + return pos; +} + +int get_next(int pos, struct plist *pl, int n) +{ + if (pos == -1) return pos; + if (pl[pos].open) { + if (pos +1 < n) + return pos+1; + else + return -1; + } + while (pos >= 0 && pl[pos].next == -1) + pos = pl[pos].parent; + if (pos >= 0) + pos = pl[pos].next; + return pos; +} + +void draw_one(int row, struct plist *pl) +{ + char hdr[10]; + hdr[0] = 0; + + if (pl == NULL) { + move(row,0); + clrtoeol(); + return; + } + if (pl->chunks > 99) + strcpy(hdr, "XX"); + else sprintf(hdr, "%02d", pl->chunks); + if (pl->wiggles > 99) + strcpy(hdr, " XX"); + else sprintf(hdr+2, " %02d", pl->wiggles); + if (pl->conflicts > 99) + strcpy(hdr, " XX"); + else sprintf(hdr+5, " %02d ", pl->conflicts); + if (pl->end) + strcpy(hdr+9, "= "); + else if (pl->open) + strcpy(hdr+9, "+ "); + else strcpy(hdr+9, "- "); + + mvaddstr(row, 0, hdr); + mvaddstr(row, 11, pl->file); + clrtoeol(); +} + +void addword(struct elmnt e) +{ + addnstr(e.start, e.len); +} + +void diff_window(struct plist *p, FILE *f) +{ + /* + * I wonder what to display here .... + */ + struct stream s; + struct stream s1, s2; + struct file f1, f2; + struct csl *csl; + char buf[100]; + int ch; + s = load_segment(f, p->start, p->end); + ch = split_patch(s, &s1, &s2); + + clear(); + sprintf(buf, "Chunk count: %d\n", ch); + mvaddstr(1,1,buf); clrtoeol(); + + + f1 = split_stream(s1, ByWord, 0); + f2 = split_stream(s2, ByWord, 0); + + csl = diff(f1, f2); + + /* now try to display the diff highlighted */ + int sol = 1; + int a=0, b=0; + + while(aa) { + if (sol) { + int a1; + /* if we remove a whole line, output +line, + * else clear sol and retry + */ + sol = 0; + for (a1=a; a1a; a1++) + if (f1.list[a1].start[0] == '\n') { + sol = 1; + break; + } + if (sol) { + addch('-'); + attron(A_UNDERLINE); + for (; aa; a++) { + addword(f1.list[a]); + if (f1.list[a].start[0] == '\n') { + a++; + break; + } + } + attroff(A_UNDERLINE); + } else addch('|'); + } + if (!sol) { + attron(A_UNDERLINE); + do { + if (sol) { + attroff(A_UNDERLINE); + addch('|'); + attron(A_UNDERLINE); + } + addword(f1.list[a]); + sol = (f1.list[a].start[0] == '\n'); + a++; + } while (a < csl->a); + attroff(A_UNDERLINE); + if (sol) addch('|'); + sol = 0; + } + } else if (b < csl->b) { + if (sol) { + int b1; + sol = 0; + for (b1=b; b1b; b1++) + if (f2.list[b1].start[0] == '\n') { + sol = 1; + break; + } + if (sol) { + addch('+'); + attron(A_BOLD); + for (; bb; b++) { + addword(f2.list[b]); + if (f2.list[b].start[0] == '\n') { + b++; + break; + } + } + attroff(A_BOLD); + } else addch('|'); + } + if (!sol) { + attron(A_BOLD); + do { + if (sol) { + attroff(A_BOLD); + addch('|'); + attron(A_BOLD); + } + addword(f2.list[b]); + sol = (f2.list[b].start[0] == '\n'); + b++; + } while (b < csl->b); + attroff(A_BOLD); + if (sol) addch('|'); + sol = 0; + } + } else { + if (sol) { + int a1; + sol = 0; + for (a1=a; a1a+csl->len; a1++) + if (f1.list[a1].start[0] == '\n') + sol = 1; + if (sol) { + if (f1.list[a].start[0]) { + addch(' '); + for (; a< csl->a+csl->len; a++,b++) { + addword(f1.list[a]); + if (f1.list[a].start[0]=='\n') { + a++,b++; + break; + } + } + } else { + addstr("SEP\n"); + a++; b++; + } + } else addch('|'); + } + if (!sol) { + addword(f1.list[a]); + if (f1.list[a].start[0] == '\n') + sol = 1; + a++; + b++; + } + if (a >= csl->a+csl->len) + csl++; + } + } + + + getch(); + + free(s1.body); + free(s2.body); + free(f1.list); + free(f2.list); +} + +void main_window(struct plist *pl, int n, FILE *f) +{ + /* The main window lists all files together with summary information: + * number of chunks, number of wiggles, number of conflicts. + * The list is scrollable + * When a entry is 'selected', we switch to the 'file' window + * The list can be condensed by removing files with no conflict + * or no wiggles, or removing subdirectories + * + * We record which file in the list is 'current', and which + * screen line it is on. We try to keep things stable while + * moving. + * + * Counts are printed before the name using at most 2 digits. + * Numbers greater than 99 are XX + * Ch Wi Co File + * 27 5 1 drivers/md/md.c + * + * A directory show the sum in all children. + * + * Commands: + * select: enter, space, mouseclick + * on file, go to file window + * on directory, toggle open + * up: k, p, control-p uparrow + * Move to previous open object + * down: j, n, control-n, downarrow + * Move to next open object + * + */ + int pos=0; /* position in file */ + int row=1; /* position on screen */ + int rows; /* size of screen in rows */ + int cols; + int tpos, i; + int refresh = 2; + int c; + + while(1) { + if (refresh == 2) { + clear(); + attron(A_BOLD); + mvaddstr(0,0,"Ch Wi Co Patched Files"); + move(2,0); + attroff(A_BOLD); + refresh = 1; + } + if (row <1 || row >= rows) + refresh = 1; + if (refresh) { + refresh = 0; + getmaxyx(stdscr, rows, cols); + if (row >= rows +3) + row = (rows+1)/2; + if (row >= rows) + row = rows-1; + tpos = pos; + for (i=row; i>1; i--) { + tpos = get_prev(tpos, pl, n); + if (tpos == -1) { + row = row - i + 1; + break; + } + } + /* Ok, row and pos could be trustworthy now */ + tpos = pos; + for (i=row; i>=1; i--) { + draw_one(i, &pl[tpos]); + tpos = get_prev(tpos, pl, n); + } + tpos = pos; + for (i=row+1; i= 0) + draw_one(i, &pl[tpos]); + else + draw_one(i, NULL); + } + } + move(row, 9); + c = getch(); + switch(c) { + case 'j': + case 'n': + case 'N': + case 'N'-64: + case KEY_DOWN: + tpos = get_next(pos, pl, n); + if (tpos >= 0) { + pos = tpos; + row++; + } + break; + case 'k': + case 'p': + case 'P': + case 'P'-64: + case KEY_UP: + tpos = get_prev(pos, pl, n); + if (tpos >= 0) { + pos = tpos; + row--; + } + break; + + case ' ': + case 13: + if (pl[pos].end == 0) { + pl[pos].open = ! pl[pos].open; + refresh = 1; + } else { + diff_window(&pl[pos], f); + refresh = 2; + } + break; + case 27: /* escape */ + case 'q': + return; + } + } +} + + +int main(int argc, char *argv[]) +{ + int n = 0; + FILE *f = NULL; + FILE *in = stdin; + struct plist *pl; + + if (argc == 3) + f = fopen(argv[argc-1], "w+"); + if (argc >=2) + in = fopen(argv[1], "r"); + else { + printf("no arg...\n"); + exit(2); + } + + pl = parse_patch(in, f, &n); + pl = sort_patches(pl, &n); + + if (f) { + fclose(in); + in = f; + } +#if 0 + int i; + for (i=0; i + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +/* + * Wiggle is a tool for working with patches that don't quite apply properly. + * It provides functionality similar to 'diff' and 'merge' but can + * work at the level of individual words thus allowing the merging of + * two changes that affect the same line, but not the same parts of that line. + * + * Wiggle can also read patch and merge files. Unlike 'merge' it does not + * need to be given three separate files, but can be given a file and a patch + * and it will extract the pieces of the two two other files that it needs from + * the patch. + * + * Wiggle performs one of three core function: + * --extract -x extract part of a patch or merge file + * --diff -d report differences between two files + * --merge -m merge the changes between two files into a third file + * + * To perform these, wiggle requires 1, 2, or 3 input streams respectively. + * I can get there from individual files, from a diff (unified or context) or + * from a merge file. + * + * For merge: + * If one file is given, it is a merge file (output of 'merge'). + * If two files are given, the second is assumed to be a patch, the first is a normal file. + * If three files are given, they are taken to be normal files. + * + * For diff: + * If one file is given, it is a patch + * If two files are given, they are normal files. + * + * For extract: + * Only one file can be given. -p indicates it is a patch, otherwise it is a merge. + * One of the flags -1 -2 or -3 must also be given and they indicate which + * part of the patch or merge to extract. + * + * Difference calculate and merging is performed on lines (-l) or words (-w). + * In the case of -w, an initial diff is computed based on non-trivial words. + * i.e. spaces are ignored + * This diff is computed from the ends of the file and is used to find a suitable + * starting point and range. Then a more precise diff is computed over that + * restricted range + * + * Other options available are: + * --replace -r replace first file with result of merge. + * --help -h provide help + * --version -v version + * + * Defaults are --merge --words + * + */ + +#include "wiggle.h" +#include +#include +#include +#include +#include +#include + +void die() +{ + fprintf(stderr,"wiggle: fatal error\n"); + abort(); + exit(3); +} + +void printword(FILE *f, struct elmnt e) +{ + if (e.start[0]) + fprintf(f, "%.*s", e.len, e.start); + else { + int a,b,c; + sscanf(e.start+1, "%d %d %d", &a, &b, &c); + fprintf(f, "*** %d,%d **** %d\n", b,c,a); + } +} + +static void printsep(struct elmnt e1, struct elmnt e2) +{ + int a,b,c,d,e,f; + sscanf(e1.start+1, "%d %d %d", &a, &b, &c); + sscanf(e2.start+1, "%d %d %d", &d, &e, &f); + printf("@@ -%d,%d +%d,%d @@\n", b,c,e,f); +} + + +/* Remove any entries from the common-sublist that are + * just spaces, tabs, or newlines + */ +void cleanlist(struct file a, struct file b, struct csl *list) +{ + struct csl *new = list; + + while (list->len) { + int i; + int ap; + for( ap = list->a; ap< list->a+list->len; ap++) { + for (i=0; ia+list->len) + list++; + else + *new++ = *list++; + } + *new = *list; +} + +int main(int argc, char *argv[]) +{ + int opt; + int option_index; + int mode = 0; + int obj = 0; + int replace = 0; + char *replacename=NULL, *orignew=NULL; + int which = 0; + int ispatch = 0; + int reverse = 0; + int verbose=0, quiet=0; + int i; + int chunks1=0, chunks2=0, chunks3=0; + int exit_status = 0; + FILE *outfile = stdout; + char *helpmsg; + + struct stream f, flist[3]; + struct file fl[3]; + struct csl *csl1, *csl2; + + while ((opt = getopt_long(argc, argv, + short_options, long_options, + &option_index)) != -1) + switch(opt) { + case 'h': + helpmsg = Help; + switch(mode) { + case 'x': helpmsg = HelpExtract; break; + case 'd': helpmsg = HelpDiff; break; + case 'm': helpmsg = HelpMerge; break; + } + fputs(helpmsg, stderr); + exit(0); + + case 'V': + fputs(Version, stderr); + exit(0); + case ':': + case '?': + default: + fputs(Usage, stderr); + exit(2); + + case 'x': + case 'd': + case 'm': + if (mode ==0){ + mode = opt; + continue; + } + fprintf(stderr, "wiggle: mode is '%c' - cannot set to '%c'\n", + mode, opt); + exit(2); + + case 'w': + case 'l': + if (obj == 0 || obj == opt) { + obj = opt; + continue; + } + fprintf(stderr, "wiggle: cannot select both words and lines.\n"); + exit(2); + + case 'r': + replace = 1; + continue; + case 'R': + reverse = 1; + continue; + + case '1': + case '2': + case '3': + if (which == 0 || which == opt) { + which = opt; + continue; + } + fprintf(stderr, "wiggle: can only select one of -1, -2, -3\n"); + exit(2); + + case 'p': + ispatch = 1; + continue; + + case 'v': verbose++; continue; + case 'q': quiet=1 ; continue; + } + if (!mode) + mode = 'm'; + + if (obj && mode == 'x') { + fprintf(stderr,"wiggle: cannot specify --line or --word with --extract\n"); + exit(2); + } + if (mode != 'm' && !obj) obj = 'w'; + if (replace && mode != 'm') { + fprintf(stderr, "wiggle: --replace only allowed with --merge\n"); + exit(2); + } + if (mode == 'x' && !which) { + fprintf(stderr, "wiggle: must specify -1, -2 or -3 with --extract\n"); + exit(2); + } + if (mode != 'x' && mode != 'd' && which) { + fprintf(stderr, "wiggle: -1, -2 or -3 only allowed with --extract or --diff\n"); + exit(2); + } + if (ispatch && (mode != 'x' && mode != 'd')) { + fprintf(stderr, "wiggle: --patch only allowed with --extract or --diff\n"); + exit(2); + } + if (ispatch && which == '3') { + fprintf(stderr, "wiggle: cannot extract -3 from a patch.\n"); + exit(2); + } + + switch(mode) { + case 'x': + /* extract a branch of a diff or diff3 or merge output + * We need one file + */ + if (optind == argc) { + fprintf(stderr, "wiggle: no file given for --extract\n"); + exit(2); + } + if (optind < argc-1) { + fprintf(stderr, "wiggle: only give one file for --extract\n"); + exit(2); + } + f = load_file(argv[optind]); + if (f.body==NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind], strerror(errno)); + exit(2); + } + if (ispatch) + chunks1 = chunks2 = split_patch(f, &flist[0], &flist[1]); + else { + if (!split_merge(f, &flist[0], &flist[1], &flist[2])) { + fprintf(stderr, "wiggle: merge file %s looks bad.\n", + argv[optind]); + exit(2); + } + } + if (flist[which-'1'].body == NULL) { + fprintf(stderr, "wiggle: %s has no -%c component.\n", + argv[optind], which); + exit(2); + } else { + write(1, flist[which-'1'].body, flist[which-'1'].len); + } + + break; + case 'd': + /* create a diff (line or char) of two streams */ + switch (argc-optind) { + case 0: + fprintf(stderr, "wiggle: no file given for --diff\n"); + exit(2); + case 1: + f = load_file(argv[optind]); + if (f.body == NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind], strerror(errno)); + exit(2); + } + chunks1 = chunks2 = split_patch(f, &flist[0], &flist[1]); + if (!flist[0].body || !flist[1].body) { + fprintf(stderr, "wiggle: couldn't parse patch %s\n", + argv[optind]); + exit(2); + } + break; + case 2: + flist[0] = load_file(argv[optind]); + if (flist[0].body == NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind], strerror(errno)); + exit(2); + } + if (ispatch) { + f = load_file(argv[optind+1]); + if (f.body == NULL) { + fprintf(stderr, "wiggle: cannot load patch '%s' - %s\n", + argv[optind], strerror(errno)); + exit(2); + } + if (which == '2') + chunks2 = chunks3 = split_patch(f, &flist[2], &flist[1]); + else + chunks2 = chunks3 = split_patch(f, &flist[1], &flist[2]); + + } else + flist[1] = load_file(argv[optind+1]); + if (flist[1].body == NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind+1], strerror(errno)); + exit(2); + } + break; + default: + fprintf(stderr, "wiggle: too many files given for --diff\n"); + exit(2); + } + if (reverse) { + f=flist[1]; + flist[1] = flist[2]; + flist[2]= f; + } + if (obj == 'l') { + int a,b; + fl[0] = split_stream(flist[0], ByLine, 0); + fl[1] = split_stream(flist[1], ByLine, 0); + if (chunks2 && ! chunks1) + csl1 = pdiff(fl[0], fl[1], chunks2); + else + csl1 = diff(fl[0], fl[1]); + + if (!chunks1) + printf("@@ -1,%d +1,%d @@\n", fl[0].elcnt, fl[1].elcnt); + a = b = 0; + while (aa) { + if (fl[0].list[a].start[0]) { + printf("-"); + printword(stdout, fl[0].list[a]); + } + a++; + exit_status++; + } else if (b < csl1->b) { + if (fl[1].list[b].start[0]) { + printf("+"); + printword(stdout, fl[1].list[b]); + } + b++; + exit_status++; + } else { + if (fl[0].list[a].start[0] == '\0') + printsep(fl[0].list[a], fl[1].list[b]); + else { + printf(" "); + printword(stdout, fl[0].list[a]); + } + a++; + b++; + if (a >= csl1->a+csl1->len) + csl1++; + } + } + } else { + int a,b; + int sol = 1; /* start of line */ + fl[0] = split_stream(flist[0], ByWord, 0); + fl[1] = split_stream(flist[1], ByWord, 0); + if (chunks2 && !chunks1) + csl1 = pdiff(fl[0], fl[1], chunks2); + else + csl1 = diff(fl[0], fl[1]); + + if (!chunks1) { + /* count lines in each file */ + int l1, l2, i; + l1=l2=0; + for (i=0;ia) { + exit_status++; + if (sol) { + int a1; + /* If we remove a whole line, output +line + * else clear sol and retry */ + sol = 0; + for (a1=a; a1a;a1++) + if (ends_line(fl[0].list[a1])) { + sol=1; + break; + } + if (sol) { + printf("-"); + for (; aa; a++) { + printword(stdout, fl[0].list[a]); + if (ends_line(fl[0].list[a])) { + a++; + break; + } + } + } else printf("|"); + } + if (!sol) { + printf("<<<--"); + do { + if (sol) printf("|"); + printword(stdout, fl[0].list[a]); + sol = ends_line(fl[0].list[a]); + a++; + } while (a < csl1->a); + printf("%s-->>>", sol?"|":""); + sol=0; + } + } else if (b < csl1->b) { + exit_status++; + if (sol) { + int b1; + sol = 0; + for (b1=b; b1b;b1++) + if(ends_line(fl[1].list[b1])) { + sol=1; + break; + } + if (sol) { + printf("+"); + for(; bb ; b++) { + printword(stdout, fl[1].list[b]); + if(ends_line(fl[1].list[b])) { + b++; + break; + } + } + } else printf("|"); + } + if (!sol) { + printf("<<<++"); + do { + if (sol) printf("|"); + printword(stdout, fl[1].list[b]); + sol = ends_line(fl[1].list[b]); + b++; + } while (b < csl1->b); + printf("%s++>>>",sol?"|":""); + sol=0; + } + } else { + if (sol) { + int a1; + sol = 0; + for (a1=a; a1a+csl1->len; a1++) + if (ends_line(fl[0].list[a1])) + sol=1; + if (sol) { + if (fl[0].list[a].start[0]) { + printf(" "); + for(; aa+csl1->len; a++,b++) { + printword(stdout, fl[0].list[a]); + if (ends_line(fl[0].list[a])) { + a++,b++; + break; + } + } + } else { + printsep(fl[0].list[a], fl[1].list[b]); + a++; b++; + } + } + else printf("|"); + } + if (!sol) { + printword(stdout, fl[0].list[a]); + if (ends_line(fl[0].list[a])) + sol=1; + a++; + b++; + } + if (a >= csl1->a+csl1->len) + csl1++; + } + } + + } + break; + case 'm': + /* merge three files, A B C, so changed between B and C get made to A + */ + switch (argc-optind) { + case 0: + fprintf(stderr, "wiggle: no files given for --merge\n"); + exit(2); + case 3: + case 2: + case 1: + for (i=0; i< argc-optind; i++) { + flist[i] = load_file(argv[optind+i]); + if (flist[i].body == NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind+i], strerror(errno)); + exit(2); + } + } + break; + default: + fprintf(stderr, "wiggle: too many files given for --merge\n"); + exit(2); + } + switch(argc-optind) { + case 1: /* a merge file */ + f = flist[0]; + if (!split_merge(f, &flist[0], &flist[1], &flist[2])) { + fprintf(stderr,"wiggle: merge file %s looks bad.\n", + argv[optind]); + exit(2); + } + break; + case 2: /* a file and a patch */ + f = flist[1]; + chunks2 = chunks3 = split_patch(f, &flist[1], &flist[2]); + break; + case 3: /* three separate files */ + break; + } + if (reverse) { + f=flist[1]; + flist[1] = flist[2]; + flist[2]= f; + } + + for (i=0; i<3; i++) { + if (flist[i].body==NULL) { + fprintf(stderr, "wiggle: file %d missing\n", i); + exit(2); + } + } + if (replace) { + int fd; + replacename = malloc(strlen(argv[optind])+ 20); + if (!replacename) die(); + orignew = malloc(strlen(argv[optind])+20); + if (!orignew) die(); + strcpy(replacename, argv[optind]); + strcpy(orignew, argv[optind]); + strcat(orignew, ".porig"); + if (open(orignew, O_RDONLY) >= 0 || + errno != ENOENT) { + fprintf(stderr,"wiggle: %s already exists\n", + orignew); + exit(2); + } + strcat(replacename,"XXXXXX"); + fd = mkstemp(replacename); + if (fd == -1) { + fprintf(stderr,"wiggle: could not create temporary file for %s\n", + replacename); + exit(2); + } + outfile = fdopen(fd, "w"); + + } + + if (obj == 'l') { + fl[0] = split_stream(flist[0], ByLine, 0); + fl[1] = split_stream(flist[1], ByLine, 0); + fl[2] = split_stream(flist[2], ByLine, 0); + } else { + fl[0] = split_stream(flist[0], ByWord, 0); + fl[1] = split_stream(flist[1], ByWord, 0); + fl[2] = split_stream(flist[2], ByWord, 0); + } + if (chunks2 && !chunks1) + csl1 = pdiff(fl[0], fl[1], chunks2); + else + csl1 = diff(fl[0], fl[1]); + csl2 = diff(fl[1], fl[2]); + +#if 0 + cleanlist(fl[0],fl[1],csl1); + cleanlist(fl[1],fl[2],csl2); +#endif + + { + struct ci ci; + + ci = print_merge(outfile, &fl[0], &fl[1], &fl[2], + csl1, csl2, obj=='w'); + if (!quiet && ci.conflicts) + fprintf(stderr, "%d unresolved conflict%s found\n", ci.conflicts, ci.conflicts==1?"":"s"); + if (!quiet && ci.ignored) + fprintf(stderr, "%d already-applied change%s ignored\n", ci.ignored, ci.ignored==1?"":"s"); + exit_status = (ci.conflicts > 0); + } + if (replace) { + fclose(outfile); + if (rename(argv[optind], orignew) ==0 && + rename(replacename, argv[optind]) ==0) + /* all ok */; + else { + fprintf(stderr, "wiggle: failed to move new file into place.\n"); + exit(2); + } + } + break; + + } + exit(exit_status); +} diff --git a/demo.patched/Makefile b/demo.patched/Makefile new file mode 100644 index 0000000..c7d827e --- /dev/null +++ b/demo.patched/Makefile @@ -0,0 +1,56 @@ + +# Note on my Mobile Pentium II, -march=pentium2 delivers twice the performance of i386 +#OptDbg=-O3 +#OptDbg=-O3 -march=pentium2 +OptDbg=-ggdb +CFLAGS=$(OptDbg) -Wall -Werror + +# STRIP = -s +INSTALL = /usr/bin/install +DESTDIR = +BINDIR = /usr/bin +MANDIR = /usr/share/man +MAN1DIR = $(MANDIR)/man1 +MAN5DIR = $(MANDIR)/man5 +LDLIBS=-lncurses + +all: wiggle wiggle.man test + +vpatch : vpatch.o extract.o split.o diff.o + + +wiggle : wiggle.o load.o split.o extract.o diff.o bestmatch.o ReadMe.o merge.o +wiggle.o load.o split.o extract.o diff.o bestmatch.o ReadMe.o merge.o : wiggle.h + +test: wiggle dotest + sh dotest + +wiggle.man : wiggle.1 + nroff -man wiggle.1 > wiggle.man + +clean: + rm -f *.o *.man wiggle .version* version + find . -name core -o -name '*.tmp*' -o -name .tmp | xargs rm -f + +install : wiggle wiggle.1 + $(INSTALL) -D $(STRIP) -m 755 wiggle $(DESTDIR)$(BINDIR)/wiggle + $(INSTALL) -D -m 644 wiggle.1 $(DESTDIR)$(MAN1DIR)/wiggle.1 + +version : wiggle.1 + @rm -f version + @sed -n -e 's/.*wiggle - v\([0-9.]*\) - .*/\1/p' ReadMe.c > .version-readme + @sed -n -e 's/.*WIGGLE 1 "" v\([0-9.]*\)$$/\1/p' wiggle.1 > .version-man + @cmp -s .version-readme .version-man && cat .version-man > version || { echo Inconsistant versions.; exit 1;} + +dist : clean version + mkdir -p DIST + rm -f DIST/wiggle-`cat version` + ln -s .. DIST/wiggle-`cat version` + tar czvf DIST/wiggle-`cat version`.tar.gz -h -C DIST --exclude RCS --exclude DIST wiggle-`cat version` + rm -f DIST/wiggle-`cat version` + +v : version + cat version + +demo.patch: + diff -ru demo.orig demo.patched | sed 's/demo.patched/demo/' > demo.patch diff --git a/demo.patched/README b/demo.patched/README new file mode 100644 index 0000000..d08848f --- /dev/null +++ b/demo.patched/README @@ -0,0 +1,58 @@ + +This is demo file for wiggle's --browse mode. + +Browse mode is intended to let you look through a patch +to see how it will apply to a set of files. It is +possible that the patch will have some conflicts. +That is: the patch was created from a different version of +the source to the version that you are applying the patch +to. This is what makes it interesting. + +You can use normal cursor motion to scroll around, +both vertially and horizontally (both emacs and vi style). +From the initial file-list patch, use space +or to open/close a directory or file. +Form the file-view mode, use 'q' to get back to +to file list. + +Differences applied by the patch are shown as +RED for removal and BLUE for addition. +Text with a pink background was not matched - +maybe it has been changed since the patch was +created. + +green-background text is text that the patch wants +to change, but the exact correct change has already +been made. + +Captial-N might go to the next interesting chunk +of the file. + +You can use 'o' and 'r' to move between showing the +original and result file in the left window. +An intermediate shows both. + +You can use 'b' and 'a' to move between showing the +before and after veiws of the patch in the right +window. Again, an intermediate shows both. + +Have fun. + +--------------------------------------- +This demonstrates where a diff on one line +applies to text that is now split + +The quick brown fox jumps over the lazy hound. + +------------ + +This demonstrates a diff which contains some +extraneous lines. +(this is an extra line) + +The quick brown fox jumps over the lazy cat. + +There will be extra lines in the diff +(as is this) + +-------------- diff --git a/demo.patched/vpatch.c b/demo.patched/vpatch.c new file mode 100644 index 0000000..1746ab0 --- /dev/null +++ b/demo.patched/vpatch.c @@ -0,0 +1,667 @@ + +/* + * vpatch - visual front end for wiggle + * + * "files" Display, lists all files with statistics + * - can hide various lines including subdirectories + * and files without wiggles or conflicts + * "diff" display shows merged file with different parts + * in different colours + * - untouched are pale A_DIM + * - matched/remaining are regular A_NORMAL + * - matched/removed are red/underlined A_UNDERLINE + * - unmatched in file are A_STANDOUT + * - unmatched in patch are A_STANDOUT|A_UNDERLINE ??? + * - inserted are inverse/green ?? A_REVERSE + * + * The window can be split horizontally or vertically and + * two different views displayed. They will have different + * parts missing + * + * So a display of NORMAL, underline, standout|underline reverse + * should show a normal patch. + * + */ + +#include "wiggle.h" +#include +#include +#include +#include +#include +#include + +#define assert(x) do { if (!(x)) abort(); } while (0) + +struct plist { + char *file; + unsigned int start, end; + int parent; + int next, prev, last; + int open; + int chunks, wiggles, conflicts; +}; + +struct plist *patch_add_file(struct plist *pl, int *np, char *file, + unsigned int start, unsigned int end) +{ + /* size of pl is 0, 16, n^2 */ + int n = *np; + int asize; + +/* printf("adding %s at %d: %u %u\n", file, n, start, end); */ + if (n==0) asize = 0; + else if (n<=16) asize = 16; + else if ((n&(n-1))==0) asize = n; + else asize = n+1; /* not accurate, but not too large */ + if (asize <= n) { + /* need to extend array */ + struct plist *npl; + if (asize < 16) asize = 16; + else asize += asize; + npl = realloc(pl, asize * sizeof(struct plist)); + if (!npl) { + fprintf(stderr, "malloc failed - skipping %s\n", file); + return pl; + } + pl = npl; + } + pl[n].file = file; + pl[n].start = start; + pl[n].end = end; + pl[n].last = pl[n].next = pl[n].prev = pl[n].parent = -1; + pl[n].chunks = pl[n].wiggles = pl[n].conflicts = 0; + pl[n].open = 1; + *np = n+1; + return pl; +} + + + +struct plist *parse_patch(FILE *f, FILE *of, int *np) +{ + /* read a multi-file patch from 'f' and record relevant + * details in a plist. + * if 'of' >= 0, fd might not be seekable so we write + * to 'of' and use lseek on 'of' to determine position + */ + struct plist *plist = NULL; + + while (!feof(f)) { + /* first, find the start of a patch: "\n+++ " + * grab the file name and scan to the end of a line + */ + char *target="\n+++ "; + char *target2="\n--- "; + char *pos = target; + int c; + char name[1024]; + unsigned start, end; + + while (*pos && (c=fgetc(f)) != EOF ) { + if (of) fputc(c, of); + if (c == *pos) + pos++; + else pos = target; + } + if (c == EOF) + break; + assert(c == ' '); + /* now read a file name */ + pos = name; + while ((c=fgetc(f)) != EOF && c != '\t' && c != '\n' && c != ' ' && + pos - name < 1023) { + *pos++ = c; + if (of) fputc(c, of); + } + *pos = 0; + if (c == EOF) + break; + if (of) fputc(c, of); + while (c != '\n' && (c=fgetc(f)) != EOF) { + if (of) fputc(c, of); + } + start = of ? ftell(of) : ftell(f); + + if (c == EOF) break; + + /* now skip to end - "\n--- " */ + pos = target2+1; + + while (*pos && (c=fgetc(f)) != EOF) { + if (of) fputc(c, of); + if (c == *pos) + pos++; + else pos = target2; + } + if (pos > target2) { + end = of ? ftell(of) : ftell(f); + end -= (pos - target2) - 1; + plist = patch_add_file(plist, np, + strdup(name), start, end); + } + } + return plist; +} +void die() +{ + fprintf(stderr,"vpatch: fatal error\n"); + abort(); + exit(3); +} + + +static struct stream load_segment(FILE *f, + unsigned int start, unsigned int end) +{ + struct stream s; + s.len = end - start; + s.body = malloc(s.len); + if (s.body) { + fseek(f, start, 0); + if (fread(s.body, 1, s.len, f) != s.len) { + free(s.body); + s.body = NULL; + } + } else + die(); + return s; +} + + +void catch(int sig) +{ + if (sig == SIGINT) { + signal(sig, catch); + return; + } + nocbreak();nl();endwin(); + printf("Died on signal %d\n", sig); + exit(2); +} + +int pl_cmp(const void *av, const void *bv) +{ + const struct plist *a = av; + const struct plist *b = bv; + return strcmp(a->file, b->file); +} + +int common_depth(char *a, char *b) +{ + /* find number of patch segments that these two have + * in common + */ + int depth = 0; + while(1) { + char *c; + int al, bl; + c = strchr(a, '/'); + if (c) al = c-a; else al = strlen(a); + c = strchr(b, '/'); + if (c) bl = c-b; else bl = strlen(b); + if (al == 0 || al != bl || strncmp(a,b,al) != 0) + return depth; + a+= al; + while (*a=='/') a++; + b+= bl; + while(*b=='/') b++; + + depth++; + } +} + +struct plist *add_dir(struct plist *pl, int *np, char *file, char *curr) +{ + /* any parent of file that is not a parent of curr + * needs to be added to pl + */ + int d = common_depth(file, curr); + char *buf = curr; + while (d) { + char *c = strchr(file, '/'); + int l; + if (c) l = c-file; else l = strlen(file); + file += l; + curr += l; + while (*file == '/') file++; + while (*curr == '/') curr++; + d--; + } + while (*file) { + if (curr > buf && curr[-1] != '/') + *curr++ = '/'; + while (*file && *file != '/') + *curr++ = *file++; + while (*file == '/') *file++; + *curr = '\0'; + if (*file) + pl = patch_add_file(pl, np, strdup(buf), + 0, 0); + } + return pl; +} + +struct plist *sort_patches(struct plist *pl, int *np) +{ + /* sort the patches, add directory names, and re-sort */ + char curr[1024]; + char *prev; + int parents[100]; + int prevnode[100]; + int i, n; + qsort(pl, *np, sizeof(struct plist), pl_cmp); + curr[0] = 0; + n = *np; + for (i=0; i -1) + pl[pl[i].prev].next = i; + prev = pl[i].file; + parents[d] = i; + prevnode[d] = i; + prevnode[d+1] = -1; + } + return pl; +} + +int get_prev(int pos, struct plist *pl, int n) +{ + if (pos == -1) return pos; + if (pl[pos].prev == -1) + return pl[pos].parent; + pos = pl[pos].prev; + while (pl[pos].open && + pl[pos].last >= 0) + pos = pl[pos].last; + return pos; +} + +int get_next(int pos, struct plist *pl, int n) +{ + if (pos == -1) return pos; + if (pl[pos].open) { + if (pos +1 < n) + return pos+1; + else + return -1; + } + while (pos >= 0 && pl[pos].next == -1) + pos = pl[pos].parent; + if (pos >= 0) + pos = pl[pos].next; + return pos; +} + +void draw_one(int row, struct plist *pl) +{ + char hdr[10]; + hdr[0] = 0; + + if (pl == NULL) { + move(row,0); + clrtoeol(); + return; + } + if (pl->chunks > 99) + strcpy(hdr, "XX"); + else sprintf(hdr, "%02d", pl->chunks); + if (pl->wiggles > 99) + strcpy(hdr, " XX"); + else sprintf(hdr+2, " %02d", pl->wiggles); + if (pl->conflicts > 99) + strcpy(hdr, " XX"); + else sprintf(hdr+5, " %02d ", pl->conflicts); + if (pl->end) + strcpy(hdr+9, "= "); + else if (pl->open) + strcpy(hdr+9, "+ "); + else strcpy(hdr+9, "- "); + + mvaddstr(row, 0, hdr); + mvaddstr(row, 11, pl->file); + clrtoeol(); +} + +void addword(struct elmnt e) +{ + addnstr(e.start, e.len); +} + +void diff_window(struct plist *p, FILE *f) +{ + /* + * I wonder what to display here .... + */ + struct stream s; + struct stream s1, s2; + struct file f1, f2; + struct csl *csl; + char buf[100]; + int ch; + s = load_segment(f, p->start, p->end); + ch = split_patch(s, &s1, &s2); + + clear(); + sprintf(buf, "Chunk count: %d\n", ch); + mvaddstr(1,1,buf); clrtoeol(); + + + f1 = split_stream(s1, ByWord, 0); + f2 = split_stream(s2, ByWord, 0); + + csl = diff(f1, f2); + + /* now try to display the diff highlighted */ + int sol = 1; + int a=0, b=0; + + while(aa) { + if (sol) { + int a1; + /* if we remove a whole line, output +line, + * else clear sol and retry + */ + sol = 0; + for (a1=a; a1a; a1++) + if (f1.list[a1].start[0] == '\n') { + sol = 1; + break; + } + if (sol) { + addch('-'); + attron(A_UNDERLINE); + for (; aa; a++) { + addword(f1.list[a]); + if (f1.list[a].start[0] == '\n') { + a++; + break; + } + } + attroff(A_UNDERLINE); + } else addch('|'); + } + if (!sol) { + attron(A_UNDERLINE); + do { + if (sol) { + attroff(A_UNDERLINE); + addch('|'); + attron(A_UNDERLINE); + } + addword(f1.list[a]); + sol = (f1.list[a].start[0] == '\n'); + a++; + } while (a < csl->a); + attroff(A_UNDERLINE); + if (sol) addch('|'); + sol = 0; + } + } else if (b < csl->b) { + if (sol) { + int b1; + sol = 0; + for (b1=b; b1b; b1++) + if (f2.list[b1].start[0] == '\n') { + sol = 1; + break; + } + if (sol) { + addch('+'); + attron(A_BOLD); + for (; bb; b++) { + addword(f2.list[b]); + if (f2.list[b].start[0] == '\n') { + b++; + break; + } + } + attroff(A_BOLD); + } else addch('|'); + } + if (!sol) { + attron(A_BOLD); + do { + if (sol) { + attroff(A_BOLD); + addch('|'); + attron(A_BOLD); + } + addword(f2.list[b]); + sol = (f2.list[b].start[0] == '\n'); + b++; + } while (b < csl->b); + attroff(A_BOLD); + if (sol) addch('|'); + sol = 0; + } + } else { + if (sol) { + int a1; + sol = 0; + for (a1=a; a1a+csl->len; a1++) + if (f1.list[a1].start[0] == '\n') + sol = 1; + if (sol) { + if (f1.list[a].start[0]) { + addch(' '); + for (; a< csl->a+csl->len; a++,b++) { + addword(f1.list[a]); + if (f1.list[a].start[0]=='\n') { + a++,b++; + break; + } + } + } else { + addstr("SEP\n"); + a++; b++; + } + } else addch('|'); + } + if (!sol) { + addword(f1.list[a]); + if (f1.list[a].start[0] == '\n') + sol = 1; + a++; + b++; + } + if (a >= csl->a+csl->len) + csl++; + } + } + + + getch(); + + free(s1.body); + free(s2.body); + free(f1.list); + free(f2.list); +} + +void main_window(struct plist *pl, int n, FILE *f) +{ + /* The main window lists all files together with summary information: + * number of chunks, number of wiggles, number of conflicts. + * The list is scrollable + * When a entry is 'selected', we switch to the 'file' window + * The list can be condensed by removing files with no conflict + * or no wiggles, or removing subdirectories + * + * We record which file in the list is 'current', and which + * screen line it is on. We try to keep things stable while + * moving. + * + * Counts are printed before the name using at most 2 digits. + * Numbers greater than 99 are XX + * Ch Wi Co File + * 27 5 1 drivers/md/md.c + * + * A directory show the sum in all children. + * + * Commands: + * select: enter, space, mouseclick + * on file, go to file window + * on directory, toggle open + * up: k, p, control-p uparrow + * Move to previous open object + * down: j, n, control-n, downarrow + * Move to next open object + * + */ + int pos=0; /* position in file */ + int row=1; /* position on screen */ + int rows; /* size of screen in rows */ + int cols; + int tpos, i; + int refresh = 2; + int c; + + while(1) { + if (refresh == 2) { + clear(); + attron(A_BOLD); + mvaddstr(0,0,"Ch Wi Co Patched Files"); + move(2,0); + attroff(A_BOLD); + refresh = 1; + } + if (row <1 || row >= rows) + refresh = 1; + if (refresh) { + refresh = 0; + getmaxyx(stdscr, rows, cols); + if (row >= rows +3) + row = (rows+1)/2; + if (row >= rows) + row = rows-1; + tpos = pos; + for (i=row; i>1; i--) { + tpos = get_prev(tpos, pl, n); + if (tpos == -1) { + row = row - i + 1; + break; + } + } + /* Ok, row and pos could be trustworthy now */ + tpos = pos; + for (i=row; i>=1; i--) { + draw_one(i, &pl[tpos]); + tpos = get_prev(tpos, pl, n); + } + tpos = pos; + for (i=row+1; i= 0) + draw_one(i, &pl[tpos]); + else + draw_one(i, NULL); + } + } + move(row, 9); + c = getch(); + switch(c) { + case 'j': + case 'n': + case 'N': + case 'N'-64: + case KEY_DOWN: + tpos = get_next(pos, pl, n); + if (tpos >= 0) { + pos = tpos; + row++; + } + break; + case 'k': + case 'p': + case 'P': + case 'P'-64: + case KEY_UP: + tpos = get_prev(pos, pl, n); + if (tpos >= 0) { + pos = tpos; + row--; + } + break; + + case ' ': + case 13: + if (pl[pos].end == 0) { + pl[pos].open = ! pl[pos].open; + refresh = 1; + } else { + diff_window(&pl[pos], f); + refresh = 2; + } + break; + case 27: /* escape */ + case 'q': + return; + } + } +} + + +int main(int argc, char *argv[]) +{ + int n = 0; + FILE *f = NULL; + FILE *in = stdin; + struct plist *pl; + + if (argc == 3) + f = fopen(argv[argc-1], "w+"); + if (argc >=2) + in = fopen(argv[1], "r"); + else { + printf("no arg...\n"); + exit(2); + } + + pl = parse_patch(in, f, &n); + pl = sort_patches(pl, &n); + + if (f) { + fclose(in); + in = f; + } +#if 0 + int i; + for (i=0; i + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +/* + * Wiggle is a tool for working with patches that don't quite apply properly. + * It provides functionality similar to 'diff' and 'merge' but can + * work at the level of individual words thus allowing the merging of + * two changes that affect the same line, but not the same parts of that line. + * + * Wiggle can also read patch and merge files. Unlike 'merge' it does not + * need to be given three separate files, but can be given a file and a patch + * and it will extract the pieces of the two two other files that it needs from + * the patch. + * + * Wiggle performs one of three core function: + * --extract -x extract part of a patch or merge file + * --diff -d report differences between two files + * --merge -m merge the changes between two files into a third file + * + * To perform these, wiggle requires 1, 2, or 3 input streams respectively. + * I can get there from individual files, from a diff (unified or context) or + * from a merge file. + * + * For merge: + * If one file is given, it is a merge file (output of 'merge'). + * If two files are given, the second is assumed to be a patch, the first is a normal file. + * If three files are given, they are taken to be normal files. + * + * For diff: + * If one file is given, it is a patch + * If two files are given, they are normal files. + * + * For extract: + * Only one file can be given. -p indicates it is a patch, otherwise it is a merge. + * One of the flags -1 -2 or -3 must also be given and they indicate which + * part of the patch or merge to extract. + * + * Difference calculate and merging is performed on lines (-l) or words (-w). + * In the case of -w, an initial diff is computed based on non-trivial words. + * i.e. spaces are ignored + * This diff is computed from the ends of the file and is used to find a suitable + * starting point and range. Then a more precise diff is computed over that + * restricted range + * + * Other options available are: + * --replace -r replace first file with result of merge. + * --help -h provide help + * --version -v version + * + * Defaults are --merge --words + * + */ + +#include "wiggle.h" +#include +#include +#include +#include +#include +#include + +void die() +{ + fprintf(stderr,"wiggle: fatal error\n"); + abort(); + exit(3); +} + +void printword(FILE *f, struct elmnt e) +{ + if (e.start[0]) + fprintf(f, "%.*s", e.len, e.start); + else { + int a,b,c; + sscanf(e.start+1, "%d %d %d", &a, &b, &c); + fprintf(f, "*** %d,%d **** %d\n", b,c,a); + } +} + +static void printsep(struct elmnt e1, struct elmnt e2) +{ + int a,b,c,d,e,f; + sscanf(e1.start+1, "%d %d %d", &a, &b, &c); + sscanf(e2.start+1, "%d %d %d", &d, &e, &f); + printf("@@ -%d,%d +%d,%d @@\n", b,c,e,f); +} + + +/* Remove any entries from the common-sublist that are + * just spaces, tabs, or newlines + */ +void cleanlist(struct file a, struct file b, struct csl *list) +{ + struct csl *new = list; + + while (list->len) { + int i; + int ap; + for( ap = list->a; ap< list->a+list->len; ap++) { + for (i=0; ia+list->len) + list++; + else + *new++ = *list++; + } + *new = *list; +} + +int main(int argc, char *argv[]) +{ + int opt; + int option_index; + int mode = 0; + int obj = 0; + int replace = 0; + char *replacename=NULL, *orignew=NULL; + int which = 0; + int ispatch = 0; + int reverse = 0; + int verbose=0, quiet=0; + int i; + int chunks1=0, chunks2=0, chunks3=0; + int exit_status = 0; + FILE *outfile = stdout; + char *helpmsg; + + struct stream f, flist[3]; + struct file fl[3]; + struct csl *csl1, *csl2; + + while ((opt = getopt_long(argc, argv, + short_options, long_options, + &option_index)) != -1) + switch(opt) { + case 'h': + helpmsg = Help; + switch(mode) { + case 'x': helpmsg = HelpExtract; break; + case 'd': helpmsg = HelpDiff; break; + case 'm': helpmsg = HelpMerge; break; + } + fputs(helpmsg, stderr); + exit(0); + + case 'V': + fputs(Version, stderr); + exit(0); + case ':': + case '?': + default: + fputs(Usage, stderr); + exit(2); + + case 'x': + case 'd': + case 'm': + if (mode ==0){ + mode = opt; + continue; + } + fprintf(stderr, "wiggle: mode is '%c' - cannot set to '%c'\n", + mode, opt); + exit(2); + + case 'w': + case 'l': + if (obj == 0 || obj == opt) { + obj = opt; + continue; + } + fprintf(stderr, "wiggle: cannot select both words and lines.\n"); + exit(2); + + case 'r': + replace = 1; + continue; + case 'R': + reverse = 1; + continue; + + case '1': + case '2': + case '3': + if (which == 0 || which == opt) { + which = opt; + continue; + } + fprintf(stderr, "wiggle: can only select one of -1, -2, -3\n"); + exit(2); + + case 'p': + ispatch = 1; + continue; + + case 'v': verbose++; continue; + case 'q': quiet=1 ; continue; + } + if (!mode) + mode = 'm'; + + if (obj && mode == 'x') { + fprintf(stderr,"wiggle: cannot specify --line or --word with --extract\n"); + exit(2); + } + if (mode != 'm' && !obj) obj = 'w'; + if (replace && mode != 'm') { + fprintf(stderr, "wiggle: --replace only allowed with --merge\n"); + exit(2); + } + if (mode == 'x' && !which) { + fprintf(stderr, "wiggle: must specify -1, -2 or -3 with --extract\n"); + exit(2); + } + if (mode != 'x' && mode != 'd' && which) { + fprintf(stderr, "wiggle: -1, -2 or -3 only allowed with --extract or --diff\n"); + exit(2); + } + if (ispatch && (mode != 'x' && mode != 'd')) { + fprintf(stderr, "wiggle: --patch only allowed with --extract or --diff\n"); + exit(2); + } + if (ispatch && which == '3') { + fprintf(stderr, "wiggle: cannot extract -3 from a patch.\n"); + exit(2); + } + + switch(mode) { + case 'x': + /* extract a branch of a diff or diff3 or merge output + * We need one file + */ + if (optind == argc) { + fprintf(stderr, "wiggle: no file given for --extract\n"); + exit(2); + } + if (optind < argc-1) { + fprintf(stderr, "wiggle: only give one file for --extract\n"); + exit(2); + } + f = load_file(argv[optind]); + if (f.body==NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind], strerror(errno)); + exit(2); + } + if (ispatch) + chunks1 = chunks2 = split_patch(f, &flist[0], &flist[1]); + else { + if (!split_merge(f, &flist[0], &flist[1], &flist[2])) { + fprintf(stderr, "wiggle: merge file %s looks bad.\n", + argv[optind]); + exit(2); + } + } + if (flist[which-'1'].body == NULL) { + fprintf(stderr, "wiggle: %s has no -%c component.\n", + argv[optind], which); + exit(2); + } else { + write(1, flist[which-'1'].body, flist[which-'1'].len); + } + + break; + case 'd': + /* create a diff (line or char) of two streams */ + switch (argc-optind) { + case 0: + fprintf(stderr, "wiggle: no file given for --diff\n"); + exit(2); + case 1: + f = load_file(argv[optind]); + if (f.body == NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind], strerror(errno)); + exit(2); + } + chunks1 = chunks2 = split_patch(f, &flist[0], &flist[1]); + if (!flist[0].body || !flist[1].body) { + fprintf(stderr, "wiggle: couldn't parse patch %s\n", + argv[optind]); + exit(2); + } + break; + case 2: + flist[0] = load_file(argv[optind]); + if (flist[0].body == NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind], strerror(errno)); + exit(2); + } + if (ispatch) { + f = load_file(argv[optind+1]); + if (f.body == NULL) { + fprintf(stderr, "wiggle: cannot load patch '%s' - %s\n", + argv[optind], strerror(errno)); + exit(2); + } + if (which == '2') + chunks2 = chunks3 = split_patch(f, &flist[2], &flist[1]); + else + chunks2 = chunks3 = split_patch(f, &flist[1], &flist[2]); + + } else + flist[1] = load_file(argv[optind+1]); + if (flist[1].body == NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind+1], strerror(errno)); + exit(2); + } + break; + default: + fprintf(stderr, "wiggle: too many files given for --diff\n"); + exit(2); + } + if (reverse) { + f=flist[1]; + flist[1] = flist[2]; + flist[2]= f; + } + if (obj == 'l') { + int a,b; + fl[0] = split_stream(flist[0], ByLine, 0); + fl[1] = split_stream(flist[1], ByLine, 0); + if (chunks2 && ! chunks1) + csl1 = pdiff(fl[0], fl[1], chunks2); + else + csl1 = diff(fl[0], fl[1]); + + if (!chunks1) + printf("@@ -1,%d +1,%d @@\n", fl[0].elcnt, fl[1].elcnt); + a = b = 0; + while (aa) { + if (fl[0].list[a].start[0]) { + printf("-"); + printword(stdout, fl[0].list[a]); + } + a++; + exit_status++; + } else if (b < csl1->b) { + if (fl[1].list[b].start[0]) { + printf("+"); + printword(stdout, fl[1].list[b]); + } + b++; + exit_status++; + } else { + if (fl[0].list[a].start[0] == '\0') + printsep(fl[0].list[a], fl[1].list[b]); + else { + printf(" "); + printword(stdout, fl[0].list[a]); + } + a++; + b++; + if (a >= csl1->a+csl1->len) + csl1++; + } + } + } else { + int a,b; + int sol = 1; /* start of line */ + fl[0] = split_stream(flist[0], ByWord, 0); + fl[1] = split_stream(flist[1], ByWord, 0); + if (chunks2 && !chunks1) + csl1 = pdiff(fl[0], fl[1], chunks2); + else + csl1 = diff(fl[0], fl[1]); + + if (!chunks1) { + /* count lines in each file */ + int l1, l2, i; + l1=l2=0; + for (i=0;ia) { + exit_status++; + if (sol) { + int a1; + /* If we remove a whole line, output +line + * else clear sol and retry */ + sol = 0; + for (a1=a; a1a;a1++) + if (ends_line(fl[0].list[a1])) { + sol=1; + break; + } + if (sol) { + printf("-"); + for (; aa; a++) { + printword(stdout, fl[0].list[a]); + if (ends_line(fl[0].list[a])) { + a++; + break; + } + } + } else printf("|"); + } + if (!sol) { + printf("<<<--"); + do { + if (sol) printf("|"); + printword(stdout, fl[0].list[a]); + sol = ends_line(fl[0].list[a]); + a++; + } while (a < csl1->a); + printf("%s-->>>", sol?"|":""); + sol=0; + } + } else if (b < csl1->b) { + exit_status++; + if (sol) { + int b1; + sol = 0; + for (b1=b; b1b;b1++) + if(ends_line(fl[1].list[b1])) { + sol=1; + break; + } + if (sol) { + printf("+"); + for(; bb ; b++) { + printword(stdout, fl[1].list[b]); + if(ends_line(fl[1].list[b])) { + b++; + break; + } + } + } else printf("|"); + } + if (!sol) { + printf("<<<++"); + do { + if (sol) printf("|"); + printword(stdout, fl[1].list[b]); + sol = ends_line(fl[1].list[b]); + b++; + } while (b < csl1->b); + printf("%s++>>>",sol?"|":""); + sol=0; + } + } else { + if (sol) { + int a1; + sol = 0; + for (a1=a; a1a+csl1->len; a1++) + if (ends_line(fl[0].list[a1])) + sol=1; + if (sol) { + if (fl[0].list[a].start[0]) { + printf(" "); + for(; aa+csl1->len; a++,b++) { + printword(stdout, fl[0].list[a]); + if (ends_line(fl[0].list[a])) { + a++,b++; + break; + } + } + } else { + printsep(fl[0].list[a], fl[1].list[b]); + a++; b++; + } + } + else printf("|"); + } + if (!sol) { + printword(stdout, fl[0].list[a]); + if (ends_line(fl[0].list[a])) + sol=1; + a++; + b++; + } + if (a >= csl1->a+csl1->len) + csl1++; + } + } + + } + break; + case 'm': + /* merge three files, A B C, so changed between B and C get made to A + */ + switch (argc-optind) { + case 0: + fprintf(stderr, "wiggle: no files given for --merge\n"); + exit(2); + case 3: + case 2: + case 1: + for (i=0; i< argc-optind; i++) { + flist[i] = load_file(argv[optind+i]); + if (flist[i].body == NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind+i], strerror(errno)); + exit(2); + } + } + break; + default: + fprintf(stderr, "wiggle: too many files given for --merge\n"); + exit(2); + } + switch(argc-optind) { + case 1: /* a merge file */ + f = flist[0]; + if (!split_merge(f, &flist[0], &flist[1], &flist[2])) { + fprintf(stderr,"wiggle: merge file %s looks bad.\n", + argv[optind]); + exit(2); + } + break; + case 2: /* a file and a patch */ + f = flist[1]; + chunks2 = chunks3 = split_patch(f, &flist[1], &flist[2]); + break; + case 3: /* three separate files */ + break; + } + if (reverse) { + f=flist[1]; + flist[1] = flist[2]; + flist[2]= f; + } + + for (i=0; i<3; i++) { + if (flist[i].body==NULL) { + fprintf(stderr, "wiggle: file %d missing\n", i); + exit(2); + } + } + if (replace) { + int fd; + replacename = malloc(strlen(argv[optind])+ 20); + if (!replacename) die(); + orignew = malloc(strlen(argv[optind])+20); + if (!orignew) die(); + strcpy(replacename, argv[optind]); + strcpy(orignew, argv[optind]); + strcat(orignew, ".porig"); + if (open(orignew, O_RDONLY) >= 0 || + errno != ENOENT) { + fprintf(stderr,"wiggle: %s already exists\n", + orignew); + exit(2); + } + strcat(replacename,"XXXXXX"); + fd = mkstemp(replacename); + if (fd == -1) { + fprintf(stderr,"wiggle: could not create temporary file for %s\n", + replacename); + exit(2); + } + outfile = fdopen(fd, "w"); + + } + + if (obj == 'l') { + fl[0] = split_stream(flist[0], ByLine, 0); + fl[1] = split_stream(flist[1], ByLine, 0); + fl[2] = split_stream(flist[2], ByLine, 0); + } else { + fl[0] = split_stream(flist[0], ByWord, 0); + fl[1] = split_stream(flist[1], ByWord, 0); + fl[2] = split_stream(flist[2], ByWord, 0); + } + if (chunks2 && !chunks1) + csl1 = pdiff(fl[0], fl[1], chunks2); + else + csl1 = diff(fl[0], fl[1]); + csl2 = diff(fl[1], fl[2]); + +#if 0 + cleanlist(fl[0],fl[1],csl1); + cleanlist(fl[1],fl[2],csl2); +#endif + + { + struct ci ci; + + ci = print_merge(outfile, &fl[0], &fl[1], &fl[2], + csl1, csl2, obj=='w'); + if (!quiet && ci.conflicts) + fprintf(stderr, "%d unresolved conflict%s found\n", ci.conflicts, ci.conflicts==1?"":"s"); + if (!quiet && ci.ignored) + fprintf(stderr, "%d already-applied change%s ignored\n", ci.ignored, ci.ignored==1?"":"s"); + exit_status = (ci.conflicts > 0); + } + if (replace) { + fclose(outfile); + if (rename(argv[optind], orignew) ==0 && + rename(replacename, argv[optind]) ==0) + /* all ok */; + else { + fprintf(stderr, "wiggle: failed to move new file into place.\n"); + exit(2); + } + } + break; + + } + exit(exit_status); +} diff --git a/demo/383MdBlocked b/demo/383MdBlocked new file mode 100644 index 0000000..efa1f02 --- /dev/null +++ b/demo/383MdBlocked @@ -0,0 +1,271 @@ +Status: ok + +Support 'blocked' flag on devices. + +MORE DETAILS and more code + +Signed-off-by: Neil Brown + +### Diffstat output + ./drivers/md/md.c | 28 ++++++++++++++++++++++++++++ + ./drivers/md/raid1.c | 21 ++++++++++++++++++++- + ./drivers/md/raid10.c | 24 +++++++++++++++++++++++- + ./drivers/md/raid5.c | 14 ++++++++++++-- + ./include/linux/raid/md_k.h | 7 +++++++ + 5 files changed, 90 insertions(+), 4 deletions(-) + +diff ./drivers/md/md.c~current~ ./drivers/md/md.c +--- ./drivers/md/md.c~current~ 2006-04-18 13:00:05.000000000 +1000 ++++ ./drivers/md/md.c 2006-04-11 15:44:12.000000000 +1000 +@@ -1724,6 +1724,18 @@ state_show(mdk_rdev_t *rdev, char *page) + len += sprintf(page+len, "%sin_sync",sep); + sep = ","; + } ++ if (test_bit(WriteMostly, &rdev->flags)) { ++ len += sprintf(page+len, "%swrite_mostly",sep); ++ sep = ","; ++ } ++ if (test_bit(ReadFault, &rdev->flags)) { ++ len += sprintf(page+len, "%sread_fault",sep); ++ sep = ","; ++ } ++ if (test_bit(Blocked, &rdev->flags)) { ++ len += sprintf(page+len, "%sblocked",sep); ++ sep = ","; ++ } + if (!test_bit(Faulty, &rdev->flags) && + !test_bit(In_sync, &rdev->flags)) { + len += sprintf(page+len, "%sspare", sep); +@@ -1738,6 +1750,10 @@ state_store(mdk_rdev_t *rdev, const char + /* can write + * faulty - simulates and error + * remove - disconnects the device ++ * writemostly - sets write_mostly ++ * -writemostly - clears write_mostly ++ * blocked - sets Blocked flag ++ * -blocked - clears Blocked flag + */ + int err = -EINVAL; + if (cmd_match(buf, "faulty") && rdev->mddev->pers) { +@@ -1753,6 +1769,17 @@ state_store(mdk_rdev_t *rdev, const char + md_new_event(mddev); + err = 0; + } ++ } else if (cmd_match(buf, "writemostly")) { ++ set_bit(WriteMostly, &rdev->flags); ++ } else if (cmd_match(buf, "-writemostly")) { ++ clear_bit(WriteMostly, &rdev->flags); ++ } else if (cmd_match(buf, "blocked")) { ++ set_bit(Blocked, &rdev->flags); ++ } else if (cmd_match(buf, "-blocked")) { ++ clear_bit(Blocked, &rdev->flags); ++ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); ++ md_wakeup_thread(mddev->thread); ++ wake_up(&??); + } + return err ? err : len; + } +@@ -5459,6 +5486,7 @@ void md_check_recovery(mddev_t *mddev) + ITERATE_RDEV(mddev,rdev,rtmp) + if (rdev->raid_disk >= 0 && + (test_bit(Faulty, &rdev->flags) || ! test_bit(In_sync, &rdev->flags)) && ++ !test_bit(Blocked, &rdev->flags) && + atomic_read(&rdev->nr_pending)==0) { + if (mddev->pers->hot_remove_disk(mddev, rdev->raid_disk)==0) { + char nm[20]; + +diff ./drivers/md/raid1.c~current~ ./drivers/md/raid1.c +--- ./drivers/md/raid1.c~current~ 2006-04-18 13:00:05.000000000 +1000 ++++ ./drivers/md/raid1.c 2006-04-18 12:59:28.000000000 +1000 +@@ -428,7 +428,7 @@ static int read_balance(conf_t *conf, r1 + retry: + if (conf->mddev->recovery_cp < MaxSector && + (this_sector + sectors >= conf->next_resync)) { +- /* Choose the first operation device, for consistancy */ ++ /* Choose the first operational device, for consistency */ + new_disk = 0; + + for (rdev = rcu_dereference(conf->mirrors[new_disk].rdev); +@@ -745,6 +745,7 @@ static int make_request(request_queue_t + struct page **behind_pages = NULL; + const int rw = bio_data_dir(bio); + int do_barriers; ++ int blocked = 0; + + /* + * Register the new request and wait if the reconstruction +@@ -824,12 +825,18 @@ static int make_request(request_queue_t + first = 0; + } + #endif ++ retry_write: + rcu_read_lock(); + for (i = 0; i < disks; i++) { + if ((rdev=rcu_dereference(conf->mirrors[i].rdev)) != NULL && + !test_bit(Faulty, &rdev->flags)) { + atomic_inc(&rdev->nr_pending); + if (test_bit(Faulty, &rdev->flags)) { ++ if (unlikely(test_bit(Block??, &rdev->flags))) { ++ blocked = 1; ++ rdev_dec_pending(rdev, mddev); ++ break; ++ } + rdev_dec_pending(rdev, mddev); + r1_bio->bios[i] = NULL; + } else +@@ -839,6 +846,18 @@ static int make_request(request_queue_t + r1_bio->bios[i] = NULL; + } + rcu_read_unlock(); ++ if (unlikely(blocked)) { ++ /* Have to wait for this device to get unblocked, then retry. */ ++ int j; ++ for (j=0; jbios[j]) ++ rdev_dec_pending(conf->mirrors[j].rdev, mddev); ++ allow_barrier(conf); ++ wait_event(mddev->recovery_wait, ++ !(test_bit(Faulty, &rdev->flags....); ++ wait_barrier(conf); ++ goto retry_write; ++ } + + BUG_ON(targets == 0); /* we never fail the last device */ + + +diff ./drivers/md/raid10.c~current~ ./drivers/md/raid10.c +--- ./drivers/md/raid10.c~current~ 2006-04-18 13:00:05.000000000 +1000 ++++ ./drivers/md/raid10.c 2006-04-12 14:19:51.000000000 +1000 +@@ -764,6 +764,7 @@ static int make_request(request_queue_t + const int rw = bio_data_dir(bio); + struct bio_list bl; + unsigned long flags; ++ int blocked = 0; + + if (unlikely(bio_barrier(bio))) { + bio_endio(bio, bio->bi_size, -EOPNOTSUPP); +@@ -853,17 +854,22 @@ static int make_request(request_queue_t + /* + * WRITE: + */ +- /* first select target devices under spinlock and ++ /* first select target devices under rcu_lock and + * inc refcount on their rdev. Record them by setting + * bios[x] to bio + */ + raid10_find_phys(conf, r10_bio); ++ retry_write: + rcu_read_lock(); + for (i = 0; i < conf->copies; i++) { + int d = r10_bio->devs[i].devnum; + mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[d].rdev); + if (rdev && + !test_bit(Faulty, &rdev->flags)) { ++ if (unlikely(test_bit(Blocked, &rdev->flags))) { ++ blocked = 1; ++ break; ++ } + atomic_inc(&rdev->nr_pending); + r10_bio->devs[i].bio = bio; + } else { +@@ -873,6 +879,22 @@ static int make_request(request_queue_t + } + rcu_read_unlock(); + ++ if (unlikely(blocked)) { ++ /* Have to wait for this device to get unblocked, then retry */ ++ int j; ++ int d; ++ for (j=0; jdevs[j].bio) { ++ d = r10_bio->devs[j].devnum; ++ rdev_dec_pending(conf->mirrors[d].rdev, mddev); ++ } ++ allow_barrier(conf); ++ d = r10_bio->devs[i].devnum; ++ wait_event(...); ++ wait_barrier(conf); ++ goto retry_write; ++ } ++ + atomic_set(&r10_bio->remaining, 0); + + bio_list_init(&bl); + +diff ./drivers/md/raid5.c~current~ ./drivers/md/raid5.c +--- ./drivers/md/raid5.c~current~ 2006-04-18 13:00:05.000000000 +1000 ++++ ./drivers/md/raid5.c 2006-04-12 10:26:53.000000000 +1000 +@@ -1349,6 +1349,7 @@ static void handle_stripe5(struct stripe + int syncing, expanding, expanded; + int locked=0, uptodate=0, to_read=0, to_write=0, failed=0, written=0; + int non_overwrite = 0; ++ int blocked = 0; + int failed_num=0; + struct r5dev *dev; + +@@ -1400,7 +1401,6 @@ static void handle_stripe5(struct stripe + if (test_bit(R5_LOCKED, &dev->flags)) locked++; + if (test_bit(R5_UPTODATE, &dev->flags)) uptodate++; + +- + if (dev->toread) to_read++; + if (dev->towrite) { + to_write++; +@@ -1414,6 +1414,9 @@ static void handle_stripe5(struct stripe + clear_bit(R5_ReadError, &dev->flags); + clear_bit(R5_ReWrite, &dev->flags); + } ++ if (rdev && test_bit(Blocked, &rdev->flags) && ++ test_bit(Faulty, &rdev->flags)) ++ blocked = 1; + if (!rdev || !test_bit(In_sync, &rdev->flags) + || test_bit(R5_ReadError, &dev->flags)) { + failed++; +@@ -1505,6 +1508,11 @@ static void handle_stripe5(struct stripe + clear_bit(STRIPE_SYNCING, &sh->state); + syncing = 0; + } ++ if (blocked) { ++ set_bit(STRIPE_DELAYED, &sh->state); ++ set_bit(STRIPE_HANDLE, &sh->state); ++ goto unlock; ++ } + + /* might be able to return some write requests if the parity block + * is safe, or on a failed drive +@@ -1818,7 +1826,7 @@ static void handle_stripe5(struct stripe + release_stripe(sh2); + } + } +- ++ unlock: + spin_unlock(&sh->lock); + + while ((bi=return_bi)) { +@@ -2454,6 +2462,8 @@ static void handle_stripe(struct stripe_ + + static void raid5_activate_delayed(raid5_conf_t *conf) + { ++ if (conf->blocked) ++ return; + if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) { + while (!list_empty(&conf->delayed_list)) { + struct list_head *l = conf->delayed_list.next; + +diff ./include/linux/raid/md_k.h~current~ ./include/linux/raid/md_k.h +--- ./include/linux/raid/md_k.h~current~ 2006-04-18 13:00:05.000000000 +1000 ++++ ./include/linux/raid/md_k.h 2006-04-18 12:59:43.000000000 +1000 +@@ -82,6 +82,13 @@ struct mdk_rdev_s + #define In_sync 2 /* device is in_sync with rest of array */ + #define WriteMostly 4 /* Avoid reading if at all possible */ + #define BarriersNotsupp 5 /* BIO_RW_BARRIER is not supported */ ++#define DoBlock 6 /* If an error occurs, block IO until ++ * error or DoBlock is cleared ++ */ ++#define Blocked 7 /* An error occurred and DoBlock was ++ * set, so don't touch this device until ++ * it is cleared. ++ */ + + int desc_nr; /* descriptor index in the superblock */ + int raid_disk; /* role of device in array */ diff --git a/demo/Makefile b/demo/Makefile new file mode 100644 index 0000000..a8652de --- /dev/null +++ b/demo/Makefile @@ -0,0 +1,52 @@ +# Note on my Mobile Pentium II, -march=pentium2 delivers twice the performance of i386 +#OptDbg=-O3 +#OptDbg=-O3 -march=pentium2 +CFLAGS=$(OptDbg) -Wall -Werror + +# STRIP = +INSTALL = /usr/bin/install +DESTDIR = +BINDIR = /usr/bin +MANDIR = /var/share/man +MAN1DIR = $(MANDIR)/man1 +MAN5DIR = $(MANDIR)/man5 +LDLIBS=-lncurses + +all: wiggle wiggle.man test + +vpatch : vpatch.o extract.o split.o diff.o + + +# comment +wiggle : wiggle.o load.o split.o extract.o diff.o bestmatch.o ReadMe.o merge.o +wiggle.o load.o split.o extract.o diff.o bestmatch.o ReadMe.o merge.o : wiggle.h + +test: wiggle dotest + sh dotest + +wiggle.man : wiggle.1 + nroff -man wiggle.1 > wiggle.man + +clean: + rm -f *.o *.man wiggle .version* version + find . -name core -o -name '*.tmp*' -o -name .tmp | xargs rm -f + +install : wiggle wiggle.1 + $(INSTALL) -D $(STRIP) -m 755 wiggle $(DESTDIR)$(BINDIR)/wiggle + $(INSTALL) -D -m 644 wiggle.1 $(DESTDIR)$(MAN1DIR)/wiggle.1 + +version : ReadMe.c wiggle.1 Readme.c + @rm -f version + @sed -n -e 's/.*wiggle - v\([0-9.]*\) - .*/\1/p' ReadMe.c > .version-readme + @sed -n -e 's/.*WIGGLE 1 "" v\([0-9.]*\)$$/\1/p' wiggle.1 > .version-man + @cmp -s .version-readme .version-man && cat .version-man > version || { echo Inconsistant versions.; exit 1;} + +dist : test clean version Test + mkdir -p DIST + rm -f DIST/wiggle-`cat version` + ln -s .. DIST/wiggle-`cat version` + tar czvf DIST/wiggle-`cat version`.tar.gz -h -C DIST --exclude RCS --exclude DIST wiggle-`cat version` + rm -f DIST/wiggle-`cat version` + +v : version + cat version diff --git a/demo/README b/demo/README new file mode 100644 index 0000000..d84c857 --- /dev/null +++ b/demo/README @@ -0,0 +1,47 @@ +This is demo file for wiggle's --browse mode. + +Browse mode is intended to let you look through a patch +to see how it will apply to a set of files. It is +possible that the patch will have some conflicts. +i.e. the patch was created from a different version of +the source to the version that you are applying the patch +to. This is what makes it interesting. + +You can use normal cursor motion to scroll around, +both vertially and horizontally (both EMACS and VI style). +From the initial file-list patch, use space +or to open/close a directory or file. +Form the file-view mode, use 'q' to get back to +to file list. + +Differences applied by the patch are shown as +red for removal and blue for addition. +Text with a pink background was not matched - +maybe it has been changed since the patch was +created. + +green-background text is text that the patch wants +to change, but the exact correct change has already +been made. + +Captial-N might go to the next interesting chunk +of the file. + + +--------------------------------------- +This demonstrates where a diff on one line +applies to text that is now split + +The swift brown fox +jumps over the lazy dog. + +------------ + +This demonstrates a diff which contains some +extraneous lines. + +The swift brown fox jumps over the lazy dog. + +There will be extra lines in the diff + +-------------- diff --git a/demo/md.c b/demo/md.c new file mode 100644 index 0000000..3b8f0f8 --- /dev/null +++ b/demo/md.c @@ -0,0 +1,5769 @@ +/* + md.c : Multiple Devices driver for Linux + Copyright (C) 1998, 1999, 2000 Ingo Molnar + + completely rewritten, based on the MD driver code from Marc Zyngier + + Changes: + + - RAID-1/RAID-5 extensions by Miguel de Icaza, Gadi Oxman, Ingo Molnar + - RAID-6 extensions by H. Peter Anvin + - boot support for linear and striped mode by Harald Hoyer + - kerneld support by Boris Tobotras + - kmod support by: Cyrus Durgin + - RAID0 bugfixes: Mark Anthony Lisher + - Devfs support by Richard Gooch + + - lots of fixes and improvements to the RAID1/RAID5 and generic + RAID code (such as request based resynchronization): + + Neil Brown . + + - persistent bitmap code + Copyright (C) 2003-2004, Paul Clements, SteelEye Technology, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + You should have received a copy of the GNU General Public License + (for example /usr/src/linux/COPYING); if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for invalidate_bdev */ +#include +#include +#include +#include + +#include + +#include + +#ifdef CONFIG_KMOD +#include +#endif + +#include + +#define MAJOR_NR MD_MAJOR +#define MD_DRIVER + +/* 63 partitions with the alternate major number (mdp) */ +#define MdpMinorShift 6 + +#define DEBUG 0 +#define dprintk(x...) ((void)(DEBUG && printk(x))) + + +#ifndef MODULE +static void autostart_arrays (int part); +#endif + +static LIST_HEAD(pers_list); +static DEFINE_SPINLOCK(pers_lock); + +static void md_print_devices(void); + +#define MD_BUG(x...) { printk("md: bug in file %s, line %d\n", __FILE__, __LINE__); md_print_devices(); } + +/* + * Current RAID-1,4,5 parallel reconstruction 'guaranteed speed limit' + * is 1000 KB/sec, so the extra system load does not show up that much. + * Increase it if you want to have more _guaranteed_ speed. Note that + * the RAID driver will use the maximum available bandwidth if the IO + * subsystem is idle. There is also an 'absolute maximum' reconstruction + * speed limit - in case reconstruction slows down your system despite + * idle IO detection. + * + * you can change it via /proc/sys/dev/raid/speed_limit_min and _max. + * or /sys/block/mdX/md/sync_speed_{min,max} + */ + +static int sysctl_speed_limit_min = 1000; +static int sysctl_speed_limit_max = 200000; +static inline int speed_min(mddev_t *mddev) +{ + return mddev->sync_speed_min ? + mddev->sync_speed_min : sysctl_speed_limit_min; +} + +static inline int speed_max(mddev_t *mddev) +{ + return mddev->sync_speed_max ? + mddev->sync_speed_max : sysctl_speed_limit_max; +} + +static struct ctl_table_header *raid_table_header; + +static ctl_table raid_table[] = { + { + .ctl_name = DEV_RAID_SPEED_LIMIT_MIN, + .procname = "speed_limit_min", + .data = &sysctl_speed_limit_min, + .maxlen = sizeof(int), + .mode = S_IRUGO|S_IWUSR, + .proc_handler = &proc_dointvec, + }, + { + .ctl_name = DEV_RAID_SPEED_LIMIT_MAX, + .procname = "speed_limit_max", + .data = &sysctl_speed_limit_max, + .maxlen = sizeof(int), + .mode = S_IRUGO|S_IWUSR, + .proc_handler = &proc_dointvec, + }, + { .ctl_name = 0 } +}; + +static ctl_table raid_dir_table[] = { + { + .ctl_name = DEV_RAID, + .procname = "raid", + .maxlen = 0, + .mode = S_IRUGO|S_IWUGO, + .child = raid_table, + }, + { .ctl_name = 0 } +}; + +static ctl_table raid_root_table[] = { + { + .ctl_name = CTL_DEV, + .procname = "dev", + .maxlen = 0, + .mode = 0555, + .child = raid_dir_table, + }, + { .ctl_name = 0 } +}; + +static struct block_device_operations md_fops; + +static int start_readonly; + +/* + * We have a system wide 'event count' that is incremented + * on any 'interesting' event, and readers of /proc/mdstat + * can use 'poll' or 'select' to find out when the event + * count increases. + * + * Events are: + * start array, stop array, error, add device, remove device, + * start build, activate spare + */ +static DECLARE_WAIT_QUEUE_HEAD(md_event_waiters); +static atomic_t md_event_count; +void md_new_event(mddev_t *mddev) +{ + atomic_inc(&md_event_count); + wake_up(&md_event_waiters); + sysfs_notify(&mddev->kobj, NULL, "sync_action"); +} +EXPORT_SYMBOL_GPL(md_new_event); + +/* Alternate version that can be called from interrupts + * when calling sysfs_notify isn't needed. + */ +void md_new_event_inintr(mddev_t *mddev) +{ + atomic_inc(&md_event_count); + wake_up(&md_event_waiters); +} + +/* + * Enables to iterate over all existing md arrays + * all_mddevs_lock protects this list. + */ +static LIST_HEAD(all_mddevs); +static DEFINE_SPINLOCK(all_mddevs_lock); + + +/* + * iterates through all used mddevs in the system. + * We take care to grab the all_mddevs_lock whenever navigating + * the list, and to always hold a refcount when unlocked. + * Any code which breaks out of this loop while own + * a reference to the current mddev and must mddev_put it. + */ +#define ITERATE_MDDEV(mddev,tmp) \ + \ + for (({ spin_lock(&all_mddevs_lock); \ + tmp = all_mddevs.next; \ + mddev = NULL;}); \ + ({ if (tmp != &all_mddevs) \ + mddev_get(list_entry(tmp, mddev_t, all_mddevs));\ + spin_unlock(&all_mddevs_lock); \ + if (mddev) mddev_put(mddev); \ + mddev = list_entry(tmp, mddev_t, all_mddevs); \ + tmp != &all_mddevs;}); \ + ({ spin_lock(&all_mddevs_lock); \ + tmp = tmp->next;}) \ + ) + + +static int md_fail_request (request_queue_t *q, struct bio *bio) +{ + bio_io_error(bio, bio->bi_size); + return 0; +} + +static inline mddev_t *mddev_get(mddev_t *mddev) +{ + atomic_inc(&mddev->active); + return mddev; +} + +static void mddev_put(mddev_t *mddev) +{ + if (!atomic_dec_and_lock(&mddev->active, &all_mddevs_lock)) + return; + if (!mddev->raid_disks && list_empty(&mddev->disks)) { + list_del(&mddev->all_mddevs); + spin_unlock(&all_mddevs_lock); + blk_cleanup_queue(mddev->queue); + kobject_unregister(&mddev->kobj); + } else + spin_unlock(&all_mddevs_lock); +} + +static mddev_t * mddev_find(dev_t unit) +{ + mddev_t *mddev, *new = NULL; + + retry: + spin_lock(&all_mddevs_lock); + list_for_each_entry(mddev, &all_mddevs, all_mddevs) + if (mddev->unit == unit) { + mddev_get(mddev); + spin_unlock(&all_mddevs_lock); + kfree(new); + return mddev; + } + + if (new) { + list_add(&new->all_mddevs, &all_mddevs); + spin_unlock(&all_mddevs_lock); + return new; + } + spin_unlock(&all_mddevs_lock); + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return NULL; + + new->unit = unit; + if (MAJOR(unit) == MD_MAJOR) + new->md_minor = MINOR(unit); + else + new->md_minor = MINOR(unit) >> MdpMinorShift; + + mutex_init(&new->reconfig_mutex); + INIT_LIST_HEAD(&new->disks); + INIT_LIST_HEAD(&new->all_mddevs); + init_timer(&new->safemode_timer); + atomic_set(&new->active, 1); + spin_lock_init(&new->write_lock); + init_waitqueue_head(&new->sb_wait); + new->resync_max = MaxSector; + + new->queue = blk_alloc_queue(GFP_KERNEL); + if (!new->queue) { + kfree(new); + return NULL; + } + set_bit(QUEUE_FLAG_CLUSTER, &new->queue->queue_flags); + + blk_queue_make_request(new->queue, md_fail_request); + + goto retry; +} + +static inline int mddev_lock(mddev_t * mddev) +{ + return mutex_lock_interruptible(&mddev->reconfig_mutex); +} + +static inline int mddev_trylock(mddev_t * mddev) +{ + return mutex_trylock(&mddev->reconfig_mutex); +} + +static inline void mddev_unlock(mddev_t * mddev) +{ + mutex_unlock(&mddev->reconfig_mutex); + + md_wakeup_thread(mddev->thread); +} + +static mdk_rdev_t * find_rdev_nr(mddev_t *mddev, int nr) +{ + mdk_rdev_t * rdev; + struct list_head *tmp; + + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->desc_nr == nr) + return rdev; + } + return NULL; +} + +static mdk_rdev_t * find_rdev(mddev_t * mddev, dev_t dev) +{ + struct list_head *tmp; + mdk_rdev_t *rdev; + + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->bdev->bd_dev == dev) + return rdev; + } + return NULL; +} + +static struct mdk_personality *find_pers(int level, char *clevel) +{ + struct mdk_personality *pers; + list_for_each_entry(pers, &pers_list, list) { + if (level != LEVEL_NONE && pers->level == level) + return pers; + if (strcmp(pers->name, clevel)==0) + return pers; + } + return NULL; +} + +static inline sector_t calc_dev_sboffset(struct block_device *bdev) +{ + sector_t size = bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + return MD_NEW_SIZE_BLOCKS(size); +} + +static sector_t calc_dev_size(mdk_rdev_t *rdev, unsigned chunk_size) +{ + sector_t size; + + size = rdev->sb_offset; + + if (chunk_size) + size &= ~((sector_t)chunk_size/1024 - 1); + return size; +} + +static int alloc_disk_sb(mdk_rdev_t * rdev) +{ + if (rdev->sb_page) + MD_BUG(); + + rdev->sb_page = alloc_page(GFP_KERNEL); + if (!rdev->sb_page) { + printk(KERN_ALERT "md: out of memory.\n"); + return -EINVAL; + } + + return 0; +} + +static void free_disk_sb(mdk_rdev_t * rdev) +{ + if (rdev->sb_page) { + put_page(rdev->sb_page); + rdev->sb_loaded = 0; + rdev->sb_page = NULL; + rdev->sb_offset = 0; + rdev->size = 0; + } +} + + +static int super_written(struct bio *bio, unsigned int bytes_done, int error) +{ + mdk_rdev_t *rdev = bio->bi_private; + mddev_t *mddev = rdev->mddev; + if (bio->bi_size) + return 1; + + if (error || !test_bit(BIO_UPTODATE, &bio->bi_flags)) + md_error(mddev, rdev); + + if (atomic_dec_and_test(&mddev->pending_writes)) + wake_up(&mddev->sb_wait); + bio_put(bio); + return 0; +} + +static int super_written_barrier(struct bio *bio, unsigned int bytes_done, int error) +{ + struct bio *bio2 = bio->bi_private; + mdk_rdev_t *rdev = bio2->bi_private; + mddev_t *mddev = rdev->mddev; + if (bio->bi_size) + return 1; + + if (!test_bit(BIO_UPTODATE, &bio->bi_flags) && + error == -EOPNOTSUPP) { + unsigned long flags; + /* barriers don't appear to be supported :-( */ + set_bit(BarriersNotsupp, &rdev->flags); + mddev->barriers_work = 0; + spin_lock_irqsave(&mddev->write_lock, flags); + bio2->bi_next = mddev->biolist; + mddev->biolist = bio2; + spin_unlock_irqrestore(&mddev->write_lock, flags); + wake_up(&mddev->sb_wait); + bio_put(bio); + return 0; + } + bio_put(bio2); + bio->bi_private = rdev; + return super_written(bio, bytes_done, error); +} + +void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev, + sector_t sector, int size, struct page *page) +{ + /* write first size bytes of page to sector of rdev + * Increment mddev->pending_writes before returning + * and decrement it on completion, waking up sb_wait + * if zero is reached. + * If an error occurred, call md_error + * + * As we might need to resubmit the request if BIO_RW_BARRIER + * causes ENOTSUPP, we allocate a spare bio... + */ + struct bio *bio = bio_alloc(GFP_NOIO, 1); + int rw = (1<bi_bdev = rdev->bdev; + bio->bi_sector = sector; + bio_add_page(bio, page, size, 0); + bio->bi_private = rdev; + bio->bi_end_io = super_written; + bio->bi_rw = rw; + + atomic_inc(&mddev->pending_writes); + if (!test_bit(BarriersNotsupp, &rdev->flags)) { + struct bio *rbio; + rw |= (1<bi_private = bio; + rbio->bi_end_io = super_written_barrier; + submit_bio(rw, rbio); + } else + submit_bio(rw, bio); +} + +void md_super_wait(mddev_t *mddev) +{ + /* wait for all superblock writes that were scheduled to complete. + * if any had to be retried (due to BARRIER problems), retry them + */ + DEFINE_WAIT(wq); + for(;;) { + prepare_to_wait(&mddev->sb_wait, &wq, TASK_UNINTERRUPTIBLE); + if (atomic_read(&mddev->pending_writes)==0) + break; + while (mddev->biolist) { + struct bio *bio; + spin_lock_irq(&mddev->write_lock); + bio = mddev->biolist; + mddev->biolist = bio->bi_next ; + bio->bi_next = NULL; + spin_unlock_irq(&mddev->write_lock); + submit_bio(bio->bi_rw, bio); + } + schedule(); + } + finish_wait(&mddev->sb_wait, &wq); +} + +static int bi_complete(struct bio *bio, unsigned int bytes_done, int error) +{ + if (bio->bi_size) + return 1; + + complete((struct completion*)bio->bi_private); + return 0; +} + +int sync_page_io(struct block_device *bdev, sector_t sector, int size, + struct page *page, int rw) +{ + struct bio *bio = bio_alloc(GFP_NOIO, 1); + struct completion event; + int ret; + + rw |= (1 << BIO_RW_SYNC); + + bio->bi_bdev = bdev; + bio->bi_sector = sector; + bio_add_page(bio, page, size, 0); + init_completion(&event); + bio->bi_private = &event; + bio->bi_end_io = bi_complete; + submit_bio(rw, bio); + wait_for_completion(&event); + + ret = test_bit(BIO_UPTODATE, &bio->bi_flags); + bio_put(bio); + return ret; +} +EXPORT_SYMBOL_GPL(sync_page_io); + +static int read_disk_sb(mdk_rdev_t * rdev, int size) +{ + char b[BDEVNAME_SIZE]; + if (!rdev->sb_page) { + MD_BUG(); + return -EINVAL; + } + if (rdev->sb_loaded) + return 0; + + + if (!sync_page_io(rdev->bdev, rdev->sb_offset<<1, size, rdev->sb_page, READ)) + goto fail; + rdev->sb_loaded = 1; + return 0; + +fail: + printk(KERN_WARNING "md: disabled device %s, could not read superblock.\n", + bdevname(rdev->bdev,b)); + return -EINVAL; +} + +static int uuid_equal(mdp_super_t *sb1, mdp_super_t *sb2) +{ + if ( (sb1->set_uuid0 == sb2->set_uuid0) && + (sb1->set_uuid1 == sb2->set_uuid1) && + (sb1->set_uuid2 == sb2->set_uuid2) && + (sb1->set_uuid3 == sb2->set_uuid3)) + + return 1; + + return 0; +} + + +static int sb_equal(mdp_super_t *sb1, mdp_super_t *sb2) +{ + int ret; + mdp_super_t *tmp1, *tmp2; + + tmp1 = kmalloc(sizeof(*tmp1),GFP_KERNEL); + tmp2 = kmalloc(sizeof(*tmp2),GFP_KERNEL); + + if (!tmp1 || !tmp2) { + ret = 0; + printk(KERN_INFO "md.c: sb1 is not equal to sb2!\n"); + goto abort; + } + + *tmp1 = *sb1; + *tmp2 = *sb2; + + /* + * nr_disks is not constant + */ + tmp1->nr_disks = 0; + tmp2->nr_disks = 0; + + if (memcmp(tmp1, tmp2, MD_SB_GENERIC_CONSTANT_WORDS * 4)) + ret = 0; + else + ret = 1; + +abort: + kfree(tmp1); + kfree(tmp2); + return ret; +} + +static unsigned int calc_sb_csum(mdp_super_t * sb) +{ + unsigned int disk_csum, csum; + + disk_csum = sb->sb_csum; + sb->sb_csum = 0; + csum = csum_partial((void *)sb, MD_SB_BYTES, 0); + sb->sb_csum = disk_csum; + return csum; +} + + +/* + * Handle superblock details. + * We want to be able to handle multiple superblock formats + * so we have a common interface to them all, and an array of + * different handlers. + * We rely on user-space to write the initial superblock, and support + * reading and updating of superblocks. + * Interface methods are: + * int load_super(mdk_rdev_t *dev, mdk_rdev_t *refdev, int minor_version) + * loads and validates a superblock on dev. + * if refdev != NULL, compare superblocks on both devices + * Return: + * 0 - dev has a superblock that is compatible with refdev + * 1 - dev has a superblock that is compatible and newer than refdev + * so dev should be used as the refdev in future + * -EINVAL superblock incompatible or invalid + * -othererror e.g. -EIO + * + * int validate_super(mddev_t *mddev, mdk_rdev_t *dev) + * Verify that dev is acceptable into mddev. + * The first time, mddev->raid_disks will be 0, and data from + * dev should be merged in. Subsequent calls check that dev + * is new enough. Return 0 or -EINVAL + * + * void sync_super(mddev_t *mddev, mdk_rdev_t *dev) + * Update the superblock for rdev with data in mddev + * This does not write to disc. + * + */ + +struct super_type { + char *name; + struct module *owner; + int (*load_super)(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version); + int (*validate_super)(mddev_t *mddev, mdk_rdev_t *rdev); + void (*sync_super)(mddev_t *mddev, mdk_rdev_t *rdev); +}; + +/* + * load_super for 0.90.0 + */ +static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) +{ + char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; + mdp_super_t *sb; + int ret; + sector_t sb_offset; + + /* + * Calculate the position of the superblock, + * it's at the end of the disk. + * + * It also happens to be a multiple of 4Kb. + */ + sb_offset = calc_dev_sboffset(rdev->bdev); + rdev->sb_offset = sb_offset; + + ret = read_disk_sb(rdev, MD_SB_BYTES); + if (ret) return ret; + + ret = -EINVAL; + + bdevname(rdev->bdev, b); + sb = (mdp_super_t*)page_address(rdev->sb_page); + + if (sb->md_magic != MD_SB_MAGIC) { + printk(KERN_ERR "md: invalid raid superblock magic on %s\n", + b); + goto abort; + } + + if (sb->major_version != 0 || + sb->minor_version < 90 || + sb->minor_version > 91) { + printk(KERN_WARNING "Bad version number %d.%d on %s\n", + sb->major_version, sb->minor_version, + b); + goto abort; + } + + if (sb->raid_disks <= 0) + goto abort; + + if (csum_fold(calc_sb_csum(sb)) != csum_fold(sb->sb_csum)) { + printk(KERN_WARNING "md: invalid superblock checksum on %s\n", + b); + goto abort; + } + + rdev->preferred_minor = sb->md_minor; + rdev->data_offset = 0; + rdev->sb_size = MD_SB_BYTES; + + if (sb->level == LEVEL_MULTIPATH) + rdev->desc_nr = -1; + else + rdev->desc_nr = sb->this_disk.number; + + if (refdev == 0) + ret = 1; + else { + __u64 ev1, ev2; + mdp_super_t *refsb = (mdp_super_t*)page_address(refdev->sb_page); + if (!uuid_equal(refsb, sb)) { + printk(KERN_WARNING "md: %s has different UUID to %s\n", + b, bdevname(refdev->bdev,b2)); + goto abort; + } + if (!sb_equal(refsb, sb)) { + printk(KERN_WARNING "md: %s has same UUID" + " but different superblock to %s\n", + b, bdevname(refdev->bdev, b2)); + goto abort; + } + ev1 = md_event(sb); + ev2 = md_event(refsb); + if (ev1 > ev2) + ret = 1; + else + ret = 0; + } + rdev->size = calc_dev_size(rdev, sb->chunk_size); + + if (rdev->size < sb->size && sb->level > 1) + /* "this cannot possibly happen" ... */ + ret = -EINVAL; + + abort: + return ret; +} + +/* + * validate_super for 0.90.0 + */ +static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev) +{ + mdp_disk_t *desc; + mdp_super_t *sb = (mdp_super_t *)page_address(rdev->sb_page); + __u64 ev1 = md_event(sb); + + rdev->raid_disk = -1; + rdev->flags = 0; + if (mddev->raid_disks == 0) { + mddev->major_version = 0; + mddev->minor_version = sb->minor_version; + mddev->patch_version = sb->patch_version; + mddev->persistent = 1; + mddev->external = 0; + mddev->chunk_size = sb->chunk_size; + mddev->ctime = sb->ctime; + mddev->utime = sb->utime; + mddev->level = sb->level; + mddev->clevel[0] = 0; + mddev->layout = sb->layout; + mddev->raid_disks = sb->raid_disks; + mddev->size = sb->size; + mddev->events = ev1; + mddev->bitmap_offset = 0; + mddev->default_bitmap_offset = MD_SB_BYTES >> 9; + + if (mddev->minor_version >= 91) { + mddev->reshape_position = sb->reshape_position; + mddev->delta_disks = sb->delta_disks; + mddev->new_level = sb->new_level; + mddev->new_layout = sb->new_layout; + mddev->new_chunk = sb->new_chunk; + } else { + mddev->reshape_position = MaxSector; + mddev->delta_disks = 0; + mddev->new_level = mddev->level; + mddev->new_layout = mddev->layout; + mddev->new_chunk = mddev->chunk_size; + } + + if (sb->state & (1<recovery_cp = MaxSector; + else { + if (sb->events_hi == sb->cp_events_hi && + sb->events_lo == sb->cp_events_lo) { + mddev->recovery_cp = sb->recovery_cp; + } else + mddev->recovery_cp = 0; + } + + memcpy(mddev->uuid+0, &sb->set_uuid0, 4); + memcpy(mddev->uuid+4, &sb->set_uuid1, 4); + memcpy(mddev->uuid+8, &sb->set_uuid2, 4); + memcpy(mddev->uuid+12,&sb->set_uuid3, 4); + + mddev->max_disks = MD_SB_DISKS; + + if (sb->state & (1<bitmap_file == NULL) { + if (mddev->level != 1 && mddev->level != 4 + && mddev->level != 5 && mddev->level != 6 + && mddev->level != 10) { + /* FIXME use a better test */ + printk(KERN_WARNING "md: bitmaps not supported for this level.\n"); + return -EINVAL; + } + mddev->bitmap_offset = mddev->default_bitmap_offset; + } + + } else if (mddev->pers == NULL) { + /* Insist on good event counter while assembling */ + ++ev1; + if (ev1 < mddev->events) + return -EINVAL; + } else if (mddev->bitmap) { + /* if adding to array with a bitmap, then we can accept an + * older device ... but not too old. + */ + if (ev1 < mddev->bitmap->events_cleared) + return 0; + } else { + if (ev1 < mddev->events) + /* just a hot-add of a new device, leave raid_disk at -1 */ + return 0; + } + + if (mddev->level != LEVEL_MULTIPATH) { + desc = sb->disks + rdev->desc_nr; + + if (desc->state & (1<flags); + else if (desc->state & (1<raid_disk < mddev->raid_disks */) { + set_bit(In_sync, &rdev->flags); + rdev->raid_disk = desc->raid_disk; + } + if (desc->state & (1<flags); + } else /* MULTIPATH are always insync */ + set_bit(In_sync, &rdev->flags); + return 0; +} + +/* + * sync_super for 0.90.0 + */ +static void super_90_sync(mddev_t *mddev, mdk_rdev_t *rdev) +{ + mdp_super_t *sb; + struct list_head *tmp; + mdk_rdev_t *rdev2; + int next_spare = mddev->raid_disks; + + + /* make rdev->sb match mddev data.. + * + * 1/ zero out disks + * 2/ Add info for each disk, keeping track of highest desc_nr (next_spare); + * 3/ any empty disks < next_spare become removed + * + * disks[0] gets initialised to REMOVED because + * we cannot be sure from other fields if it has + * been initialised or not. + */ + int i; + int active=0, working=0,failed=0,spare=0,nr_disks=0; + + rdev->sb_size = MD_SB_BYTES; + + sb = (mdp_super_t*)page_address(rdev->sb_page); + + memset(sb, 0, sizeof(*sb)); + + sb->md_magic = MD_SB_MAGIC; + sb->major_version = mddev->major_version; + sb->patch_version = mddev->patch_version; + sb->gvalid_words = 0; /* ignored */ + memcpy(&sb->set_uuid0, mddev->uuid+0, 4); + memcpy(&sb->set_uuid1, mddev->uuid+4, 4); + memcpy(&sb->set_uuid2, mddev->uuid+8, 4); + memcpy(&sb->set_uuid3, mddev->uuid+12,4); + + sb->ctime = mddev->ctime; + sb->level = mddev->level; + sb->size = mddev->size; + sb->raid_disks = mddev->raid_disks; + sb->md_minor = mddev->md_minor; + sb->not_persistent = 0; + sb->utime = mddev->utime; + sb->state = 0; + sb->events_hi = (mddev->events>>32); + sb->events_lo = (u32)mddev->events; + + if (mddev->reshape_position == MaxSector) + sb->minor_version = 90; + else { + sb->minor_version = 91; + sb->reshape_position = mddev->reshape_position; + sb->new_level = mddev->new_level; + sb->delta_disks = mddev->delta_disks; + sb->new_layout = mddev->new_layout; + sb->new_chunk = mddev->new_chunk; + } + mddev->minor_version = sb->minor_version; + if (mddev->in_sync) + { + sb->recovery_cp = mddev->recovery_cp; + sb->cp_events_hi = (mddev->events>>32); + sb->cp_events_lo = (u32)mddev->events; + if (mddev->recovery_cp == MaxSector) + sb->state = (1<< MD_SB_CLEAN); + } else + sb->recovery_cp = 0; + + sb->layout = mddev->layout; + sb->chunk_size = mddev->chunk_size; + + if (mddev->bitmap && mddev->bitmap_file == NULL) + sb->state |= (1<disks[0].state = (1<raid_disk >= 0 && test_bit(In_sync, &rdev2->flags) + && !test_bit(Faulty, &rdev2->flags)) + desc_nr = rdev2->raid_disk; + else + desc_nr = next_spare++; + rdev2->desc_nr = desc_nr; + d = &sb->disks[rdev2->desc_nr]; + nr_disks++; + d->number = rdev2->desc_nr; + d->major = MAJOR(rdev2->bdev->bd_dev); + d->minor = MINOR(rdev2->bdev->bd_dev); + if (rdev2->raid_disk >= 0 && test_bit(In_sync, &rdev2->flags) + && !test_bit(Faulty, &rdev2->flags)) + d->raid_disk = rdev2->raid_disk; + else + d->raid_disk = rdev2->desc_nr; /* compatibility */ + if (test_bit(Faulty, &rdev2->flags)) + d->state = (1<flags)) { + d->state = (1<state |= (1<state = 0; + spare++; + working++; + } + if (test_bit(WriteMostly, &rdev2->flags)) + d->state |= (1<raid_disks ; i++) { + mdp_disk_t *d = &sb->disks[i]; + if (d->state == 0 && d->number == 0) { + d->number = i; + d->raid_disk = i; + d->state = (1<state |= (1<nr_disks = nr_disks; + sb->active_disks = active; + sb->working_disks = working; + sb->failed_disks = failed; + sb->spare_disks = spare; + + sb->this_disk = sb->disks[rdev->desc_nr]; + sb->sb_csum = calc_sb_csum(sb); +} + +/* + * version 1 superblock + */ + +static unsigned int calc_sb_1_csum(struct mdp_superblock_1 * sb) +{ + unsigned int disk_csum, csum; + unsigned long long newcsum; + int size = 256 + le32_to_cpu(sb->max_dev)*2; + unsigned int *isuper = (unsigned int*)sb; + int i; + + disk_csum = sb->sb_csum; + sb->sb_csum = 0; + newcsum = 0; + for (i=0; size>=4; size -= 4 ) + newcsum += le32_to_cpu(*isuper++); + + if (size == 2) + newcsum += le16_to_cpu(*(unsigned short*) isuper); + + csum = (newcsum & 0xffffffff) + (newcsum >> 32); + sb->sb_csum = disk_csum; + return cpu_to_le32(csum); +} + +static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version) +{ + struct mdp_superblock_1 *sb; + int ret; + sector_t sb_offset; + char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; + int bmask; + + /* + * Calculate the position of the superblock. + * It is always aligned to a 4K boundary and + * depeding on minor_version, it can be: + * 0: At least 8K, but less than 12K, from end of device + * 1: At start of device + * 2: 4K from start of device. + */ + switch(minor_version) { + case 0: + sb_offset = rdev->bdev->bd_inode->i_size >> 9; + sb_offset -= 8*2; + sb_offset &= ~(sector_t)(4*2-1); + /* convert from sectors to K */ + sb_offset /= 2; + break; + case 1: + sb_offset = 0; + break; + case 2: + sb_offset = 4; + break; + default: + return -EINVAL; + } + rdev->sb_offset = sb_offset; + + /* superblock is rarely larger than 1K, but it can be larger, + * and it is safe to read 4k, so we do that + */ + ret = read_disk_sb(rdev, 4096); + if (ret) return ret; + + + sb = (struct mdp_superblock_1*)page_address(rdev->sb_page); + + if (sb->magic != cpu_to_le32(MD_SB_MAGIC) || + sb->major_version != cpu_to_le32(1) || + le32_to_cpu(sb->max_dev) > (4096-256)/2 || + le64_to_cpu(sb->super_offset) != (rdev->sb_offset<<1) || + (le32_to_cpu(sb->feature_map) & ~MD_FEATURE_ALL) != 0) + return -EINVAL; + + if (calc_sb_1_csum(sb) != sb->sb_csum) { + printk("md: invalid superblock checksum on %s\n", + bdevname(rdev->bdev,b)); + return -EINVAL; + } + if (le64_to_cpu(sb->data_size) < 10) { + printk("md: data_size too small on %s\n", + bdevname(rdev->bdev,b)); + return -EINVAL; + } + rdev->preferred_minor = 0xffff; + rdev->data_offset = le64_to_cpu(sb->data_offset); + atomic_set(&rdev->corrected_errors, le32_to_cpu(sb->cnt_corrected_read)); + + rdev->sb_size = le32_to_cpu(sb->max_dev) * 2 + 256; + bmask = queue_hardsect_size(rdev->bdev->bd_disk->queue)-1; + if (rdev->sb_size & bmask) + rdev-> sb_size = (rdev->sb_size | bmask)+1; + + if (refdev == 0) + ret = 1; + else { + __u64 ev1, ev2; + struct mdp_superblock_1 *refsb = + (struct mdp_superblock_1*)page_address(refdev->sb_page); + + if (memcmp(sb->set_uuid, refsb->set_uuid, 16) != 0 || + sb->level != refsb->level || + sb->layout != refsb->layout || + sb->chunksize != refsb->chunksize) { + printk(KERN_WARNING "md: %s has strangely different" + " superblock to %s\n", + bdevname(rdev->bdev,b), + bdevname(refdev->bdev,b2)); + return -EINVAL; + } + ev1 = le64_to_cpu(sb->events); + ev2 = le64_to_cpu(refsb->events); + + if (ev1 > ev2) + ret = 1; + else + ret = 0; + } + if (minor_version) + rdev->size = ((rdev->bdev->bd_inode->i_size>>9) - le64_to_cpu(sb->data_offset)) / 2; + else + rdev->size = rdev->sb_offset; + if (rdev->size < le64_to_cpu(sb->data_size)/2) + return -EINVAL; + rdev->size = le64_to_cpu(sb->data_size)/2; + if (le32_to_cpu(sb->chunksize)) + rdev->size &= ~((sector_t)le32_to_cpu(sb->chunksize)/2 - 1); + + if (le32_to_cpu(sb->size) > rdev->size*2) + return -EINVAL; + return ret; +} + +static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev) +{ + struct mdp_superblock_1 *sb = (struct mdp_superblock_1*)page_address(rdev->sb_page); + __u64 ev1 = le64_to_cpu(sb->events); + + rdev->raid_disk = -1; + rdev->flags = 0; + if (mddev->raid_disks == 0) { + mddev->major_version = 1; + mddev->patch_version = 0; + mddev->persistent = 1; + mddev->external = 0; + mddev->chunk_size = le32_to_cpu(sb->chunksize) << 9; + mddev->ctime = le64_to_cpu(sb->ctime) & ((1ULL << 32)-1); + mddev->utime = le64_to_cpu(sb->utime) & ((1ULL << 32)-1); + mddev->level = le32_to_cpu(sb->level); + mddev->clevel[0] = 0; + mddev->layout = le32_to_cpu(sb->layout); + mddev->raid_disks = le32_to_cpu(sb->raid_disks); + mddev->size = le64_to_cpu(sb->size)/2; + mddev->events = ev1; + mddev->bitmap_offset = 0; + mddev->default_bitmap_offset = 1024 >> 9; + + mddev->recovery_cp = le64_to_cpu(sb->resync_offset); + memcpy(mddev->uuid, sb->set_uuid, 16); + + mddev->max_disks = (4096-256)/2; + + if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_BITMAP_OFFSET) && + mddev->bitmap_file == NULL ) { + if (mddev->level != 1 && mddev->level != 5 && mddev->level != 6 + && mddev->level != 10) { + printk(KERN_WARNING "md: bitmaps not supported for this level.\n"); + return -EINVAL; + } + mddev->bitmap_offset = (__s32)le32_to_cpu(sb->bitmap_offset); + } + if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_RESHAPE_ACTIVE)) { + mddev->reshape_position = le64_to_cpu(sb->reshape_position); + mddev->delta_disks = le32_to_cpu(sb->delta_disks); + mddev->new_level = le32_to_cpu(sb->new_level); + mddev->new_layout = le32_to_cpu(sb->new_layout); + mddev->new_chunk = le32_to_cpu(sb->new_chunk)<<9; + } else { + mddev->reshape_position = MaxSector; + mddev->delta_disks = 0; + mddev->new_level = mddev->level; + mddev->new_layout = mddev->layout; + mddev->new_chunk = mddev->chunk_size; + } + + } else if (mddev->pers == NULL) { + /* Insist of good event counter while assembling */ + ++ev1; + if (ev1 < mddev->events) + return -EINVAL; + } else if (mddev->bitmap) { + /* If adding to array with a bitmap, then we can accept an + * older device, but not too old. + */ + if (ev1 < mddev->bitmap->events_cleared) + return 0; + } else { + if (ev1 < mddev->events) + /* just a hot-add of a new device, leave raid_disk at -1 */ + return 0; + } + if (mddev->level != LEVEL_MULTIPATH) { + int role; + rdev->desc_nr = le32_to_cpu(sb->dev_number); + role = le16_to_cpu(sb->dev_roles[rdev->desc_nr]); + switch(role) { + case 0xffff: /* spare */ + break; + case 0xfffe: /* faulty */ + set_bit(Faulty, &rdev->flags); + break; + default: + if ((le32_to_cpu(sb->feature_map) & + MD_FEATURE_RECOVERY_OFFSET)) + rdev->recovery_offset = le64_to_cpu(sb->recovery_offset); + else + set_bit(In_sync, &rdev->flags); + rdev->raid_disk = role; + break; + } + if (sb->devflags & WriteMostly1) + set_bit(WriteMostly, &rdev->flags); + } else /* MULTIPATH are always insync */ + set_bit(In_sync, &rdev->flags); + + return 0; +} + +static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev) +{ + struct mdp_superblock_1 *sb; + struct list_head *tmp; + mdk_rdev_t *rdev2; + int max_dev, i; + /* make rdev->sb match mddev and rdev data. */ + + sb = (struct mdp_superblock_1*)page_address(rdev->sb_page); + + sb->feature_map = 0; + sb->pad0 = 0; + sb->recovery_offset = cpu_to_le64(0); + memset(sb->pad1, 0, sizeof(sb->pad1)); + memset(sb->pad2, 0, sizeof(sb->pad2)); + memset(sb->pad3, 0, sizeof(sb->pad3)); + + sb->utime = cpu_to_le64((__u64)mddev->utime); + sb->events = cpu_to_le64(mddev->events); + if (mddev->in_sync) + sb->resync_offset = cpu_to_le64(mddev->recovery_cp); + else + sb->resync_offset = cpu_to_le64(0); + + sb->cnt_corrected_read = atomic_read(&rdev->corrected_errors); + + sb->raid_disks = cpu_to_le32(mddev->raid_disks); + sb->size = cpu_to_le64(mddev->size<<1); + + if (mddev->bitmap && mddev->bitmap_file == NULL) { + sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_offset); + sb->feature_map = cpu_to_le32(MD_FEATURE_BITMAP_OFFSET); + } + + if (rdev->raid_disk >= 0 && + !test_bit(In_sync, &rdev->flags) && + rdev->recovery_offset > 0) { + sb->feature_map |= cpu_to_le32(MD_FEATURE_RECOVERY_OFFSET); + sb->recovery_offset = cpu_to_le64(rdev->recovery_offset); + } + + if (mddev->reshape_position != MaxSector) { + sb->feature_map |= cpu_to_le32(MD_FEATURE_RESHAPE_ACTIVE); + sb->reshape_position = cpu_to_le64(mddev->reshape_position); + sb->new_layout = cpu_to_le32(mddev->new_layout); + sb->delta_disks = cpu_to_le32(mddev->delta_disks); + sb->new_level = cpu_to_le32(mddev->new_level); + sb->new_chunk = cpu_to_le32(mddev->new_chunk>>9); + } + + max_dev = 0; + ITERATE_RDEV(mddev,rdev2,tmp) + if (rdev2->desc_nr+1 > max_dev) + max_dev = rdev2->desc_nr+1; + + sb->max_dev = cpu_to_le32(max_dev); + for (i=0; idev_roles[i] = cpu_to_le16(0xfffe); + + ITERATE_RDEV(mddev,rdev2,tmp) { + i = rdev2->desc_nr; + if (test_bit(Faulty, &rdev2->flags)) + sb->dev_roles[i] = cpu_to_le16(0xfffe); + else if (test_bit(In_sync, &rdev2->flags)) + sb->dev_roles[i] = cpu_to_le16(rdev2->raid_disk); + else if (rdev2->raid_disk >= 0 && rdev2->recovery_offset > 0) + sb->dev_roles[i] = cpu_to_le16(rdev2->raid_disk); + else + sb->dev_roles[i] = cpu_to_le16(0xffff); + } + + sb->sb_csum = calc_sb_1_csum(sb); +} + + +static struct super_type super_types[] = { + [0] = { + .name = "0.90.0", + .owner = THIS_MODULE, + .load_super = super_90_load, + .validate_super = super_90_validate, + .sync_super = super_90_sync, + }, + [1] = { + .name = "md-1", + .owner = THIS_MODULE, + .load_super = super_1_load, + .validate_super = super_1_validate, + .sync_super = super_1_sync, + }, +}; + +static mdk_rdev_t * match_dev_unit(mddev_t *mddev, mdk_rdev_t *dev) +{ + struct list_head *tmp; + mdk_rdev_t *rdev; + + ITERATE_RDEV(mddev,rdev,tmp) + if (rdev->bdev->bd_contains == dev->bdev->bd_contains) + return rdev; + + return NULL; +} + +static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2) +{ + struct list_head *tmp; + mdk_rdev_t *rdev; + + ITERATE_RDEV(mddev1,rdev,tmp) + if (match_dev_unit(mddev2, rdev)) + return 1; + + return 0; +} + +static LIST_HEAD(pending_raid_disks); + +static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) +{ + mdk_rdev_t *same_pdev; + char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; + struct kobject *ko; + char *s; + + if (rdev->mddev) { + MD_BUG(); + return -EINVAL; + } + /* make sure rdev->size exceeds mddev->size */ + if (rdev->size && (mddev->size == 0 || rdev->size < mddev->size)) { + if (mddev->pers) + /* Cannot change size, so fail */ + return -ENOSPC; + else + mddev->size = rdev->size; + } + same_pdev = match_dev_unit(mddev, rdev); + if (same_pdev) + printk(KERN_WARNING + "%s: WARNING: %s appears to be on the same physical" + " disk as %s. True\n protection against single-disk" + " failure might be compromised.\n", + mdname(mddev), bdevname(rdev->bdev,b), + bdevname(same_pdev->bdev,b2)); + + /* Verify rdev->desc_nr is unique. + * If it is -1, assign a free number, else + * check number is not in use + */ + if (rdev->desc_nr < 0) { + int choice = 0; + if (mddev->pers) choice = mddev->raid_disks; + while (find_rdev_nr(mddev, choice)) + choice++; + rdev->desc_nr = choice; + } else { + if (find_rdev_nr(mddev, rdev->desc_nr)) + return -EBUSY; + } + bdevname(rdev->bdev,b); + if (kobject_set_name(&rdev->kobj, "dev-%s", b) < 0) + return -ENOMEM; + while ( (s=strchr(rdev->kobj.k_name, '/')) != NULL) + *s = '!'; + + list_add(&rdev->same_set, &mddev->disks); + rdev->mddev = mddev; + printk(KERN_INFO "md: bind<%s>\n", b); + + rdev->kobj.parent = &mddev->kobj; + kobject_add(&rdev->kobj); + + if (rdev->bdev->bd_part) + ko = &rdev->bdev->bd_part->kobj; + else + ko = &rdev->bdev->bd_disk->kobj; + sysfs_create_link(&rdev->kobj, ko, "block"); + bd_claim_by_disk(rdev->bdev, rdev, mddev->gendisk); + return 0; +} + +static void unbind_rdev_from_array(mdk_rdev_t * rdev) +{ + char b[BDEVNAME_SIZE]; + if (!rdev->mddev) { + MD_BUG(); + return; + } + bd_release_from_disk(rdev->bdev, rdev->mddev->gendisk); + list_del_init(&rdev->same_set); + printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b)); + rdev->mddev = NULL; + sysfs_remove_link(&rdev->kobj, "block"); + kobject_del(&rdev->kobj); +} + +/* + * prevent the device from being mounted, repartitioned or + * otherwise reused by a RAID array (or any other kernel + * subsystem), by bd_claiming the device. + */ +static int lock_rdev(mdk_rdev_t *rdev, dev_t dev) +{ + int err = 0; + struct block_device *bdev; + char b[BDEVNAME_SIZE]; + + bdev = open_partition_by_devnum(dev, FMODE_READ|FMODE_WRITE); + if (IS_ERR(bdev)) { + printk(KERN_ERR "md: could not open %s.\n", + __bdevname(dev, b)); + return PTR_ERR(bdev); + } + err = bd_claim(bdev, rdev); + if (err) { + printk(KERN_ERR "md: could not bd_claim %s.\n", + bdevname(bdev, b)); + blkdev_put_partition(bdev); + return err; + } + rdev->bdev = bdev; + return err; +} + +static void unlock_rdev(mdk_rdev_t *rdev) +{ + struct block_device *bdev = rdev->bdev; + rdev->bdev = NULL; + if (!bdev) + MD_BUG(); + bd_release(bdev); + blkdev_put_partition(bdev); +} + +void md_autodetect_dev(dev_t dev); + +static void export_rdev(mdk_rdev_t * rdev) +{ + char b[BDEVNAME_SIZE]; + printk(KERN_INFO "md: export_rdev(%s)\n", + bdevname(rdev->bdev,b)); + if (rdev->mddev) + MD_BUG(); + free_disk_sb(rdev); + list_del_init(&rdev->same_set); +#ifndef MODULE + md_autodetect_dev(rdev->bdev->bd_dev); +#endif + unlock_rdev(rdev); + kobject_put(&rdev->kobj); +} + +static void kick_rdev_from_array(mdk_rdev_t * rdev) +{ + unbind_rdev_from_array(rdev); + export_rdev(rdev); +} + +static void export_array(mddev_t *mddev) +{ + struct list_head *tmp; + mdk_rdev_t *rdev; + + ITERATE_RDEV(mddev,rdev,tmp) { + if (!rdev->mddev) { + MD_BUG(); + continue; + } + kick_rdev_from_array(rdev); + } + if (!list_empty(&mddev->disks)) + MD_BUG(); + mddev->raid_disks = 0; + mddev->major_version = 0; +} + +static void print_desc(mdp_disk_t *desc) +{ + printk(" DISK\n", desc->number, + desc->major,desc->minor,desc->raid_disk,desc->state); +} + +static void print_sb(mdp_super_t *sb) +{ + int i; + + printk(KERN_INFO + "md: SB: (V:%d.%d.%d) ID:<%08x.%08x.%08x.%08x> CT:%08x\n", + sb->major_version, sb->minor_version, sb->patch_version, + sb->set_uuid0, sb->set_uuid1, sb->set_uuid2, sb->set_uuid3, + sb->ctime); + printk(KERN_INFO "md: L%d S%08d ND:%d RD:%d md%d LO:%d CS:%d\n", + sb->level, sb->size, sb->nr_disks, sb->raid_disks, + sb->md_minor, sb->layout, sb->chunk_size); + printk(KERN_INFO "md: UT:%08x ST:%d AD:%d WD:%d" + " FD:%d SD:%d CSUM:%08x E:%08lx\n", + sb->utime, sb->state, sb->active_disks, sb->working_disks, + sb->failed_disks, sb->spare_disks, + sb->sb_csum, (unsigned long)sb->events_lo); + + printk(KERN_INFO); + for (i = 0; i < MD_SB_DISKS; i++) { + mdp_disk_t *desc; + + desc = sb->disks + i; + if (desc->number || desc->major || desc->minor || + desc->raid_disk || (desc->state && (desc->state != 4))) { + printk(" D %2d: ", i); + print_desc(desc); + } + } + printk(KERN_INFO "md: THIS: "); + print_desc(&sb->this_disk); + +} + +static void print_rdev(mdk_rdev_t *rdev) +{ + char b[BDEVNAME_SIZE]; + printk(KERN_INFO "md: rdev %s, SZ:%08llu F:%d S:%d DN:%u\n", + bdevname(rdev->bdev,b), (unsigned long long)rdev->size, + test_bit(Faulty, &rdev->flags), test_bit(In_sync, &rdev->flags), + rdev->desc_nr); + if (rdev->sb_loaded) { + printk(KERN_INFO "md: rdev superblock:\n"); + print_sb((mdp_super_t*)page_address(rdev->sb_page)); + } else + printk(KERN_INFO "md: no rdev superblock!\n"); +} + +static void md_print_devices(void) +{ + struct list_head *tmp, *tmp2; + mdk_rdev_t *rdev; + mddev_t *mddev; + char b[BDEVNAME_SIZE]; + + printk("\n"); + printk("md: **********************************\n"); + printk("md: * *\n"); + printk("md: **********************************\n"); + ITERATE_MDDEV(mddev,tmp) { + + if (mddev->bitmap) + bitmap_print_sb(mddev->bitmap); + else + printk("%s: ", mdname(mddev)); + ITERATE_RDEV(mddev,rdev,tmp2) + printk("<%s>", bdevname(rdev->bdev,b)); + printk("\n"); + + ITERATE_RDEV(mddev,rdev,tmp2) + print_rdev(rdev); + } + printk("md: **********************************\n"); + printk("\n"); +} + + +static void sync_sbs(mddev_t * mddev, int nospares) +{ + /* Update each superblock (in-memory image), but + * if we are allowed to, skip spares which already + * have the right event counter, or have one earlier + * (which would mean they aren't being marked as dirty + * with the rest of the array) + */ + mdk_rdev_t *rdev; + struct list_head *tmp; + + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev->sb_events == mddev->events || + (nospares && + rdev->raid_disk < 0 && + (rdev->sb_events&1)==0 && + rdev->sb_events+1 == mddev->events)) { + /* Don't update this superblock */ + rdev->sb_loaded = 2; + } else { + super_types[mddev->major_version]. + sync_super(mddev, rdev); + rdev->sb_loaded = 1; + } + } +} + +void md_update_sb(mddev_t * mddev) +{ + int err; + struct list_head *tmp; + mdk_rdev_t *rdev; + int sync_req; + int nospares = 0; + +repeat: + spin_lock_irq(&mddev->write_lock); + sync_req = mddev->in_sync; + mddev->utime = get_seconds(); + if (mddev->sb_dirty == 3) + /* just a clean<-> dirty transition, possibly leave spares alone, + * though if events isn't the right even/odd, we will have to do + * spares after all + */ + nospares = 1; + + /* If this is just a dirty<->clean transition, and the array is clean + * and 'events' is odd, we can roll back to the previous clean state */ + if (mddev->sb_dirty == 3 + && (mddev->in_sync && mddev->recovery_cp == MaxSector) + && (mddev->events & 1)) + mddev->events--; + else { + /* otherwise we have to go forward and ... */ + mddev->events ++; + if (!mddev->in_sync || mddev->recovery_cp != MaxSector) { /* not clean */ + /* .. if the array isn't clean, insist on an odd 'events' */ + if ((mddev->events&1)==0) { + mddev->events++; + nospares = 0; + } + } else { + /* otherwise insist on an even 'events' (for clean states) */ + if ((mddev->events&1)) { + mddev->events++; + nospares = 0; + } + } + } + + if (!mddev->events) { + /* + * oops, this 64-bit counter should never wrap. + * Either we are in around ~1 trillion A.C., assuming + * 1 reboot per second, or we have a bug: + */ + MD_BUG(); + mddev->events --; + } + + /* + * do not write anything to disk if using + * nonpersistent superblocks + */ + if (!mddev->persistent) { + if (!mddev->external) + mddev->sb_dirty = 0; + spin_unlock_irq(&mddev->write_lock); + wake_up(&mddev->sb_wait); + return; + } + mddev->sb_dirty = 2; + sync_sbs(mddev, nospares); + spin_unlock_irq(&mddev->write_lock); + + dprintk(KERN_INFO + "md: updating %s RAID superblock on device (in sync %d)\n", + mdname(mddev),mddev->in_sync); + + err = bitmap_update_sb(mddev->bitmap); + ITERATE_RDEV(mddev,rdev,tmp) { + char b[BDEVNAME_SIZE]; + dprintk(KERN_INFO "md: "); + if (rdev->sb_loaded != 1) + continue; /* no noise on spare devices */ + if (test_bit(Faulty, &rdev->flags)) + dprintk("(skipping faulty "); + + dprintk("%s ", bdevname(rdev->bdev,b)); + if (!test_bit(Faulty, &rdev->flags)) { + md_super_write(mddev,rdev, + rdev->sb_offset<<1, rdev->sb_size, + rdev->sb_page); + dprintk(KERN_INFO "(write) %s's sb offset: %llu\n", + bdevname(rdev->bdev,b), + (unsigned long long)rdev->sb_offset); + rdev->sb_events = mddev->events; + + } else + dprintk(")\n"); + if (mddev->level == LEVEL_MULTIPATH) + /* only need to write one superblock... */ + break; + } + md_super_wait(mddev); + /* if there was a failure, sb_dirty was set to 1, and we re-write super */ + + spin_lock_irq(&mddev->write_lock); + if (mddev->in_sync != sync_req|| mddev->sb_dirty == 1) { + /* have to write it out again */ + spin_unlock_irq(&mddev->write_lock); + goto repeat; + } + mddev->sb_dirty = 0; + spin_unlock_irq(&mddev->write_lock); + wake_up(&mddev->sb_wait); + +} +EXPORT_SYMBOL_GPL(md_update_sb); + +/* words written to sysfs files may, or my not, be \n terminated. + * We want to accept with case. For this we use cmd_match. + */ +static int cmd_match(const char *cmd, const char *str) +{ + /* See if cmd, written into a sysfs file, matches + * str. They must either be the same, or cmd can + * have a trailing newline + */ + while (*cmd && *str && *cmd == *str) { + cmd++; + str++; + } + if (*cmd == '\n') + cmd++; + if (*str || *cmd) + return 0; + return 1; +} + +struct rdev_sysfs_entry { + struct attribute attr; + ssize_t (*show)(mdk_rdev_t *, char *); + ssize_t (*store)(mdk_rdev_t *, const char *, size_t); +}; + +static ssize_t +state_show(mdk_rdev_t *rdev, char *page) +{ + char *sep = ""; + int len=0; + + if (test_bit(Faulty, &rdev->flags)) { + len+= sprintf(page+len, "%sfaulty",sep); + sep = ","; + } + if (test_bit(In_sync, &rdev->flags)) { + len += sprintf(page+len, "%sin_sync",sep); + sep = ","; + } + if (test_bit(WriteMostly, &rdev->flags)) { + len += sprintf(page+len, "%swrite_mostly",sep); + sep = ","; + } + if (!test_bit(Faulty, &rdev->flags) && + !test_bit(In_sync, &rdev->flags)) { + len += sprintf(page+len, "%sspare", sep); + sep = ","; + } + return len+sprintf(page+len, "\n"); +} + +static ssize_t +state_store(mdk_rdev_t *rdev, const char *buf, size_t len) +{ + /* can write + * faulty - simulates and error + * remove - disconnects the device + * writemostly - sets write_mostly + * -writemostly - clears write_mostly + */ + int err = -EINVAL; + if (cmd_match(buf, "faulty") && rdev->mddev->pers) { + md_error(rdev->mddev, rdev); + err = 0; + } else if (cmd_match(buf, "remove")) { + if (rdev->raid_disk >= 0) + err = -EBUSY; + else { + mddev_t *mddev = rdev->mddev; + kick_rdev_from_array(rdev); + md_update_sb(mddev); + md_new_event(mddev); + err = 0; + } + } else if (cmd_match(buf, "writemostly")) { + set_bit(WriteMostly, &rdev->flags); + err = 0; + } else if (cmd_match(buf, "-writemostly")) { + clear_bit(WriteMostly, &rdev->flags); + err = 0; + } + return err ? err : len; +} +static struct rdev_sysfs_entry rdev_state = +__ATTR(state, S_IRUGO|S_IWUSR, state_show, state_store); + +static ssize_t +super_show(mdk_rdev_t *rdev, char *page) +{ + if (rdev->sb_loaded && rdev->sb_size) { + memcpy(page, page_address(rdev->sb_page), rdev->sb_size); + return rdev->sb_size; + } else + return 0; +} +static struct rdev_sysfs_entry rdev_super = __ATTR_RO(super); + +static ssize_t +errors_show(mdk_rdev_t *rdev, char *page) +{ + return sprintf(page, "%d\n", atomic_read(&rdev->corrected_errors)); +} + +static ssize_t +errors_store(mdk_rdev_t *rdev, const char *buf, size_t len) +{ + char *e; + unsigned long n = simple_strtoul(buf, &e, 10); + if (*buf && (*e == 0 || *e == '\n')) { + atomic_set(&rdev->corrected_errors, n); + return len; + } + return -EINVAL; +} +static struct rdev_sysfs_entry rdev_errors = +__ATTR(errors, S_IRUGO|S_IWUSR, errors_show, errors_store); + +static ssize_t +slot_show(mdk_rdev_t *rdev, char *page) +{ + if (rdev->raid_disk < 0) + return sprintf(page, "none\n"); + else + return sprintf(page, "%d\n", rdev->raid_disk); +} + +static ssize_t +slot_store(mdk_rdev_t *rdev, const char *buf, size_t len) +{ + char *e; + int slot = simple_strtoul(buf, &e, 10); + if (strncmp(buf, "none", 4)==0) + slot = -1; + else if (e==buf || (*e && *e!= '\n')) + return -EINVAL; + if (rdev->mddev->pers) + /* Cannot set slot in active array (yet) */ + return -EBUSY; + if (slot >= rdev->mddev->raid_disks) + return -ENOSPC; + rdev->raid_disk = slot; + /* assume it is working */ + rdev->flags = 0; + set_bit(In_sync, &rdev->flags); + return len; +} + + +static struct rdev_sysfs_entry rdev_slot = +__ATTR(slot, S_IRUGO|S_IWUSR, slot_show, slot_store); + +static ssize_t +offset_show(mdk_rdev_t *rdev, char *page) +{ + return sprintf(page, "%llu\n", (unsigned long long)rdev->data_offset); +} + +static ssize_t +offset_store(mdk_rdev_t *rdev, const char *buf, size_t len) +{ + char *e; + unsigned long long offset = simple_strtoull(buf, &e, 10); + if (e==buf || (*e && *e != '\n')) + return -EINVAL; + if (rdev->mddev->pers) + return -EBUSY; + rdev->data_offset = offset; + return len; +} + +static struct rdev_sysfs_entry rdev_offset = +__ATTR(offset, S_IRUGO|S_IWUSR, offset_show, offset_store); + +static ssize_t +rdev_size_show(mdk_rdev_t *rdev, char *page) +{ + return sprintf(page, "%llu\n", (unsigned long long)rdev->size); +} + +static ssize_t +rdev_size_store(mdk_rdev_t *rdev, const char *buf, size_t len) +{ + char *e; + unsigned long long size = simple_strtoull(buf, &e, 10); + if (e==buf || (*e && *e != '\n')) + return -EINVAL; + if (rdev->mddev->pers) + return -EBUSY; + rdev->size = size; + if (size < rdev->mddev->size || rdev->mddev->size == 0) + rdev->mddev->size = size; + return len; +} + +static struct rdev_sysfs_entry rdev_size = +__ATTR(size, S_IRUGO|S_IWUSR, rdev_size_show, rdev_size_store); + +static struct attribute *rdev_default_attrs[] = { + &rdev_state.attr, + &rdev_super.attr, + &rdev_errors.attr, + &rdev_slot.attr, + &rdev_offset.attr, + &rdev_size.attr, + NULL, +}; +static ssize_t +rdev_attr_show(struct kobject *kobj, struct attribute *attr, char *page) +{ + struct rdev_sysfs_entry *entry = container_of(attr, struct rdev_sysfs_entry, attr); + mdk_rdev_t *rdev = container_of(kobj, mdk_rdev_t, kobj); + + if (!entry->show) + return -EIO; + return entry->show(rdev, page); +} + +static ssize_t +rdev_attr_store(struct kobject *kobj, struct attribute *attr, + const char *page, size_t length) +{ + struct rdev_sysfs_entry *entry = container_of(attr, struct rdev_sysfs_entry, attr); + mdk_rdev_t *rdev = container_of(kobj, mdk_rdev_t, kobj); + + if (!entry->store) + return -EIO; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + return entry->store(rdev, page, length); +} + +static void rdev_free(struct kobject *ko) +{ + mdk_rdev_t *rdev = container_of(ko, mdk_rdev_t, kobj); + kfree(rdev); +} +static struct sysfs_ops rdev_sysfs_ops = { + .show = rdev_attr_show, + .store = rdev_attr_store, +}; +static struct kobj_type rdev_ktype = { + .release = rdev_free, + .sysfs_ops = &rdev_sysfs_ops, + .default_attrs = rdev_default_attrs, +}; + +/* + * Import a device. If 'super_format' >= 0, then sanity check the superblock + * + * mark the device faulty if: + * + * - the device is nonexistent (zero size) + * - the device has no valid superblock + * + * a faulty rdev _never_ has rdev->sb set. + */ +static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_minor) +{ + char b[BDEVNAME_SIZE]; + int err; + mdk_rdev_t *rdev; + sector_t size; + + rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); + if (!rdev) { + printk(KERN_ERR "md: could not alloc mem for new device!\n"); + return ERR_PTR(-ENOMEM); + } + + if ((err = alloc_disk_sb(rdev))) + goto abort_free; + + err = lock_rdev(rdev, newdev); + if (err) + goto abort_free; + + rdev->kobj.parent = NULL; + rdev->kobj.ktype = &rdev_ktype; + kobject_init(&rdev->kobj); + + rdev->desc_nr = -1; + rdev->flags = 0; + rdev->data_offset = 0; + rdev->sb_events = 0; + atomic_set(&rdev->nr_pending, 0); + atomic_set(&rdev->read_errors, 0); + atomic_set(&rdev->corrected_errors, 0); + + size = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + if (!size) { + printk(KERN_WARNING + "md: %s has zero or unknown size, marking faulty!\n", + bdevname(rdev->bdev,b)); + err = -EINVAL; + goto abort_free; + } + + if (super_format >= 0) { + err = super_types[super_format]. + load_super(rdev, NULL, super_minor); + if (err == -EINVAL) { + printk(KERN_WARNING + "md: %s has invalid sb, not importing!\n", + bdevname(rdev->bdev,b)); + goto abort_free; + } + if (err < 0) { + printk(KERN_WARNING + "md: could not read %s's sb, not importing!\n", + bdevname(rdev->bdev,b)); + goto abort_free; + } + } + INIT_LIST_HEAD(&rdev->same_set); + + return rdev; + +abort_free: + if (rdev->sb_page) { + if (rdev->bdev) + unlock_rdev(rdev); + free_disk_sb(rdev); + } + kfree(rdev); + return ERR_PTR(err); +} + +/* + * Check a full RAID array for plausibility + */ + + +static void analyze_sbs(mddev_t * mddev) +{ + int i; + struct list_head *tmp; + mdk_rdev_t *rdev, *freshest; + char b[BDEVNAME_SIZE]; + + freshest = NULL; + ITERATE_RDEV(mddev,rdev,tmp) + switch (super_types[mddev->major_version]. + load_super(rdev, freshest, mddev->minor_version)) { + case 1: + freshest = rdev; + break; + case 0: + break; + default: + printk( KERN_ERR \ + "md: fatal superblock inconsistency in %s" + " -- removing from array\n", + bdevname(rdev->bdev,b)); + kick_rdev_from_array(rdev); + } + + + super_types[mddev->major_version]. + validate_super(mddev, freshest); + + i = 0; + ITERATE_RDEV(mddev,rdev,tmp) { + if (rdev != freshest) + if (super_types[mddev->major_version]. + validate_super(mddev, rdev)) { + printk(KERN_WARNING "md: kicking non-fresh %s" + " from array!\n", + bdevname(rdev->bdev,b)); + kick_rdev_from_array(rdev); + continue; + } + if (mddev->level == LEVEL_MULTIPATH) { + rdev->desc_nr = i++; + rdev->raid_disk = rdev->desc_nr; + set_bit(In_sync, &rdev->flags); + } + } + + + + if (mddev->recovery_cp != MaxSector && + mddev->level >= 1) + printk(KERN_ERR "md: %s: raid array is not clean" + " -- starting background reconstruction\n", + mdname(mddev)); + +} + +static ssize_t +safe_delay_show(mddev_t *mddev, char *page) +{ + int msec = (mddev->safemode_delay*1000)/HZ; + return sprintf(page, "%d.%03d\n", msec/1000, msec%1000); +} +static ssize_t +safe_delay_store(mddev_t *mddev, const char *cbuf, size_t len) +{ + int scale=1; + int dot=0; + int i; + unsigned long msec; + char buf[30]; + char *e; + /* remove a period, and count digits after it */ + if (len >= sizeof(buf)) + return -EINVAL; + strlcpy(buf, cbuf, len); + buf[len] = 0; + for (i=0; isafemode_delay = 0; + else { + mddev->safemode_delay = (msec*HZ)/1000; + if (mddev->safemode_delay == 0) + mddev->safemode_delay = 1; + } + return len; +} +static struct md_sysfs_entry md_safe_delay = +__ATTR(safe_mode_delay, S_IRUGO|S_IWUSR,safe_delay_show, safe_delay_store); + +static ssize_t +level_show(mddev_t *mddev, char *page) +{ + struct mdk_personality *p = mddev->pers; + if (p) + return sprintf(page, "%s\n", p->name); + else if (mddev->clevel[0]) + return sprintf(page, "%s\n", mddev->clevel); + else if (mddev->level != LEVEL_NONE) + return sprintf(page, "%d\n", mddev->level); + else + return 0; +} + +static ssize_t +level_store(mddev_t *mddev, const char *buf, size_t len) +{ + int rv = len; + if (mddev->pers) + return -EBUSY; + if (len == 0) + return 0; + if (len >= sizeof(mddev->clevel)) + return -ENOSPC; + strncpy(mddev->clevel, buf, len); + if (mddev->clevel[len-1] == '\n') + len--; + mddev->clevel[len] = 0; + mddev->level = LEVEL_NONE; + return rv; +} + +static struct md_sysfs_entry md_level = +__ATTR(level, S_IRUGO|S_IWUSR, level_show, level_store); + + +static ssize_t +layout_show(mddev_t *mddev, char *page) +{ + /* just a number, not meaningful for all levels */ + return sprintf(page, "%d\n", mddev->layout); +} + +static ssize_t +layout_store(mddev_t *mddev, const char *buf, size_t len) +{ + char *e; + unsigned long n = simple_strtoul(buf, &e, 10); + if (mddev->pers) + return -EBUSY; + + if (!*buf || (*e && *e != '\n')) + return -EINVAL; + + mddev->layout = n; + return len; +} +static struct md_sysfs_entry md_layout = +__ATTR(layout, S_IRUGO|S_IWUSR, layout_show, layout_store); + + +static ssize_t +raid_disks_show(mddev_t *mddev, char *page) +{ + if (mddev->raid_disks == 0) + return 0; + return sprintf(page, "%d\n", mddev->raid_disks); +} + +static int update_raid_disks(mddev_t *mddev, int raid_disks); + +static ssize_t +raid_disks_store(mddev_t *mddev, const char *buf, size_t len) +{ + /* can only set raid_disks if array is not yet active */ + char *e; + int rv = 0; + unsigned long n = simple_strtoul(buf, &e, 10); + + if (!*buf || (*e && *e != '\n')) + return -EINVAL; + + if (mddev->pers) + rv = update_raid_disks(mddev, n); + else + mddev->raid_disks = n; + return rv ? rv : len; +} +static struct md_sysfs_entry md_raid_disks = +__ATTR(raid_disks, S_IRUGO|S_IWUSR, raid_disks_show, raid_disks_store); + +static ssize_t +chunk_size_show(mddev_t *mddev, char *page) +{ + return sprintf(page, "%d\n", mddev->chunk_size); +} + +static ssize_t +chunk_size_store(mddev_t *mddev, const char *buf, size_t len) +{ + /* can only set chunk_size if array is not yet active */ + char *e; + unsigned long n = simple_strtoul(buf, &e, 10); + + if (mddev->pers) + return -EBUSY; + if (!*buf || (*e && *e != '\n')) + return -EINVAL; + + mddev->chunk_size = n; + return len; +} +static struct md_sysfs_entry md_chunk_size = +__ATTR(chunk_size, S_IRUGO|S_IWUSR, chunk_size_show, chunk_size_store); + +static ssize_t +resync_start_show(mddev_t *mddev, char *page) +{ + return sprintf(page, "%llu\n", (unsigned long long)mddev->recovery_cp); +} + +static ssize_t +resync_start_store(mddev_t *mddev, const char *buf, size_t len) +{ + /* can only set chunk_size if array is not yet active */ + char *e; + unsigned long long n = simple_strtoull(buf, &e, 10); + + if (mddev->pers) + return -EBUSY; + if (!*buf || (*e && *e != '\n')) + return -EINVAL; + + mddev->recovery_cp = n; + return len; +} +static struct md_sysfs_entry md_resync_start = +__ATTR(resync_start, S_IRUGO|S_IWUSR, resync_start_show, resync_start_store); + +/* + * The array state can be: + * + * clear + * No devices, no size, no level + * Equivalent to STOP_ARRAY ioctl + * inactive + * May have some settings, but array is not active + * all IO results in error + * When written, doesn't tear down array, but just stops it + * suspended (not supported yet) + * All IO requests will block. The array can be reconfigured. + * Writing this, if accepted, will block until array is quiessent + * readonly + * no resync can happen. no superblocks get written. + * write requests fail + * read-auto + * like readonly, but behaves like 'clean' on a write request. + * + * clean - no pending writes, but otherwise active. + * When written to inactive array, starts without resync + * If a write request arrives then + * if metadata is known, mark 'dirty' and switch to 'active'. + * if not known, block and switch to write-pending + * If written to an active array that has pending writes, then fails. + * active + * fully active: IO and resync can be happening. + * When written to inactive array, starts with resync + * + * write-pending + * clean, but writes are blocked waiting for 'active' to be written. + * + * active-idle + * like active, but no writes have been seen for a while (100msec). + * + */ +enum array_state { clear, inactive, suspended, readonly, read_auto, clean, active, + write_pending, active_idle, bad_word}; +char *array_states[] = { + "clear", "inactive", "suspended", "readonly", "read-auto", "clean", "active", + "write-pending", "active-idle", NULL }; + +static int match_word(const char *word, char **list) +{ + int n; + for (n=0; list[n]; n++) + if (cmd_match(word, list[n])) + break; + return n; +} + +static ssize_t +array_state_show(mddev_t *mddev, char *page) +{ + enum array_state st = inactive; + + if (mddev->pers) + switch(mddev->ro) { + case 1: + st = readonly; + break; + case 2: + st = read_auto; + break; + case 0: + if (mddev->in_sync) + st = clean; + else if (mddev->sb_dirty) + st = write_pending; + else if (mddev->safemode) + st = active_idle; + else + st = active; + } + else { + if (list_empty(&mddev->disks) && + mddev->raid_disks == 0 && + mddev->size == 0) + st = clear; + else + st = inactive; + } + return sprintf(page, "%s\n", array_states[st]); +} + +static int do_md_stop(mddev_t * mddev, int ro); +static int do_md_run(mddev_t * mddev); +static int restart_array(mddev_t *mddev); + +static ssize_t +array_state_store(mddev_t *mddev, const char *buf, size_t len) +{ + int err = -EINVAL; + enum array_state st = match_word(buf, array_states); + switch(st) { + case bad_word: + break; + case clear: + /* stopping an active array */ + if (atomic_read(&mddev->active) > 1) + return -EBUSY; + err = do_md_stop(mddev, 0); + break; + case inactive: + /* stopping an active array */ + if (mddev->pers) { + if (atomic_read(&mddev->active) > 1) + return -EBUSY; + err = do_md_stop(mddev, 2); + } else + err = 0; /* already inactive */ + break; + case suspended: + break; /* not supported yet */ + case readonly: + if (mddev->pers) + err = do_md_stop(mddev, 1); + else { + mddev->ro = 1; + err = do_md_run(mddev); + } + break; + case read_auto: + /* stopping an active array */ + if (mddev->pers) { + err = do_md_stop(mddev, 1); + if (err == 0) + mddev->ro = 2; /* FIXME mark devices writable */ + } else { + mddev->ro = 2; + err = do_md_run(mddev); + } + break; + case clean: + if (mddev->pers) { + restart_array(mddev); + spin_lock_irq(&mddev->write_lock); + if (atomic_read(&mddev->writes_pending) == 0) { + if (mddev->in_sync == 0) { + mddev->in_sync = 1; + if (mddev->persistent) + mddev->sb_dirty = 1; + } + err = 0; + } else + err = -EBUSY; + spin_unlock_irq(&mddev->write_lock); + } else { + mddev->ro = 0; + mddev->recovery_cp = MaxSector; + err = do_md_run(mddev); + } + break; + case active: + if (mddev->pers) { + restart_array(mddev); + mddev->sb_dirty = 0; + wake_up(&mddev->sb_wait); + err = 0; + } else { + mddev->ro = 0; + err = do_md_run(mddev); + } + break; + case write_pending: + case active_idle: + /* these cannot be set */ + break; + } + if (err) + return err; + else + return len; +} +static struct md_sysfs_entry md_array_state = +__ATTR(array_state, S_IRUGO|S_IWUSR, array_state_show, array_state_store); + +static ssize_t +null_show(mddev_t *mddev, char *page) +{ + return -EINVAL; +} + +static ssize_t +new_dev_store(mddev_t *mddev, const char *buf, size_t len) +{ + /* buf must be %d:%d\n? giving major and minor numbers */ + /* The new device is added to the array. + * If the array has a persistent superblock, we read the + * superblock to initialise info and check validity. + * Otherwise, only checking done is that in bind_rdev_to_array, + * which mainly checks size. + */ + char *e; + int major = simple_strtoul(buf, &e, 10); + int minor; + dev_t dev; + mdk_rdev_t *rdev; + int err; + + if (!*buf || *e != ':' || !e[1] || e[1] == '\n') + return -EINVAL; + minor = simple_strtoul(e+1, &e, 10); + if (*e && *e != '\n') + return -EINVAL; + dev = MKDEV(major, minor); + if (major != MAJOR(dev) || + minor != MINOR(dev)) + return -EOVERFLOW; + + + if (mddev->persistent) { + rdev = md_import_device(dev, mddev->major_version, + mddev->minor_version); + if (!IS_ERR(rdev) && !list_empty(&mddev->disks)) { + mdk_rdev_t *rdev0 = list_entry(mddev->disks.next, + mdk_rdev_t, same_set); + err = super_types[mddev->major_version] + .load_super(rdev, rdev0, mddev->minor_version); + if (err < 0) + goto out; + } + } else + rdev = md_import_device(dev, -1, -1); + + if (IS_ERR(rdev)) + return PTR_ERR(rdev); + err = bind_rdev_to_array(rdev, mddev); + out: + if (err) + export_rdev(rdev); + return err ? err : len; +} + +static struct md_sysfs_entry md_new_device = +__ATTR(new_dev, S_IWUSR, null_show, new_dev_store); + +static ssize_t +size_show(mddev_t *mddev, char *page) +{ + return sprintf(page, "%llu\n", (unsigned long long)mddev->size); +} + +static int update_size(mddev_t *mddev, unsigned long size); + +static ssize_t +size_store(mddev_t *mddev, const char *buf, size_t len) +{ + /* If array is inactive, we can reduce the component size, but + * not increase it (except from 0). + * If array is active, we can try an on-line resize + */ + char *e; + int err = 0; + unsigned long long size = simple_strtoull(buf, &e, 10); + if (!*buf || *buf == '\n' || + (*e && *e != '\n')) + return -EINVAL; + + if (mddev->pers) { + err = update_size(mddev, size); + md_update_sb(mddev); + } else { + if (mddev->size == 0 || + mddev->size > size) + mddev->size = size; + else + err = -ENOSPC; + } + return err ? err : len; +} + +static struct md_sysfs_entry md_size = +__ATTR(component_size, S_IRUGO|S_IWUSR, size_show, size_store); + + +/* Metdata version. + * This is one of + * 'none' for arrays with no metadata (good luck...) + * 'external' for arrays with externally managed metadata, + * or N.M for internally known formats + */ +static ssize_t +metadata_show(mddev_t *mddev, char *page) +{ + if (mddev->persistent) + return sprintf(page, "%d.%d\n", + mddev->major_version, mddev->minor_version); + else if (mddev->external) + return sprintf(page, "external\n"); + else + return sprintf(page, "none\n"); +} + +static ssize_t +metadata_store(mddev_t *mddev, const char *buf, size_t len) +{ + int major, minor; + char *e; + if (!list_empty(&mddev->disks)) + return -EBUSY; + + if (cmd_match(buf, "none")) { + mddev->persistent = 0; + mddev->external = 0; + mddev->major_version = 0; + mddev->minor_version = 90; + return len; + } + if (cmd_match(buf, "external")) { + mddev->persistent = 0; + mddev->external = 1; + mddev->major_version = 0; + mddev->minor_version = 90; + return len; + } + major = simple_strtoul(buf, &e, 10); + if (e==buf || *e != '.') + return -EINVAL; + buf = e+1; + minor = simple_strtoul(buf, &e, 10); + if (e==buf || *e != '\n') + return -EINVAL; + if (major >= sizeof(super_types)/sizeof(super_types[0]) || + super_types[major].name == NULL) + return -ENOENT; + mddev->major_version = major; + mddev->minor_version = minor; + mddev->persistent = 1; + mddev->external = 0; + return len; +} + +static struct md_sysfs_entry md_metadata = +__ATTR(metadata_version, S_IRUGO|S_IWUSR, metadata_show, metadata_store); + +static ssize_t +action_show(mddev_t *mddev, char *page) +{ + char *type = "idle"; + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || + test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) { + if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) + type = "reshape"; + else if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) { + if (!test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) + type = "resync"; + else if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery)) + type = "check"; + else + type = "repair"; + } else + type = "recover"; + } + return sprintf(page, "%s\n", type); +} + +static ssize_t +action_store(mddev_t *mddev, const char *page, size_t len) +{ + if (!mddev->pers || !mddev->pers->sync_request) + return -EINVAL; + + if (cmd_match(page, "idle")) { + if (mddev->sync_thread) { + set_bit(MD_RECOVERY_INTR, &mddev->recovery); + md_unregister_thread(mddev->sync_thread); + mddev->sync_thread = NULL; + mddev->recovery = 0; + } + } else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) || + test_bit(MD_RECOVERY_NEEDED, &mddev->recovery)) + return -EBUSY; + else if (cmd_match(page, "resync") || cmd_match(page, "recover")) + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + else if (cmd_match(page, "reshape")) { + int err; + if (mddev->pers->start_reshape == NULL) + return -EINVAL; + err = mddev->pers->start_reshape(mddev); + if (err) + return err; + } else { + if (cmd_match(page, "check")) + set_bit(MD_RECOVERY_CHECK, &mddev->recovery); + else if (!cmd_match(page, "repair")) + return -EINVAL; + set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery); + set_bit(MD_RECOVERY_SYNC, &mddev->recovery); + } + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); + return len; +} + +static ssize_t +mismatch_cnt_show(mddev_t *mddev, char *page) +{ + return sprintf(page, "%llu\n", + (unsigned long long) mddev->resync_mismatches); +} + +static struct md_sysfs_entry md_scan_mode = +__ATTR(sync_action, S_IRUGO|S_IWUSR, action_show, action_store); + + +static struct md_sysfs_entry md_mismatches = __ATTR_RO(mismatch_cnt); + +static ssize_t +sync_min_show(mddev_t *mddev, char *page) +{ + return sprintf(page, "%d (%s)\n", speed_min(mddev), + mddev->sync_speed_min ? "local": "system"); +} + +static ssize_t +sync_min_store(mddev_t *mddev, const char *buf, size_t len) +{ + int min; + char *e; + if (strncmp(buf, "system", 6)==0) { + mddev->sync_speed_min = 0; + return len; + } + min = simple_strtoul(buf, &e, 10); + if (buf == e || (*e && *e != '\n') || min <= 0) + return -EINVAL; + mddev->sync_speed_min = min; + return len; +} + +static struct md_sysfs_entry md_sync_min = +__ATTR(sync_speed_min, S_IRUGO|S_IWUSR, sync_min_show, sync_min_store); + +static ssize_t +sync_max_show(mddev_t *mddev, char *page) +{ + return sprintf(page, "%d (%s)\n", speed_max(mddev), + mddev->sync_speed_max ? "local": "system"); +} + +static ssize_t +sync_max_store(mddev_t *mddev, const char *buf, size_t len) +{ + int max; + char *e; + if (strncmp(buf, "system", 6)==0) { + mddev->sync_speed_max = 0; + return len; + } + max = simple_strtoul(buf, &e, 10); + if (buf == e || (*e && *e != '\n') || max <= 0) + return -EINVAL; + mddev->sync_speed_max = max; + return len; +} + +static struct md_sysfs_entry md_sync_max = +__ATTR(sync_speed_max, S_IRUGO|S_IWUSR, sync_max_show, sync_max_store); + + +static ssize_t +sync_speed_show(mddev_t *mddev, char *page) +{ + unsigned long resync, dt, db; + resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active)); + dt = ((jiffies - mddev->resync_mark) / HZ); + if (!dt) dt++; + db = resync - (mddev->resync_mark_cnt); + return sprintf(page, "%ld\n", db/dt/2); /* K/sec */ +} + +static struct md_sysfs_entry md_sync_speed = __ATTR_RO(sync_speed); + +static ssize_t +sync_completed_show(mddev_t *mddev, char *page) +{ + unsigned long max_blocks, resync; + + if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) + max_blocks = mddev->resync_max_sectors; + else + max_blocks = mddev->size << 1; + + resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active)); + return sprintf(page, "%lu / %lu\n", resync, max_blocks); +} + +static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed); + +static ssize_t +max_sync_show(mddev_t *mddev, char *page) +{ + if (mddev->resync_max == MaxSector) + return sprintf(page, "max\n"); + else + return sprintf(page, "%llu\n", + (unsigned long long)mddev->resync_max); +} +static ssize_t +max_sync_store(mddev_t *mddev, const char *buf, size_t len) +{ + if (strncmp(buf, "max", 3)==0) + mddev->resync_max = MaxSector; + else { + char *ep; + unsigned long long max = simple_strtoull(buf, &ep, 10); + if (ep == buf || (*ep != 0 && *ep != '\n')) + return -EINVAL; + if (max < mddev->resync_max && + test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) + return -EBUSY; + + /* Must be a multiple of chunk_size */ + if (mddev->chunk_size) { + if (max & (sector_t)((mddev->chunk_size>>9)-1)) + return -EINVAL; + } + mddev->resync_max = max; + } + wake_up(&mddev->recovery_wait); + return len; +} + +static struct md_sysfs_entry md_max_sync = +__ATTR(sync_max, S_IRUGO|S_IWUSR, max_sync_show, max_sync_store); + +static ssize_t +suspend_lo_show(mddev_t *mddev, char *page) +{ + return sprintf(page, "%llu\n", (unsigned long long)mddev->suspend_lo); +} + +static ssize_t +suspend_lo_store(mddev_t *mddev, const char *buf, size_t len) +{ + char *e; + unsigned long long new = simple_strtoull(buf, &e, 10); + + if (mddev->pers->quiesce == NULL) + return -EINVAL; + if (buf == e || (*e && *e != '\n')) + return -EINVAL; + if (new >= mddev->suspend_hi || + (new > mddev->suspend_lo && new < mddev->suspend_hi)) { + mddev->suspend_lo = new; + mddev->pers->quiesce(mddev, 2); + return len; + } else + return -EINVAL; +} +static struct md_sysfs_entry md_suspend_lo = +__ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store); + + +static ssize_t +suspend_hi_show(mddev_t *mddev, char *page) +{ + return sprintf(page, "%llu\n", (unsigned long long)mddev->suspend_hi); +} + +static ssize_t +suspend_hi_store(mddev_t *mddev, const char *buf, size_t len) +{ + char *e; + unsigned long long new = simple_strtoull(buf, &e, 10); + + if (mddev->pers->quiesce == NULL) + return -EINVAL; + if (buf == e || (*e && *e != '\n')) + return -EINVAL; + if ((new <= mddev->suspend_lo && mddev->suspend_lo >= mddev->suspend_hi) || + (new > mddev->suspend_lo && new > mddev->suspend_hi)) { + mddev->suspend_hi = new; + mddev->pers->quiesce(mddev, 1); + mddev->pers->quiesce(mddev, 0); + return len; + } else + return -EINVAL; +} +static struct md_sysfs_entry md_suspend_hi = +__ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store); + + +static struct attribute *md_default_attrs[] = { + &md_level.attr, + &md_layout.attr, + &md_raid_disks.attr, + &md_chunk_size.attr, + &md_size.attr, + &md_resync_start.attr, + &md_metadata.attr, + &md_new_device.attr, + &md_safe_delay.attr, + &md_array_state.attr, + NULL, +}; + +static struct attribute *md_redundancy_attrs[] = { + &md_scan_mode.attr, + &md_mismatches.attr, + &md_sync_min.attr, + &md_sync_max.attr, + &md_sync_speed.attr, + &md_sync_completed.attr, + &md_max_sync.attr, + &md_suspend_lo.attr, + &md_suspend_hi.attr, + NULL, +}; +static struct attribute_group md_redundancy_group = { + .name = NULL, + .attrs = md_redundancy_attrs, +}; + + +static ssize_t +md_attr_show(struct kobject *kobj, struct attribute *attr, char *page) +{ + struct md_sysfs_entry *entry = container_of(attr, struct md_sysfs_entry, attr); + mddev_t *mddev = container_of(kobj, struct mddev_s, kobj); + ssize_t rv; + + if (!entry->show) + return -EIO; + rv = mddev_lock(mddev); + if (!rv) { + rv = entry->show(mddev, page); + mddev_unlock(mddev); + } + return rv; +} + +static ssize_t +md_attr_store(struct kobject *kobj, struct attribute *attr, + const char *page, size_t length) +{ + struct md_sysfs_entry *entry = container_of(attr, struct md_sysfs_entry, attr); + mddev_t *mddev = container_of(kobj, struct mddev_s, kobj); + ssize_t rv; + + if (!entry->store) + return -EIO; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + rv = mddev_lock(mddev); + if (!rv) { + rv = entry->store(mddev, page, length); + mddev_unlock(mddev); + } + return rv; +} + +static void md_free(struct kobject *ko) +{ + mddev_t *mddev = container_of(ko, mddev_t, kobj); + kfree(mddev); +} + +static struct sysfs_ops md_sysfs_ops = { + .show = md_attr_show, + .store = md_attr_store, +}; +static struct kobj_type md_ktype = { + .release = md_free, + .sysfs_ops = &md_sysfs_ops, + .default_attrs = md_default_attrs, +}; + +int mdp_major = 0; + +static struct kobject *md_probe(dev_t dev, int *part, void *data) +{ + static DEFINE_MUTEX(disks_mutex); + mddev_t *mddev = mddev_find(dev); + struct gendisk *disk; + int partitioned = (MAJOR(dev) != MD_MAJOR); + int shift = partitioned ? MdpMinorShift : 0; + int unit = MINOR(dev) >> shift; + + if (!mddev) + return NULL; + + mutex_lock(&disks_mutex); + if (mddev->gendisk) { + mutex_unlock(&disks_mutex); + mddev_put(mddev); + return NULL; + } + disk = alloc_disk(1 << shift); + if (!disk) { + mutex_unlock(&disks_mutex); + mddev_put(mddev); + return NULL; + } + disk->major = MAJOR(dev); + disk->first_minor = unit << shift; + if (partitioned) { + sprintf(disk->disk_name, "md_d%d", unit); + sprintf(disk->devfs_name, "md/d%d", unit); + } else { + sprintf(disk->disk_name, "md%d", unit); + sprintf(disk->devfs_name, "md/%d", unit); + } + disk->fops = &md_fops; + disk->private_data = mddev; + disk->queue = mddev->queue; + add_disk(disk); + mddev->gendisk = disk; + mutex_unlock(&disks_mutex); + mddev->kobj.parent = &disk->kobj; + mddev->kobj.k_name = NULL; + snprintf(mddev->kobj.name, KOBJ_NAME_LEN, "%s", "md"); + mddev->kobj.ktype = &md_ktype; + kobject_register(&mddev->kobj); + return NULL; +} + +static void md_safemode_timeout(unsigned long data) +{ + mddev_t *mddev = (mddev_t *) data; + + mddev->safemode = 1; + md_wakeup_thread(mddev->thread); +} + +static int start_dirty_degraded; + +static int do_md_run(mddev_t * mddev) +{ + int err; + int chunk_size; + struct list_head *tmp; + mdk_rdev_t *rdev; + struct gendisk *disk; + struct mdk_personality *pers; + char b[BDEVNAME_SIZE]; + + if (list_empty(&mddev->disks)) + /* cannot run an array with no devices.. */ + return -EINVAL; + + if (mddev->pers) + return -EBUSY; + + /* + * Analyze all RAID superblock(s) + */ + if (!mddev->raid_disks) + analyze_sbs(mddev); + + chunk_size = mddev->chunk_size; + + if (chunk_size) { + if (chunk_size > MAX_CHUNK_SIZE) { + printk(KERN_ERR "too big chunk_size: %d > %d\n", + chunk_size, MAX_CHUNK_SIZE); + return -EINVAL; + } + /* + * chunk-size has to be a power of 2 and multiples of PAGE_SIZE + */ + if ( (1 << ffz(~chunk_size)) != chunk_size) { + printk(KERN_ERR "chunk_size of %d not valid\n", chunk_size); + return -EINVAL; + } + if (chunk_size < PAGE_SIZE) { + printk(KERN_ERR "too small chunk_size: %d < %ld\n", + chunk_size, PAGE_SIZE); + return -EINVAL; + } + + /* devices must have minimum size of one chunk */ + ITERATE_RDEV(mddev,rdev,tmp) { + if (test_bit(Faulty, &rdev->flags)) + continue; + if (rdev->size < chunk_size / 1024) { + printk(KERN_WARNING + "md: Dev %s smaller than chunk_size:" + " %lluk < %dk\n", + bdevname(rdev->bdev,b), + (unsigned long long)rdev->size, + chunk_size / 1024); + return -EINVAL; + } + } + } + +#ifdef CONFIG_KMOD + if (mddev->level != LEVEL_NONE) + request_module("md-level-%d", mddev->level); + else if (mddev->clevel[0]) + request_module("md-%s", mddev->clevel); +#endif + + /* + * Drop all container device buffers, from now on + * the only valid external interface is through the md + * device. + * Also find largest hardsector size + */ + ITERATE_RDEV(mddev,rdev,tmp) { + if (test_bit(Faulty, &rdev->flags)) + continue; + sync_blockdev(rdev->bdev); + invalidate_bdev(rdev->bdev, 0); + } + + md_probe(mddev->unit, NULL, NULL); + disk = mddev->gendisk; + if (!disk) + return -ENOMEM; + + spin_lock(&pers_lock); + pers = find_pers(mddev->level, mddev->clevel); + if (!pers || !try_module_get(pers->owner)) { + spin_unlock(&pers_lock); + if (mddev->level != LEVEL_NONE) + printk(KERN_WARNING "md: personality for level %d is not loaded!\n", + mddev->level); + else + printk(KERN_WARNING "md: personality for level %s is not loaded!\n", + mddev->clevel); + return -EINVAL; + } + mddev->pers = pers; + spin_unlock(&pers_lock); + mddev->level = pers->level; + strlcpy(mddev->clevel, pers->name, sizeof(mddev->clevel)); + + if (mddev->reshape_position != MaxSector && + pers->start_reshape == NULL) { + /* This personality cannot handle reshaping... */ + mddev->pers = NULL; + module_put(pers->owner); + return -EINVAL; + } + + mddev->recovery = 0; + mddev->resync_max_sectors = mddev->size << 1; /* may be over-ridden by personality */ + mddev->barriers_work = 1; + mddev->ok_start_degraded = start_dirty_degraded; + + if (start_readonly) + mddev->ro = 2; /* read-only, but switch on first write */ + + err = mddev->pers->run(mddev); + if (!err && mddev->pers->sync_request) { + err = bitmap_create(mddev); + if (err) { + printk(KERN_ERR "%s: failed to create bitmap (%d)\n", + mdname(mddev), err); + mddev->pers->stop(mddev); + } + } + if (err) { + printk(KERN_ERR "md: pers->run() failed ...\n"); + module_put(mddev->pers->owner); + mddev->pers = NULL; + bitmap_destroy(mddev); + return err; + } + if (mddev->pers->sync_request) + sysfs_create_group(&mddev->kobj, &md_redundancy_group); + else if (mddev->ro == 2) /* auto-readonly not meaningful */ + mddev->ro = 0; + + atomic_set(&mddev->writes_pending,0); + mddev->safemode = 0; + mddev->safemode_timer.function = md_safemode_timeout; + mddev->safemode_timer.data = (unsigned long) mddev; + mddev->safemode_delay = (200 * HZ)/1000 +1; /* 200 msec delay */ + mddev->in_sync = 1; + + ITERATE_RDEV(mddev,rdev,tmp) + if (rdev->raid_disk >= 0) { + char nm[20]; + sprintf(nm, "rd%d", rdev->raid_disk); + sysfs_create_link(&mddev->kobj, &rdev->kobj, nm); + } + + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); + + if (mddev->sb_dirty) + md_update_sb(mddev); + + set_capacity(disk, mddev->array_size<<1); + + /* If we call blk_queue_make_request here, it will + * re-initialise max_sectors etc which may have been + * refined inside -> run. So just set the bits we need to set. + * Most initialisation happended when we called + * blk_queue_make_request(..., md_fail_request) + * earlier. + */ + mddev->queue->queuedata = mddev; + mddev->queue->make_request_fn = mddev->pers->make_request; + + /* If there is a partially-recovered drive we need to + * start recovery here. If we leave it to md_check_recovery, + * it will remove the drives and not do the right thing + */ + if (mddev->degraded) { + struct list_head *rtmp; + int spares = 0; + ITERATE_RDEV(mddev,rdev,rtmp) + if (rdev->raid_disk >= 0 && + !test_bit(In_sync, &rdev->flags) && + !test_bit(Faulty, &rdev->flags)) + /* complete an interrupted recovery */ + spares++; + if (spares && mddev->pers->sync_request) { + mddev->recovery = 0; + set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); + mddev->sync_thread = md_register_thread(md_do_sync, + mddev, + "%s_resync"); + if (!mddev->sync_thread) { + printk(KERN_ERR "%s: could not start resync" + " thread...\n", + mdname(mddev)); + /* leave the spares where they are, it shouldn't hurt */ + mddev->recovery = 0; + } else + md_wakeup_thread(mddev->sync_thread); + } + } + + mddev->changed = 1; + md_new_event(mddev); + return 0; +} + +static int restart_array(mddev_t *mddev) +{ + struct gendisk *disk = mddev->gendisk; + int err; + + /* + * Complain if it has no devices + */ + err = -ENXIO; + if (list_empty(&mddev->disks)) + goto out; + + if (mddev->pers) { + err = -EBUSY; + if (!mddev->ro) + goto out; + + mddev->safemode = 0; + mddev->ro = 0; + set_disk_ro(disk, 0); + + printk(KERN_INFO "md: %s switched to read-write mode.\n", + mdname(mddev)); + /* + * Kick recovery or resync if necessary + */ + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); + md_wakeup_thread(mddev->sync_thread); + err = 0; + } else + err = -EINVAL; + +out: + return err; +} + +/* similar to deny_write_access, but accounts for our holding a reference + * to the file ourselves */ +static int deny_bitmap_write_access(struct file * file) +{ + struct inode *inode = file->f_mapping->host; + + spin_lock(&inode->i_lock); + if (atomic_read(&inode->i_writecount) > 1) { + spin_unlock(&inode->i_lock); + return -ETXTBSY; + } + atomic_set(&inode->i_writecount, -1); + spin_unlock(&inode->i_lock); + + return 0; +} + +static void restore_bitmap_write_access(struct file *file) +{ + struct inode *inode = file->f_mapping->host; + + spin_lock(&inode->i_lock); + atomic_set(&inode->i_writecount, 1); + spin_unlock(&inode->i_lock); +} + +/* mode: + * 0 - completely stop and dis-assemble array + * 1 - switch to readonly + * 2 - stop but do not disassemble array + */ +static int do_md_stop(mddev_t * mddev, int mode) +{ + int err = 0; + struct gendisk *disk = mddev->gendisk; + + if (mddev->pers) { + if (atomic_read(&mddev->active)>2) { + printk("md: %s still in use.\n",mdname(mddev)); + return -EBUSY; + } + + if (mddev->sync_thread) { + set_bit(MD_RECOVERY_FROZEN, &mddev->recovery); + set_bit(MD_RECOVERY_INTR, &mddev->recovery); + md_unregister_thread(mddev->sync_thread); + mddev->sync_thread = NULL; + } + + del_timer_sync(&mddev->safemode_timer); + + invalidate_partition(disk, 0); + + switch(mode) { + case 1: /* readonly */ + err = -ENXIO; + if (mddev->ro==1) + goto out; + mddev->ro = 1; + break; + case 0: /* disassemble */ + case 2: /* stop */ + bitmap_flush(mddev); + md_super_wait(mddev); + if (mddev->ro) + set_disk_ro(disk, 0); + blk_queue_make_request(mddev->queue, md_fail_request); + mddev->pers->stop(mddev); + if (mddev->pers->sync_request) + sysfs_remove_group(&mddev->kobj, &md_redundancy_group); + + module_put(mddev->pers->owner); + mddev->pers = NULL; + if (mddev->ro) + mddev->ro = 0; + } + if (!mddev->in_sync || mddev->sb_dirty) { + /* mark array as shutdown cleanly */ + mddev->in_sync = 1; + md_update_sb(mddev); + } + if (mode == 1) + set_disk_ro(disk, 1); + clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery); + } + + /* + * Free resources if final stop + */ + if (mode == 0) { + mdk_rdev_t *rdev; + struct list_head *tmp; + struct gendisk *disk; + printk(KERN_INFO "md: %s stopped.\n", mdname(mddev)); + + bitmap_destroy(mddev); + if (mddev->bitmap_file) { + restore_bitmap_write_access(mddev->bitmap_file); + fput(mddev->bitmap_file); + mddev->bitmap_file = NULL; + } + mddev->bitmap_offset = 0; + + ITERATE_RDEV(mddev,rdev,tmp) + if (rdev->raid_disk >= 0) { + char nm[20]; + sprintf(nm, "rd%d", rdev->raid_disk); + sysfs_remove_link(&mddev->kobj, nm); + } + + export_array(mddev); + + mddev->array_size = 0; + mddev->size = 0; + mddev->raid_disks = 0; + mddev->recovery_cp = 0; + mddev->resync_max = MaxSector; + + disk = mddev->gendisk; + if (disk) + set_capacity(disk, 0); + mddev->changed = 1; + } else if (mddev->pers) + printk(KERN_INFO "md: %s switched to read-only mode.\n", + mdname(mddev)); + err = 0; + md_new_event(mddev); +out: + return err; +} + +static void autorun_array(mddev_t *mddev) +{ + mdk_rdev_t *rdev; + struct list_head *tmp; + int err; + + if (list_empty(&mddev->disks)) + return; + + printk(KERN_INFO "md: running: "); + + ITERATE_RDEV(mddev,rdev,tmp) { + char b[BDEVNAME_SIZE]; + printk("<%s>", bdevname(rdev->bdev,b)); + } + printk("\n"); + + err = do_md_run (mddev); + if (err) { + printk(KERN_WARNING "md: do_md_run() returned %d\n", err); + do_md_stop (mddev, 0); + } +} + +/* + * lets try to run arrays based on all disks that have arrived + * until now. (those are in pending_raid_disks) + * + * the method: pick the first pending disk, collect all disks with + * the same UUID, remove all from the pending list and put them into + * the 'same_array' list. Then order this list based on superblock + * update time (freshest comes first), kick out 'old' disks and + * compare superblocks. If everything's fine then run it. + * + * If "unit" is allocated, then bump its reference count + */ +static void autorun_devices(int part) +{ + struct list_head *tmp; + mdk_rdev_t *rdev0, *rdev; + mddev_t *mddev; + char b[BDEVNAME_SIZE]; + + printk(KERN_INFO "md: autorun ...\n"); + while (!list_empty(&pending_raid_disks)) { + dev_t dev; + LIST_HEAD(candidates); + rdev0 = list_entry(pending_raid_disks.next, + mdk_rdev_t, same_set); + + printk(KERN_INFO "md: considering %s ...\n", + bdevname(rdev0->bdev,b)); + INIT_LIST_HEAD(&candidates); + ITERATE_RDEV_PENDING(rdev,tmp) + if (super_90_load(rdev, rdev0, 0) >= 0) { + printk(KERN_INFO "md: adding %s ...\n", + bdevname(rdev->bdev,b)); + list_move(&rdev->same_set, &candidates); + } + /* + * now we have a set of devices, with all of them having + * mostly sane superblocks. It's time to allocate the + * mddev. + */ + if (rdev0->preferred_minor < 0 || rdev0->preferred_minor >= MAX_MD_DEVS) { + printk(KERN_INFO "md: unit number in %s is bad: %d\n", + bdevname(rdev0->bdev, b), rdev0->preferred_minor); + break; + } + if (part) + dev = MKDEV(mdp_major, + rdev0->preferred_minor << MdpMinorShift); + else + dev = MKDEV(MD_MAJOR, rdev0->preferred_minor); + + md_probe(dev, NULL, NULL); + mddev = mddev_find(dev); + if (!mddev) { + printk(KERN_ERR + "md: cannot allocate memory for md drive.\n"); + break; + } + if (mddev_lock(mddev)) + printk(KERN_WARNING "md: %s locked, cannot run\n", + mdname(mddev)); + else if (mddev->raid_disks || mddev->major_version + || !list_empty(&mddev->disks)) { + printk(KERN_WARNING + "md: %s already running, cannot run %s\n", + mdname(mddev), bdevname(rdev0->bdev,b)); + mddev_unlock(mddev); + } else { + printk(KERN_INFO "md: created %s\n", mdname(mddev)); + ITERATE_RDEV_GENERIC(candidates,rdev,tmp) { + list_del_init(&rdev->same_set); + if (bind_rdev_to_array(rdev, mddev)) + export_rdev(rdev); + } + autorun_array(mddev); + mddev_unlock(mddev); + } + /* on success, candidates will be empty, on error + * it won't... + */ + ITERATE_RDEV_GENERIC(candidates,rdev,tmp) + export_rdev(rdev); + mddev_put(mddev); + } + printk(KERN_INFO "md: ... autorun DONE.\n"); +} + +/* + * import RAID devices based on one partition + * if possible, the array gets run as well. + */ + +static int autostart_array(dev_t startdev) +{ + char b[BDEVNAME_SIZE]; + int err = -EINVAL, i; + mdp_super_t *sb = NULL; + mdk_rdev_t *start_rdev = NULL, *rdev; + + start_rdev = md_import_device(startdev, 0, 0); + if (IS_ERR(start_rdev)) + return err; + + + /* NOTE: this can only work for 0.90.0 superblocks */ + sb = (mdp_super_t*)page_address(start_rdev->sb_page); + if (sb->major_version != 0 || + sb->minor_version != 90 ) { + printk(KERN_WARNING "md: can only autostart 0.90.0 arrays\n"); + export_rdev(start_rdev); + return err; + } + + if (test_bit(Faulty, &start_rdev->flags)) { + printk(KERN_WARNING + "md: can not autostart based on faulty %s!\n", + bdevname(start_rdev->bdev,b)); + export_rdev(start_rdev); + return err; + } + list_add(&start_rdev->same_set, &pending_raid_disks); + + for (i = 0; i < MD_SB_DISKS; i++) { + mdp_disk_t *desc = sb->disks + i; + dev_t dev = MKDEV(desc->major, desc->minor); + + if (!dev) + continue; + if (dev == startdev) + continue; + if (MAJOR(dev) != desc->major || MINOR(dev) != desc->minor) + continue; + rdev = md_import_device(dev, 0, 0); + if (IS_ERR(rdev)) + continue; + + list_add(&rdev->same_set, &pending_raid_disks); + } + + /* + * possibly return codes + */ + autorun_devices(0); + return 0; + +} + + +static int get_version(void __user * arg) +{ + mdu_version_t ver; + + ver.major = MD_MAJOR_VERSION; + ver.minor = MD_MINOR_VERSION; + ver.patchlevel = MD_PATCHLEVEL_VERSION; + + if (copy_to_user(arg, &ver, sizeof(ver))) + return -EFAULT; + + return 0; +} + +static int get_array_info(mddev_t * mddev, void __user * arg) +{ + mdu_array_info_t info; + int nr,working,active,failed,spare; + mdk_rdev_t *rdev; + struct list_head *tmp; + + nr=working=active=failed=spare=0; + ITERATE_RDEV(mddev,rdev,tmp) { + nr++; + if (test_bit(Faulty, &rdev->flags)) + failed++; + else { + working++; + if (test_bit(In_sync, &rdev->flags)) + active++; + else + spare++; + } + } + + info.major_version = mddev->major_version; + info.minor_version = mddev->minor_version; + info.patch_version = MD_PATCHLEVEL_VERSION; + info.ctime = mddev->ctime; + info.level = mddev->level; + info.size = mddev->size; + if (info.size != mddev->size) /* overflow */ + info.size = -1; + info.nr_disks = nr; + info.raid_disks = mddev->raid_disks; + info.md_minor = mddev->md_minor; + info.not_persistent= !mddev->persistent; + + info.utime = mddev->utime; + info.state = 0; + if (mddev->in_sync) + info.state = (1<bitmap && mddev->bitmap_offset) + info.state = (1<layout; + info.chunk_size = mddev->chunk_size; + + if (copy_to_user(arg, &info, sizeof(info))) + return -EFAULT; + + return 0; +} + +static int get_bitmap_file(mddev_t * mddev, void __user * arg) +{ + mdu_bitmap_file_t *file = NULL; /* too big for stack allocation */ + char *ptr, *buf = NULL; + int err = -ENOMEM; + + file = kmalloc(sizeof(*file), GFP_KERNEL); + if (!file) + goto out; + + /* bitmap disabled, zero the first byte and copy out */ + if (!mddev->bitmap || !mddev->bitmap->file) { + file->pathname[0] = '\0'; + goto copy_out; + } + + buf = kmalloc(sizeof(file->pathname), GFP_KERNEL); + if (!buf) + goto out; + + ptr = file_path(mddev->bitmap->file, buf, sizeof(file->pathname)); + if (!ptr) + goto out; + + strcpy(file->pathname, ptr); + +copy_out: + err = 0; + if (copy_to_user(arg, file, sizeof(*file))) + err = -EFAULT; +out: + kfree(buf); + kfree(file); + return err; +} + +static int get_disk_info(mddev_t * mddev, void __user * arg) +{ + mdu_disk_info_t info; + unsigned int nr; + mdk_rdev_t *rdev; + + if (copy_from_user(&info, arg, sizeof(info))) + return -EFAULT; + + nr = info.number; + + rdev = find_rdev_nr(mddev, nr); + if (rdev) { + info.major = MAJOR(rdev->bdev->bd_dev); + info.minor = MINOR(rdev->bdev->bd_dev); + info.raid_disk = rdev->raid_disk; + info.state = 0; + if (test_bit(Faulty, &rdev->flags)) + info.state |= (1<flags)) { + info.state |= (1<flags)) + info.state |= (1<major,info->minor); + + if (info->major != MAJOR(dev) || info->minor != MINOR(dev)) + return -EOVERFLOW; + + if (!mddev->raid_disks) { + int err; + /* expecting a device which has a superblock */ + rdev = md_import_device(dev, mddev->major_version, mddev->minor_version); + if (IS_ERR(rdev)) { + printk(KERN_WARNING + "md: md_import_device returned %ld\n", + PTR_ERR(rdev)); + return PTR_ERR(rdev); + } + if (!list_empty(&mddev->disks)) { + mdk_rdev_t *rdev0 = list_entry(mddev->disks.next, + mdk_rdev_t, same_set); + int err = super_types[mddev->major_version] + .load_super(rdev, rdev0, mddev->minor_version); + if (err < 0) { + printk(KERN_WARNING + "md: %s has different UUID to %s\n", + bdevname(rdev->bdev,b), + bdevname(rdev0->bdev,b2)); + export_rdev(rdev); + return -EINVAL; + } + } + err = bind_rdev_to_array(rdev, mddev); + if (err) + export_rdev(rdev); + return err; + } + + /* + * add_new_disk can be used once the array is assembled + * to add "hot spares". They must already have a superblock + * written + */ + if (mddev->pers) { + int err; + if (!mddev->pers->hot_add_disk) { + printk(KERN_WARNING + "%s: personality does not support diskops!\n", + mdname(mddev)); + return -EINVAL; + } + if (mddev->persistent) + rdev = md_import_device(dev, mddev->major_version, + mddev->minor_version); + else + rdev = md_import_device(dev, -1, -1); + if (IS_ERR(rdev)) { + printk(KERN_WARNING + "md: md_import_device returned %ld\n", + PTR_ERR(rdev)); + return PTR_ERR(rdev); + } + /* set save_raid_disk if appropriate */ + if (!mddev->persistent) { + if (info->state & (1<raid_disk < mddev->raid_disks) + rdev->raid_disk = info->raid_disk; + else + rdev->raid_disk = -1; + } else + super_types[mddev->major_version]. + validate_super(mddev, rdev); + rdev->saved_raid_disk = rdev->raid_disk; + + clear_bit(In_sync, &rdev->flags); /* just to be sure */ + if (info->state & (1<flags); + + rdev->raid_disk = -1; + err = bind_rdev_to_array(rdev, mddev); + if (!err && !mddev->pers->hot_remove_disk) { + /* If there is hot_add_disk but no hot_remove_disk + * then added disks for geometry changes, + * and should be added immediately. + */ + super_types[mddev->major_version]. + validate_super(mddev, rdev); + err = mddev->pers->hot_add_disk(mddev, rdev); + if (err) + unbind_rdev_from_array(rdev); + } + if (err) + export_rdev(rdev); + + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); + return err; + } + + /* otherwise, add_new_disk is only allowed + * for major_version==0 superblocks + */ + if (mddev->major_version != 0) { + printk(KERN_WARNING "%s: ADD_NEW_DISK not supported\n", + mdname(mddev)); + return -EINVAL; + } + + if (!(info->state & (1<desc_nr = info->number; + if (info->raid_disk < mddev->raid_disks) + rdev->raid_disk = info->raid_disk; + else + rdev->raid_disk = -1; + + rdev->flags = 0; + + if (rdev->raid_disk < mddev->raid_disks) + if (info->state & (1<flags); + + if (info->state & (1<flags); + + if (!mddev->persistent) { + printk(KERN_INFO "md: nonpersistent superblock ...\n"); + rdev->sb_offset = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + } else + rdev->sb_offset = calc_dev_sboffset(rdev->bdev); + rdev->size = calc_dev_size(rdev, mddev->chunk_size); + + err = bind_rdev_to_array(rdev, mddev); + if (err) { + export_rdev(rdev); + return err; + } + } + + return 0; +} + +static int hot_remove_disk(mddev_t * mddev, dev_t dev) +{ + char b[BDEVNAME_SIZE]; + mdk_rdev_t *rdev; + + if (!mddev->pers) + return -ENODEV; + + rdev = find_rdev(mddev, dev); + if (!rdev) + return -ENXIO; + + if (rdev->raid_disk >= 0) + goto busy; + + kick_rdev_from_array(rdev); + md_update_sb(mddev); + md_new_event(mddev); + + return 0; +busy: + printk(KERN_WARNING "md: cannot remove active disk %s from %s ... \n", + bdevname(rdev->bdev,b), mdname(mddev)); + return -EBUSY; +} + +static int hot_add_disk(mddev_t * mddev, dev_t dev) +{ + char b[BDEVNAME_SIZE]; + int err; + unsigned int size; + mdk_rdev_t *rdev; + + if (!mddev->pers) + return -ENODEV; + + if (mddev->major_version != 0) { + printk(KERN_WARNING "%s: HOT_ADD may only be used with" + " version-0 superblocks.\n", + mdname(mddev)); + return -EINVAL; + } + if (!mddev->pers->hot_add_disk) { + printk(KERN_WARNING + "%s: personality does not support diskops!\n", + mdname(mddev)); + return -EINVAL; + } + + rdev = md_import_device (dev, -1, 0); + if (IS_ERR(rdev)) { + printk(KERN_WARNING + "md: error, md_import_device() returned %ld\n", + PTR_ERR(rdev)); + return -EINVAL; + } + + if (mddev->persistent) + rdev->sb_offset = calc_dev_sboffset(rdev->bdev); + else + rdev->sb_offset = + rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS; + + size = calc_dev_size(rdev, mddev->chunk_size); + rdev->size = size; + + if (test_bit(Faulty, &rdev->flags)) { + printk(KERN_WARNING + "md: can not hot-add faulty %s disk to %s!\n", + bdevname(rdev->bdev,b), mdname(mddev)); + err = -EINVAL; + goto abort_export; + } + clear_bit(In_sync, &rdev->flags); + rdev->desc_nr = -1; + err = bind_rdev_to_array(rdev, mddev); + if (err) + goto abort_export; + + /* + * The rest should better be atomic, we can have disk failures + * noticed in interrupt contexts ... + */ + + if (rdev->desc_nr == mddev->max_disks) { + printk(KERN_WARNING "%s: can not hot-add to full array!\n", + mdname(mddev)); + err = -EBUSY; + goto abort_unbind_export; + } + + rdev->raid_disk = -1; + + md_update_sb(mddev); + + /* + * Kick recovery, maybe this spare has to be added to the + * array immediately. + */ + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); + md_new_event(mddev); + return 0; + +abort_unbind_export: + unbind_rdev_from_array(rdev); + +abort_export: + export_rdev(rdev); + return err; +} + +static int set_bitmap_file(mddev_t *mddev, int fd) +{ + int err; + + if (mddev->pers) { + if (!mddev->pers->quiesce) + return -EBUSY; + if (mddev->recovery || mddev->sync_thread) + return -EBUSY; + /* we should be able to change the bitmap.. */ + } + + + if (fd >= 0) { + if (mddev->bitmap) + return -EEXIST; /* cannot add when bitmap is present */ + mddev->bitmap_file = fget(fd); + + if (mddev->bitmap_file == NULL) { + printk(KERN_ERR "%s: error: failed to get bitmap file\n", + mdname(mddev)); + return -EBADF; + } + + err = deny_bitmap_write_access(mddev->bitmap_file); + if (err) { + printk(KERN_ERR "%s: error: bitmap file is already in use\n", + mdname(mddev)); + fput(mddev->bitmap_file); + mddev->bitmap_file = NULL; + return err; + } + mddev->bitmap_offset = 0; /* file overrides offset */ + } else if (mddev->bitmap == NULL) + return -ENOENT; /* cannot remove what isn't there */ + err = 0; + if (mddev->pers) { + mddev->pers->quiesce(mddev, 1); + if (fd >= 0) + err = bitmap_create(mddev); + if (fd < 0 || err) { + bitmap_destroy(mddev); + fd = -1; /* make sure to put the file */ + } + mddev->pers->quiesce(mddev, 0); + } + if (fd < 0) { + if (mddev->bitmap_file) { + restore_bitmap_write_access(mddev->bitmap_file); + fput(mddev->bitmap_file); + } + mddev->bitmap_file = NULL; + } + + return err; +} + +/* + * set_array_info is used two different ways + * The original usage is when creating a new array. + * In this usage, raid_disks is > 0 and it together with + * level, size, not_persistent,layout,chunksize determine the + * shape of the array. + * This will always create an array with a type-0.90.0 superblock. + * The newer usage is when assembling an array. + * In this case raid_disks will be 0, and the major_version field is + * use to determine which style super-blocks are to be found on the devices. + * The minor and patch _version numbers are also kept incase the + * super_block handler wishes to interpret them. + */ +static int set_array_info(mddev_t * mddev, mdu_array_info_t *info) +{ + + if (info->raid_disks == 0) { + /* just setting version number for superblock loading */ + if (info->major_version < 0 || + info->major_version >= sizeof(super_types)/sizeof(super_types[0]) || + super_types[info->major_version].name == NULL) { + /* maybe try to auto-load a module? */ + printk(KERN_INFO + "md: superblock version %d not known\n", + info->major_version); + return -EINVAL; + } + mddev->major_version = info->major_version; + mddev->minor_version = info->minor_version; + mddev->patch_version = info->patch_version; + return 0; + } + mddev->major_version = MD_MAJOR_VERSION; + mddev->minor_version = MD_MINOR_VERSION; + mddev->patch_version = MD_PATCHLEVEL_VERSION; + mddev->ctime = get_seconds(); + + mddev->level = info->level; + mddev->clevel[0] = 0; + mddev->size = info->size; + mddev->raid_disks = info->raid_disks; + /* don't set md_minor, it is determined by which /dev/md* was + * openned + */ + if (info->state & (1<recovery_cp = MaxSector; + else + mddev->recovery_cp = 0; + mddev->persistent = ! info->not_persistent; + mddev->external = 0; + + mddev->layout = info->layout; + mddev->chunk_size = info->chunk_size; + + mddev->max_disks = MD_SB_DISKS; + + if (mddev->persistent) + mddev->sb_dirty = 1; + + mddev->default_bitmap_offset = MD_SB_BYTES >> 9; + mddev->bitmap_offset = 0; + + mddev->reshape_position = MaxSector; + + /* + * Generate a 128 bit UUID + */ + get_random_bytes(mddev->uuid, 16); + + mddev->new_level = mddev->level; + mddev->new_chunk = mddev->chunk_size; + mddev->new_layout = mddev->layout; + mddev->delta_disks = 0; + + return 0; +} + +static int update_size(mddev_t *mddev, unsigned long size) +{ + mdk_rdev_t * rdev; + int rv; + struct list_head *tmp; + int fit = (size == 0); + + if (mddev->pers->resize == NULL) + return -EINVAL; + /* The "size" is the amount of each device that is used. + * This can only make sense for arrays with redundancy. + * linear and raid0 always use whatever space is available + * We can only consider changing the size if no resync + * or reconstruction is happening, and if the new size + * is acceptable. It must fit before the sb_offset or, + * if that is sync_thread) + return -EBUSY; + ITERATE_RDEV(mddev,rdev,tmp) { + sector_t avail; + if (rdev->sb_offset > rdev->data_offset) + avail = (rdev->sb_offset*2) - rdev->data_offset; + else + avail = get_capacity(rdev->bdev->bd_disk) + - rdev->data_offset; + if (fit && (size == 0 || size > avail/2)) + size = avail/2; + if (avail < ((sector_t)size << 1)) + return -ENOSPC; + } + rv = mddev->pers->resize(mddev, (sector_t)size *2); + if (!rv) { + struct block_device *bdev; + + bdev = bdget_disk(mddev->gendisk, 0); + if (bdev) { + mutex_lock(&bdev->bd_inode->i_mutex); + i_size_write(bdev->bd_inode, (loff_t)mddev->array_size << 10); + mutex_unlock(&bdev->bd_inode->i_mutex); + bdput(bdev); + } + } + return rv; +} + +static int update_raid_disks(mddev_t *mddev, int raid_disks) +{ + int rv; + /* change the number of raid disks */ + if (mddev->pers->check_reshape == NULL) + return -EINVAL; + if (raid_disks <= 0 || + raid_disks >= mddev->max_disks) + return -EINVAL; + if (mddev->sync_thread || mddev->reshape_position != MaxSector) + return -EBUSY; + mddev->delta_disks = raid_disks - mddev->raid_disks; + + rv = mddev->pers->check_reshape(mddev); + return rv; +} + + +/* + * update_array_info is used to change the configuration of an + * on-line array. + * The version, ctime,level,size,raid_disks,not_persistent, layout,chunk_size + * fields in the info are checked against the array. + * Any differences that cannot be handled will cause an error. + * Normally, only one change can be managed at a time. + */ +static int update_array_info(mddev_t *mddev, mdu_array_info_t *info) +{ + int rv = 0; + int cnt = 0; + int state = 0; + + /* calculate expected state,ignoring low bits */ + if (mddev->bitmap && mddev->bitmap_offset) + state |= (1 << MD_SB_BITMAP_PRESENT); + + if (mddev->major_version != info->major_version || + mddev->minor_version != info->minor_version || +/* mddev->patch_version != info->patch_version || */ + mddev->ctime != info->ctime || + mddev->level != info->level || +/* mddev->layout != info->layout || */ + !mddev->persistent != info->not_persistent|| + mddev->chunk_size != info->chunk_size || + /* ignore bottom 8 bits of state, and allow SB_BITMAP_PRESENT to change */ + ((state^info->state) & 0xfffffe00) + ) + return -EINVAL; + /* Check there is only one change */ + if (info->size >= 0 && mddev->size != info->size) cnt++; + if (mddev->raid_disks != info->raid_disks) cnt++; + if (mddev->layout != info->layout) cnt++; + if ((state ^ info->state) & (1< 1) return -EINVAL; + + if (mddev->layout != info->layout) { + /* Change layout + * we don't need to do anything at the md level, the + * personality will take care of it all. + */ + if (mddev->pers->reconfig == NULL) + return -EINVAL; + else + return mddev->pers->reconfig(mddev, info->layout, -1); + } + if (info->size >= 0 && mddev->size != info->size) + rv = update_size(mddev, info->size); + + if (mddev->raid_disks != info->raid_disks) + rv = update_raid_disks(mddev, info->raid_disks); + + if ((state ^ info->state) & (1<pers->quiesce == NULL) + return -EINVAL; + if (mddev->recovery || mddev->sync_thread) + return -EBUSY; + if (info->state & (1<bitmap) + return -EEXIST; + if (mddev->default_bitmap_offset == 0) + return -EINVAL; + mddev->bitmap_offset = mddev->default_bitmap_offset; + mddev->pers->quiesce(mddev, 1); + rv = bitmap_create(mddev); + if (rv) + bitmap_destroy(mddev); + mddev->pers->quiesce(mddev, 0); + } else { + /* remove the bitmap */ + if (!mddev->bitmap) + return -ENOENT; + if (mddev->bitmap->file) + return -EINVAL; + mddev->pers->quiesce(mddev, 1); + bitmap_destroy(mddev); + mddev->pers->quiesce(mddev, 0); + mddev->bitmap_offset = 0; + } + } + md_update_sb(mddev); + return rv; +} + +static int set_disk_faulty(mddev_t *mddev, dev_t dev) +{ + mdk_rdev_t *rdev; + + if (mddev->pers == NULL) + return -ENODEV; + + rdev = find_rdev(mddev, dev); + if (!rdev) + return -ENODEV; + + md_error(mddev, rdev); + return 0; +} + +static int md_getgeo(struct block_device *bdev, struct hd_geometry *geo) +{ + mddev_t *mddev = bdev->bd_disk->private_data; + + geo->heads = 2; + geo->sectors = 4; + geo->cylinders = get_capacity(mddev->gendisk) / 8; + return 0; +} + +static int md_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + void __user *argp = (void __user *)arg; + mddev_t *mddev = NULL; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + /* + * Commands dealing with the RAID driver but not any + * particular array: + */ + switch (cmd) + { + case RAID_VERSION: + err = get_version(argp); + goto done; + + case PRINT_RAID_DEBUG: + err = 0; + md_print_devices(); + goto done; + +#ifndef MODULE + case RAID_AUTORUN: + err = 0; + autostart_arrays(arg); + goto done; +#endif + default:; + } + + /* + * Commands creating/starting a new array: + */ + + mddev = inode->i_bdev->bd_disk->private_data; + + if (!mddev) { + BUG(); + goto abort; + } + + + if (cmd == START_ARRAY) { + /* START_ARRAY doesn't need to lock the array as autostart_array + * does the locking, and it could even be a different array + */ + static int cnt = 3; + if (cnt > 0 ) { + printk(KERN_WARNING + "md: %s(pid %d) used deprecated START_ARRAY ioctl. " + "This will not be supported beyond July 2006\n", + current->comm, current->pid); + cnt--; + } + err = autostart_array(new_decode_dev(arg)); + if (err) { + printk(KERN_WARNING "md: autostart failed!\n"); + goto abort; + } + goto done; + } + + err = mddev_lock(mddev); + if (err) { + printk(KERN_INFO + "md: ioctl lock interrupted, reason %d, cmd %d\n", + err, cmd); + goto abort; + } + + switch (cmd) + { + case SET_ARRAY_INFO: + { + mdu_array_info_t info; + if (!arg) + memset(&info, 0, sizeof(info)); + else if (copy_from_user(&info, argp, sizeof(info))) { + err = -EFAULT; + goto abort_unlock; + } + if (mddev->pers) { + err = update_array_info(mddev, &info); + if (err) { + printk(KERN_WARNING "md: couldn't update" + " array info. %d\n", err); + goto abort_unlock; + } + goto done_unlock; + } + if (!list_empty(&mddev->disks)) { + printk(KERN_WARNING + "md: array %s already has disks!\n", + mdname(mddev)); + err = -EBUSY; + goto abort_unlock; + } + if (mddev->raid_disks) { + printk(KERN_WARNING + "md: array %s already initialised!\n", + mdname(mddev)); + err = -EBUSY; + goto abort_unlock; + } + err = set_array_info(mddev, &info); + if (err) { + printk(KERN_WARNING "md: couldn't set" + " array info. %d\n", err); + goto abort_unlock; + } + } + goto done_unlock; + + default:; + } + + /* + * Commands querying/configuring an existing array: + */ + /* if we are not initialised yet, only ADD_NEW_DISK, STOP_ARRAY, + * RUN_ARRAY, and SET_BITMAP_FILE are allowed */ + if (!mddev->raid_disks && cmd != ADD_NEW_DISK && cmd != STOP_ARRAY + && cmd != RUN_ARRAY && cmd != SET_BITMAP_FILE) { + err = -ENODEV; + goto abort_unlock; + } + + /* + * Commands even a read-only array can execute: + */ + switch (cmd) + { + case GET_ARRAY_INFO: + err = get_array_info(mddev, argp); + goto done_unlock; + + case GET_BITMAP_FILE: + err = get_bitmap_file(mddev, argp); + goto done_unlock; + + case GET_DISK_INFO: + err = get_disk_info(mddev, argp); + goto done_unlock; + + case RESTART_ARRAY_RW: + err = restart_array(mddev); + goto done_unlock; + + case STOP_ARRAY: + err = do_md_stop (mddev, 0); + goto done_unlock; + + case STOP_ARRAY_RO: + err = do_md_stop (mddev, 1); + goto done_unlock; + + /* + * We have a problem here : there is no easy way to give a CHS + * virtual geometry. We currently pretend that we have a 2 heads + * 4 sectors (with a BIG number of cylinders...). This drives + * dosfs just mad... ;-) + */ + } + + /* + * The remaining ioctls are changing the state of the + * superblock, so we do not allow them on read-only arrays. + * However non-MD ioctls (e.g. get-size) will still come through + * here and hit the 'default' below, so only disallow + * 'md' ioctls, and switch to rw mode if started auto-readonly. + */ + if (_IOC_TYPE(cmd) == MD_MAJOR && + mddev->ro && mddev->pers) { + if (mddev->ro == 2) { + mddev->ro = 0; + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); + + } else { + err = -EROFS; + goto abort_unlock; + } + } + + switch (cmd) + { + case ADD_NEW_DISK: + { + mdu_disk_info_t info; + if (copy_from_user(&info, argp, sizeof(info))) + err = -EFAULT; + else + err = add_new_disk(mddev, &info); + goto done_unlock; + } + + case HOT_REMOVE_DISK: + err = hot_remove_disk(mddev, new_decode_dev(arg)); + goto done_unlock; + + case HOT_ADD_DISK: + err = hot_add_disk(mddev, new_decode_dev(arg)); + goto done_unlock; + + case SET_DISK_FAULTY: + err = set_disk_faulty(mddev, new_decode_dev(arg)); + goto done_unlock; + + case RUN_ARRAY: + err = do_md_run (mddev); + goto done_unlock; + + case SET_BITMAP_FILE: + err = set_bitmap_file(mddev, (int)arg); + goto done_unlock; + + default: + err = -EINVAL; + goto abort_unlock; + } + +done_unlock: +abort_unlock: + mddev_unlock(mddev); + + return err; +done: + if (err) + MD_BUG(); +abort: + return err; +} + +static int md_open(struct inode *inode, struct file *file) +{ + /* + * Succeed if we can lock the mddev, which confirms that + * it isn't being stopped right now. + */ + mddev_t *mddev = inode->i_bdev->bd_disk->private_data; + int err; + + if ((err = mddev_lock(mddev))) + goto out; + + err = 0; + mddev_get(mddev); + mddev_unlock(mddev); + + check_disk_change(inode->i_bdev); + out: + return err; +} + +static int md_release(struct inode *inode, struct file * file) +{ + mddev_t *mddev = inode->i_bdev->bd_disk->private_data; + + if (!mddev) + BUG(); + mddev_put(mddev); + + return 0; +} + +static int md_media_changed(struct gendisk *disk) +{ + mddev_t *mddev = disk->private_data; + + return mddev->changed; +} + +static int md_revalidate(struct gendisk *disk) +{ + mddev_t *mddev = disk->private_data; + + mddev->changed = 0; + return 0; +} +static struct block_device_operations md_fops = +{ + .owner = THIS_MODULE, + .open = md_open, + .release = md_release, + .ioctl = md_ioctl, + .getgeo = md_getgeo, + .media_changed = md_media_changed, + .revalidate_disk= md_revalidate, +}; + +static int md_thread(void * arg) +{ + mdk_thread_t *thread = arg; + + /* + * md_thread is a 'system-thread', it's priority should be very + * high. We avoid resource deadlocks individually in each + * raid personality. (RAID5 does preallocation) We also use RR and + * the very same RT priority as kswapd, thus we will never get + * into a priority inversion deadlock. + * + * we definitely have to have equal or higher priority than + * bdflush, otherwise bdflush will deadlock if there are too + * many dirty RAID5 blocks. + */ + + allow_signal(SIGKILL); + while (!kthread_should_stop()) { + + /* We need to wait INTERRUPTIBLE so that + * we don't add to the load-average. + * That means we need to be sure no signals are + * pending + */ + if (signal_pending(current)) + flush_signals(current); + + wait_event_interruptible_timeout + (thread->wqueue, + test_bit(THREAD_WAKEUP, &thread->flags) + || kthread_should_stop(), + thread->timeout); + try_to_freeze(); + + clear_bit(THREAD_WAKEUP, &thread->flags); + + thread->run(thread->mddev); + } + + return 0; +} + +void md_wakeup_thread(mdk_thread_t *thread) +{ + if (thread) { + dprintk("md: waking up MD thread %s.\n", thread->tsk->comm); + set_bit(THREAD_WAKEUP, &thread->flags); + wake_up(&thread->wqueue); + } +} + +mdk_thread_t *md_register_thread(void (*run) (mddev_t *), mddev_t *mddev, + const char *name) +{ + mdk_thread_t *thread; + + thread = kzalloc(sizeof(mdk_thread_t), GFP_KERNEL); + if (!thread) + return NULL; + + init_waitqueue_head(&thread->wqueue); + + thread->run = run; + thread->mddev = mddev; + thread->timeout = MAX_SCHEDULE_TIMEOUT; + thread->tsk = kthread_run(md_thread, thread, name, mdname(thread->mddev)); + if (IS_ERR(thread->tsk)) { + kfree(thread); + return NULL; + } + return thread; +} + +void md_unregister_thread(mdk_thread_t *thread) +{ + dprintk("interrupting MD-thread pid %d\n", thread->tsk->pid); + + kthread_stop(thread->tsk); + kfree(thread); +} + +void md_error(mddev_t *mddev, mdk_rdev_t *rdev) +{ + if (!mddev) { + MD_BUG(); + return; + } + + if (!rdev || test_bit(Faulty, &rdev->flags)) + return; +/* + dprintk("md_error dev:%s, rdev:(%d:%d), (caller: %p,%p,%p,%p).\n", + mdname(mddev), + MAJOR(rdev->bdev->bd_dev), MINOR(rdev->bdev->bd_dev), + __builtin_return_address(0),__builtin_return_address(1), + __builtin_return_address(2),__builtin_return_address(3)); +*/ + if (!mddev->pers->error_handler) + return; + mddev->pers->error_handler(mddev,rdev); + set_bit(MD_RECOVERY_INTR, &mddev->recovery); + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); + md_new_event_inintr(mddev); +} + +/* seq_file implementation /proc/mdstat */ + +static void status_unused(struct seq_file *seq) +{ + int i = 0; + mdk_rdev_t *rdev; + struct list_head *tmp; + + seq_printf(seq, "unused devices: "); + + ITERATE_RDEV_PENDING(rdev,tmp) { + char b[BDEVNAME_SIZE]; + i++; + seq_printf(seq, "%s ", + bdevname(rdev->bdev,b)); + } + if (!i) + seq_printf(seq, ""); + + seq_printf(seq, "\n"); +} + + +static void status_resync(struct seq_file *seq, mddev_t * mddev) +{ + sector_t max_blocks, resync, res; + unsigned long dt, db, rt; + int scale; + unsigned int per_milli; + + resync = (mddev->curr_resync - atomic_read(&mddev->recovery_active))/2; + + if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) + max_blocks = mddev->resync_max_sectors >> 1; + else + max_blocks = mddev->size; + + /* + * Should not happen. + */ + if (!max_blocks) { + MD_BUG(); + return; + } + /* Pick 'scale' such that (resync>>scale)*1000 will fit + * in a sector_t, and (max_blocks>>scale) will fit in a + * u32, as those are the requirements for sector_div. + * Thus 'scale' must be at least 10 + */ + scale = 10; + if (sizeof(sector_t) > sizeof(unsigned long)) { + while ( max_blocks/2 > (1ULL<<(scale+32))) + scale++; + } + res = (resync>>scale)*1000; + sector_div(res, (u32)((max_blocks>>scale)+1)); + + per_milli = res; + { + int i, x = per_milli/50, y = 20-x; + seq_printf(seq, "["); + for (i = 0; i < x; i++) + seq_printf(seq, "="); + seq_printf(seq, ">"); + for (i = 0; i < y; i++) + seq_printf(seq, "."); + seq_printf(seq, "] "); + } + seq_printf(seq, " %s =%3u.%u%% (%llu/%llu)", + (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)? + "reshape" : + (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ? + "resync" : "recovery")), + per_milli/10, per_milli % 10, + (unsigned long long) resync, + (unsigned long long) max_blocks); + + /* + * We do not want to overflow, so the order of operands and + * the * 100 / 100 trick are important. We do a +1 to be + * safe against division by zero. We only estimate anyway. + * + * dt: time from mark until now + * db: blocks written from mark until now + * rt: remaining time + */ + dt = ((jiffies - mddev->resync_mark) / HZ); + if (!dt) dt++; + db = resync - (mddev->resync_mark_cnt/2); + rt = (dt * ((unsigned long)(max_blocks-resync) / (db/100+1)))/100; + + seq_printf(seq, " finish=%lu.%lumin", rt / 60, (rt % 60)/6); + + seq_printf(seq, " speed=%ldK/sec", db/dt); +} + +static void *md_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct list_head *tmp; + loff_t l = *pos; + mddev_t *mddev; + + if (l >= 0x10000) + return NULL; + if (!l--) + /* header */ + return (void*)1; + + spin_lock(&all_mddevs_lock); + list_for_each(tmp,&all_mddevs) + if (!l--) { + mddev = list_entry(tmp, mddev_t, all_mddevs); + mddev_get(mddev); + spin_unlock(&all_mddevs_lock); + return mddev; + } + spin_unlock(&all_mddevs_lock); + if (!l--) + return (void*)2;/* tail */ + return NULL; +} + +static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct list_head *tmp; + mddev_t *next_mddev, *mddev = v; + + ++*pos; + if (v == (void*)2) + return NULL; + + spin_lock(&all_mddevs_lock); + if (v == (void*)1) + tmp = all_mddevs.next; + else + tmp = mddev->all_mddevs.next; + if (tmp != &all_mddevs) + next_mddev = mddev_get(list_entry(tmp,mddev_t,all_mddevs)); + else { + next_mddev = (void*)2; + *pos = 0x10000; + } + spin_unlock(&all_mddevs_lock); + + if (v != (void*)1) + mddev_put(mddev); + return next_mddev; + +} + +static void md_seq_stop(struct seq_file *seq, void *v) +{ + mddev_t *mddev = v; + + if (mddev && v != (void*)1 && v != (void*)2) + mddev_put(mddev); +} + +struct mdstat_info { + int event; +}; + +static int md_seq_show(struct seq_file *seq, void *v) +{ + mddev_t *mddev = v; + sector_t size; + struct list_head *tmp2; + mdk_rdev_t *rdev; + struct mdstat_info *mi = seq->private; + struct bitmap *bitmap; + + if (v == (void*)1) { + struct mdk_personality *pers; + seq_printf(seq, "Personalities : "); + spin_lock(&pers_lock); + list_for_each_entry(pers, &pers_list, list) + seq_printf(seq, "[%s] ", pers->name); + + spin_unlock(&pers_lock); + seq_printf(seq, "\n"); + mi->event = atomic_read(&md_event_count); + return 0; + } + if (v == (void*)2) { + status_unused(seq); + return 0; + } + + if (mddev_lock(mddev) < 0) + return -EINTR; + + if (mddev->pers || mddev->raid_disks || !list_empty(&mddev->disks)) { + seq_printf(seq, "%s : %sactive", mdname(mddev), + mddev->pers ? "" : "in"); + if (mddev->pers) { + if (mddev->ro==1) + seq_printf(seq, " (read-only)"); + if (mddev->ro==2) + seq_printf(seq, "(auto-read-only)"); + seq_printf(seq, " %s", mddev->pers->name); + } + + size = 0; + ITERATE_RDEV(mddev,rdev,tmp2) { + char b[BDEVNAME_SIZE]; + seq_printf(seq, " %s[%d]", + bdevname(rdev->bdev,b), rdev->desc_nr); + if (test_bit(WriteMostly, &rdev->flags)) + seq_printf(seq, "(W)"); + if (test_bit(Faulty, &rdev->flags)) { + seq_printf(seq, "(F)"); + continue; + } else if (rdev->raid_disk < 0) + seq_printf(seq, "(S)"); /* spare */ + size += rdev->size; + } + + if (!list_empty(&mddev->disks)) { + if (mddev->pers) + seq_printf(seq, "\n %llu blocks", + (unsigned long long)mddev->array_size); + else + seq_printf(seq, "\n %llu blocks", + (unsigned long long)size); + } + if (mddev->persistent) { + if (mddev->major_version != 0 || + mddev->minor_version != 90) { + seq_printf(seq," super %d.%d", + mddev->major_version, + mddev->minor_version); + } + } else + seq_printf(seq, " super non-persistent"); + + if (mddev->pers) { + mddev->pers->status (seq, mddev); + seq_printf(seq, "\n "); + if (mddev->pers->sync_request) { + if (mddev->curr_resync > 2) { + status_resync (seq, mddev); + seq_printf(seq, "\n "); + } else if (mddev->curr_resync == 1 || mddev->curr_resync == 2) + seq_printf(seq, "\tresync=DELAYED\n "); + else if (mddev->recovery_cp < MaxSector) + seq_printf(seq, "\tresync=PENDING\n "); + } + } else + seq_printf(seq, "\n "); + + if ((bitmap = mddev->bitmap)) { + unsigned long chunk_kb; + unsigned long flags; + spin_lock_irqsave(&bitmap->lock, flags); + chunk_kb = bitmap->chunksize >> 10; + seq_printf(seq, "bitmap: %lu/%lu pages [%luKB], " + "%lu%s chunk", + bitmap->pages - bitmap->missing_pages, + bitmap->pages, + (bitmap->pages - bitmap->missing_pages) + << (PAGE_SHIFT - 10), + chunk_kb ? chunk_kb : bitmap->chunksize, + chunk_kb ? "KB" : "B"); + if (bitmap->file) { + seq_printf(seq, ", file: "); + seq_path(seq, bitmap->file->f_vfsmnt, + bitmap->file->f_dentry," \t\n"); + } + + seq_printf(seq, "\n"); + spin_unlock_irqrestore(&bitmap->lock, flags); + } + + seq_printf(seq, "\n"); + } + mddev_unlock(mddev); + + return 0; +} + +static struct seq_operations md_seq_ops = { + .start = md_seq_start, + .next = md_seq_next, + .stop = md_seq_stop, + .show = md_seq_show, +}; + +static int md_seq_open(struct inode *inode, struct file *file) +{ + int error; + struct mdstat_info *mi = kmalloc(sizeof(*mi), GFP_KERNEL); + if (mi == NULL) + return -ENOMEM; + + error = seq_open(file, &md_seq_ops); + if (error) + kfree(mi); + else { + struct seq_file *p = file->private_data; + p->private = mi; + mi->event = atomic_read(&md_event_count); + } + return error; +} + +static int md_seq_release(struct inode *inode, struct file *file) +{ + struct seq_file *m = file->private_data; + struct mdstat_info *mi = m->private; + m->private = NULL; + kfree(mi); + return seq_release(inode, file); +} + +static unsigned int mdstat_poll(struct file *filp, poll_table *wait) +{ + struct seq_file *m = filp->private_data; + struct mdstat_info *mi = m->private; + int mask; + + poll_wait(filp, &md_event_waiters, wait); + + /* always allow read */ + mask = POLLIN | POLLRDNORM; + + if (mi->event != atomic_read(&md_event_count)) + mask |= POLLERR | POLLPRI; + return mask; +} + +static struct file_operations md_seq_fops = { + .open = md_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = md_seq_release, + .poll = mdstat_poll, +}; + +int register_md_personality(struct mdk_personality *p) +{ + spin_lock(&pers_lock); + list_add_tail(&p->list, &pers_list); + printk(KERN_INFO "md: %s personality registered for level %d\n", p->name, p->level); + spin_unlock(&pers_lock); + return 0; +} + +int unregister_md_personality(struct mdk_personality *p) +{ + printk(KERN_INFO "md: %s personality unregistered\n", p->name); + spin_lock(&pers_lock); + list_del_init(&p->list); + spin_unlock(&pers_lock); + return 0; +} + +static int is_mddev_idle(mddev_t *mddev) +{ + mdk_rdev_t * rdev; + struct list_head *tmp; + int idle; + unsigned long curr_events; + + idle = 1; + ITERATE_RDEV(mddev,rdev,tmp) { + struct gendisk *disk = rdev->bdev->bd_contains->bd_disk; + curr_events = disk_stat_read(disk, sectors[0]) + + disk_stat_read(disk, sectors[1]) - + atomic_read(&disk->sync_io); + /* The difference between curr_events and last_events + * will be affected by any new non-sync IO (making + * curr_events bigger) and any difference in the amount of + * in-flight syncio (making current_events bigger or smaller) + * The amount in-flight is currently limited to + * 32*64K in raid1/10 and 256*PAGE_SIZE in raid5/6 + * which is at most 4096 sectors. + * These numbers are fairly fragile and should be made + * more robust, probably by enforcing the + * 'window size' that md_do_sync sort-of uses. + * + * Note: the following is an unsigned comparison. + */ + if ((curr_events - rdev->last_events + 4096) > 8192) { + rdev->last_events = curr_events; + idle = 0; + } + } + return idle; +} + +void md_done_sync(mddev_t *mddev, int blocks, int ok) +{ + /* another "blocks" (512byte) blocks have been synced */ + atomic_sub(blocks, &mddev->recovery_active); + wake_up(&mddev->recovery_wait); + if (!ok) { + set_bit(MD_RECOVERY_ERR, &mddev->recovery); + md_wakeup_thread(mddev->thread); + // stop recovery, signal do_sync .... + } +} + + +/* md_write_start(mddev, bi) + * If we need to update some array metadata (e.g. 'active' flag + * in superblock) before writing, schedule a superblock update + * and wait for it to complete. + */ +void md_write_start(mddev_t *mddev, struct bio *bi) +{ + if (bio_data_dir(bi) != WRITE) + return; + + BUG_ON(mddev->ro == 1); + if (mddev->ro == 2) { + /* need to switch to read/write */ + mddev->ro = 0; + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_wakeup_thread(mddev->thread); + } + atomic_inc(&mddev->writes_pending); + if (mddev->in_sync) { + spin_lock_irq(&mddev->write_lock); + if (mddev->in_sync) { + mddev->in_sync = 0; + mddev->sb_dirty = 3; + md_wakeup_thread(mddev->thread); + } + spin_unlock_irq(&mddev->write_lock); + } + wait_event(mddev->sb_wait, mddev->sb_dirty==0); +} + +void md_write_end(mddev_t *mddev) +{ + if (atomic_dec_and_test(&mddev->writes_pending)) { + if (mddev->safemode == 2) + md_wakeup_thread(mddev->thread); + else if (mddev->safemode_delay) + mod_timer(&mddev->safemode_timer, jiffies + mddev->safemode_delay); + } +} + +static DECLARE_WAIT_QUEUE_HEAD(resync_wait); + +#define SYNC_MARKS 10 +#define SYNC_MARK_STEP (3*HZ) +void md_do_sync(mddev_t *mddev) +{ + mddev_t *mddev2; + unsigned int currspeed = 0, + window; + sector_t max_sectors,j, io_sectors; + unsigned long mark[SYNC_MARKS]; + sector_t mark_cnt[SYNC_MARKS]; + int last_mark,m; + struct list_head *tmp; + sector_t last_check; + int skipped = 0; + struct list_head *rtmp; + mdk_rdev_t *rdev; + + /* just incase thread restarts... */ + if (test_bit(MD_RECOVERY_DONE, &mddev->recovery)) + return; + if (mddev->ro) /* never try to sync a read-only array */ + return; + + /* we overload curr_resync somewhat here. + * 0 == not engaged in resync at all + * 2 == checking that there is no conflict with another sync + * 1 == like 2, but have yielded to allow conflicting resync to + * commense + * other == active in resync - this many blocks + * + * Before starting a resync we must have set curr_resync to + * 2, and then checked that every "conflicting" array has curr_resync + * less than ours. When we find one that is the same or higher + * we wait on resync_wait. To avoid deadlock, we reduce curr_resync + * to 1 if we choose to yield (based arbitrarily on address of mddev structure). + * This will mean we have to start checking from the beginning again. + * + */ + + do { + mddev->curr_resync = 2; + + try_again: + if (kthread_should_stop()) { + set_bit(MD_RECOVERY_INTR, &mddev->recovery); + goto skip; + } + ITERATE_MDDEV(mddev2,tmp) { + if (mddev2 == mddev) + continue; + if (mddev2->curr_resync && + match_mddev_units(mddev,mddev2)) { + DEFINE_WAIT(wq); + if (mddev < mddev2 && mddev->curr_resync == 2) { + /* arbitrarily yield */ + mddev->curr_resync = 1; + wake_up(&resync_wait); + } + if (mddev > mddev2 && mddev->curr_resync == 1) + /* no need to wait here, we can wait the next + * time 'round when curr_resync == 2 + */ + continue; + prepare_to_wait(&resync_wait, &wq, TASK_UNINTERRUPTIBLE); + if (!kthread_should_stop() && + mddev2->curr_resync >= mddev->curr_resync) { + printk(KERN_INFO "md: delaying resync of %s" + " until %s has finished resync (they" + " share one or more physical units)\n", + mdname(mddev), mdname(mddev2)); + mddev_put(mddev2); + schedule(); + finish_wait(&resync_wait, &wq); + goto try_again; + } + finish_wait(&resync_wait, &wq); + } + } + } while (mddev->curr_resync < 2); + + j = 0; + if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) { + /* resync follows the size requested by the personality, + * which defaults to physical size, but can be virtual size + */ + max_sectors = mddev->resync_max_sectors; + mddev->resync_mismatches = 0; + /* we don't use the checkpoint if there's a bitmap */ + if (!mddev->bitmap && + !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) + j = mddev->recovery_cp; + } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) + max_sectors = mddev->size << 1; + else { + /* recovery follows the physical size of devices */ + max_sectors = mddev->size << 1; + j = MaxSector; + ITERATE_RDEV(mddev,rdev,rtmp) + if (rdev->raid_disk >= 0 && + !test_bit(Faulty, &rdev->flags) && + !test_bit(In_sync, &rdev->flags) && + rdev->recovery_offset < j) + j = rdev->recovery_offset; + } + + printk(KERN_INFO "md: syncing RAID array %s\n", mdname(mddev)); + printk(KERN_INFO "md: minimum _guaranteed_ reconstruction speed:" + " %d KB/sec/disc.\n", speed_min(mddev)); + printk(KERN_INFO "md: using maximum available idle IO bandwidth " + "(but not more than %d KB/sec) for reconstruction.\n", + speed_max(mddev)); + + is_mddev_idle(mddev); /* this also initializes IO event counters */ + + io_sectors = 0; + for (m = 0; m < SYNC_MARKS; m++) { + mark[m] = jiffies; + mark_cnt[m] = io_sectors; + } + last_mark = 0; + mddev->resync_mark = mark[last_mark]; + mddev->resync_mark_cnt = mark_cnt[last_mark]; + + /* + * Tune reconstruction: + */ + window = 32*(PAGE_SIZE/512); + printk(KERN_INFO "md: using %dk window, over a total of %llu blocks.\n", + window/2,(unsigned long long) max_sectors/2); + + atomic_set(&mddev->recovery_active, 0); + init_waitqueue_head(&mddev->recovery_wait); + last_check = 0; + + if (j>2) { + printk(KERN_INFO + "md: resuming recovery of %s from checkpoint.\n", + mdname(mddev)); + mddev->curr_resync = j; + } + + while (j < max_sectors) { + sector_t sectors; + + skipped = 0; + if (j >= mddev->resync_max) { + sysfs_notify(&mddev->kobj, NULL, "sync_completed"); + wait_event(mddev->recovery_wait, + mddev->resync_max > j || kthread_should_stop()); + } + if (kthread_should_stop()) + goto interrupted; + sectors = mddev->pers->sync_request(mddev, j, &skipped, + currspeed < speed_min(mddev)); + if (sectors == 0) { + set_bit(MD_RECOVERY_ERR, &mddev->recovery); + goto out; + } + + if (!skipped) { /* actual IO requested */ + io_sectors += sectors; + atomic_add(sectors, &mddev->recovery_active); + } + + j += sectors; + if (j>1) mddev->curr_resync = j; + if (last_check == 0) + /* this is the earliers that rebuilt will be + * visible in /proc/mdstat + */ + md_new_event(mddev); + + if (last_check + window > io_sectors || j == max_sectors) + continue; + + last_check = io_sectors; + + if (test_bit(MD_RECOVERY_INTR, &mddev->recovery) || + test_bit(MD_RECOVERY_ERR, &mddev->recovery)) + break; + + repeat: + if (time_after_eq(jiffies, mark[last_mark] + SYNC_MARK_STEP )) { + /* step marks */ + int next = (last_mark+1) % SYNC_MARKS; + + mddev->resync_mark = mark[next]; + mddev->resync_mark_cnt = mark_cnt[next]; + mark[next] = jiffies; + mark_cnt[next] = io_sectors - atomic_read(&mddev->recovery_active); + last_mark = next; + } + + + if (kthread_should_stop()) + goto interrupted; + + + /* + * this loop exits only if either when we are slower than + * the 'hard' speed limit, or the system was IO-idle for + * a jiffy. + * the system might be non-idle CPU-wise, but we only care + * about not overloading the IO subsystem. (things like an + * e2fsck being done on the RAID array should execute fast) + */ + mddev->queue->unplug_fn(mddev->queue); + cond_resched(); + + currspeed = ((unsigned long)(io_sectors-mddev->resync_mark_cnt))/2 + /((jiffies-mddev->resync_mark)/HZ +1) +1; + + if (currspeed > speed_min(mddev)) { + if ((currspeed > speed_max(mddev)) || + !is_mddev_idle(mddev)) { + msleep(500); + goto repeat; + } + } + } + printk(KERN_INFO "md: %s: sync done.\n",mdname(mddev)); + /* + * this also signals 'finished resyncing' to md_stop + */ + out: + mddev->queue->unplug_fn(mddev->queue); + + wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active)); + + /* tell personality that we are finished */ + mddev->pers->sync_request(mddev, max_sectors, &skipped, 1); + + if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery) && + test_bit(MD_RECOVERY_SYNC, &mddev->recovery) && + !test_bit(MD_RECOVERY_CHECK, &mddev->recovery) && + mddev->curr_resync > 2) { + if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) { + if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) { + if (mddev->curr_resync >= mddev->recovery_cp) { + printk(KERN_INFO + "md: checkpointing recovery of %s.\n", + mdname(mddev)); + mddev->recovery_cp = mddev->curr_resync; + } + } else + mddev->recovery_cp = MaxSector; + } else { + if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery)) + mddev->curr_resync = MaxSector; + ITERATE_RDEV(mddev,rdev,rtmp) + if (rdev->raid_disk >= 0 && + !test_bit(Faulty, &rdev->flags) && + !test_bit(In_sync, &rdev->flags) && + rdev->recovery_offset < mddev->curr_resync) + rdev->recovery_offset = mddev->curr_resync; + } + } + + skip: + mddev->curr_resync = 0; + mddev->resync_max = MaxSector; + sysfs_notify(&mddev->kobj, NULL, "sync_completed"); + wake_up(&resync_wait); + set_bit(MD_RECOVERY_DONE, &mddev->recovery); + md_wakeup_thread(mddev->thread); + return; + + interrupted: + /* + * got a signal, exit. + */ + printk(KERN_INFO + "md: md_do_sync() got signal ... exiting\n"); + set_bit(MD_RECOVERY_INTR, &mddev->recovery); + goto out; + +} +EXPORT_SYMBOL_GPL(md_do_sync); + + +/* + * This routine is regularly called by all per-raid-array threads to + * deal with generic issues like resync and super-block update. + * Raid personalities that don't have a thread (linear/raid0) do not + * need this as they never do any recovery or update the superblock. + * + * It does not do any resync itself, but rather "forks" off other threads + * to do that as needed. + * When it is determined that resync is needed, we set MD_RECOVERY_RUNNING in + * "->recovery" and create a thread at ->sync_thread. + * When the thread finishes it sets MD_RECOVERY_DONE (and might set MD_RECOVERY_ERR) + * and wakeups up this thread which will reap the thread and finish up. + * This thread also removes any faulty devices (with nr_pending == 0). + * + * The overall approach is: + * 1/ if the superblock needs updating, update it. + * 2/ If a recovery thread is running, don't do anything else. + * 3/ If recovery has finished, clean up, possibly marking spares active. + * 4/ If there are any faulty devices, remove them. + * 5/ If array is degraded, try to add spares devices + * 6/ If array has spares or is not in-sync, start a resync thread. + */ +void md_check_recovery(mddev_t *mddev) +{ + mdk_rdev_t *rdev; + struct list_head *rtmp; + + + if (mddev->bitmap) + bitmap_daemon_work(mddev->bitmap); + + if (mddev->ro) + return; + + if (signal_pending(current)) { + if (mddev->pers->sync_request) { + printk(KERN_INFO "md: %s in immediate safe mode\n", + mdname(mddev)); + mddev->safemode = 2; + } + flush_signals(current); + } + + if ( ! ( + (mddev->sb_dirty && !mddev->external) || + test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) || + test_bit(MD_RECOVERY_DONE, &mddev->recovery) || + (mddev->safemode == 1) || + (mddev->safemode == 2 && ! atomic_read(&mddev->writes_pending) + && !mddev->in_sync && mddev->recovery_cp == MaxSector) + )) + return; + + if (mddev_trylock(mddev)) { + int spares =0; + + spin_lock_irq(&mddev->write_lock); + if (mddev->safemode && !atomic_read(&mddev->writes_pending) && + !mddev->in_sync && mddev->recovery_cp == MaxSector) { + mddev->in_sync = 1; + if (mddev->persistent) + mddev->sb_dirty = 3; + } + if (mddev->safemode == 1) + mddev->safemode = 0; + spin_unlock_irq(&mddev->write_lock); + + if (mddev->sb_dirty) + md_update_sb(mddev); + + + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) && + !test_bit(MD_RECOVERY_DONE, &mddev->recovery)) { + /* resync/recovery still happening */ + clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + goto unlock; + } + if (mddev->sync_thread) { + /* resync has finished, collect result */ + md_unregister_thread(mddev->sync_thread); + mddev->sync_thread = NULL; + if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery) && + !test_bit(MD_RECOVERY_INTR, &mddev->recovery)) { + /* success...*/ + /* activate any spares */ + mddev->pers->spare_active(mddev); + } + md_update_sb(mddev); + + /* if array is no-longer degraded, then any saved_raid_disk + * information must be scrapped + */ + if (!mddev->degraded) + ITERATE_RDEV(mddev,rdev,rtmp) + rdev->saved_raid_disk = -1; + + mddev->recovery = 0; + /* flag recovery needed just to double check */ + set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + md_new_event(mddev); + goto unlock; + } + /* Clear some bits that don't mean anything, but + * might be left set + */ + clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + clear_bit(MD_RECOVERY_ERR, &mddev->recovery); + clear_bit(MD_RECOVERY_INTR, &mddev->recovery); + clear_bit(MD_RECOVERY_DONE, &mddev->recovery); + + if (test_bit(MD_RECOVERY_FROZEN, &mddev->recovery)) + goto unlock; + /* no recovery is running. + * remove any failed drives, then + * add spares if possible. + * Spare are also removed and re-added, to allow + * the personality to fail the re-add. + */ + ITERATE_RDEV(mddev,rdev,rtmp) + if (rdev->raid_disk >= 0 && + (test_bit(Faulty, &rdev->flags) || ! test_bit(In_sync, &rdev->flags)) && + atomic_read(&rdev->nr_pending)==0) { + if (mddev->pers->hot_remove_disk(mddev, rdev->raid_disk)==0) { + char nm[20]; + sprintf(nm,"rd%d", rdev->raid_disk); + sysfs_remove_link(&mddev->kobj, nm); + rdev->raid_disk = -1; + } + } + + if (mddev->degraded) { + ITERATE_RDEV(mddev,rdev,rtmp) + if (rdev->raid_disk < 0 + && !test_bit(Faulty, &rdev->flags)) { + rdev->recovery_offset = 0; + if (mddev->pers->hot_add_disk(mddev,rdev)) { + char nm[20]; + sprintf(nm, "rd%d", rdev->raid_disk); + sysfs_create_link(&mddev->kobj, &rdev->kobj, nm); + spares++; + md_new_event(mddev); + } else + break; + } + } + + if (spares) { + clear_bit(MD_RECOVERY_SYNC, &mddev->recovery); + clear_bit(MD_RECOVERY_CHECK, &mddev->recovery); + } else if (mddev->recovery_cp < MaxSector) { + set_bit(MD_RECOVERY_SYNC, &mddev->recovery); + } else if (!test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) + /* nothing to be done ... */ + goto unlock; + + if (mddev->pers->sync_request) { + set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); + if (spares && mddev->bitmap && ! mddev->bitmap->file) { + /* We are adding a device or devices to an array + * which has the bitmap stored on all devices. + * So make sure all bitmap pages get written + */ + bitmap_write_all(mddev->bitmap); + } + mddev->sync_thread = md_register_thread(md_do_sync, + mddev, + "%s_resync"); + if (!mddev->sync_thread) { + printk(KERN_ERR "%s: could not start resync" + " thread...\n", + mdname(mddev)); + /* leave the spares where they are, it shouldn't hurt */ + mddev->recovery = 0; + } else + md_wakeup_thread(mddev->sync_thread); + md_new_event(mddev); + } + unlock: + mddev_unlock(mddev); + } +} + +static int md_notify_reboot(struct notifier_block *this, + unsigned long code, void *x) +{ + struct list_head *tmp; + mddev_t *mddev; + + if ((code == SYS_DOWN) || (code == SYS_HALT) || (code == SYS_POWER_OFF)) { + + printk(KERN_INFO "md: stopping all md devices.\n"); + + ITERATE_MDDEV(mddev,tmp) + if (mddev_trylock(mddev)) { + do_md_stop (mddev, 1); + mddev_unlock(mddev); + } + /* + * certain more exotic SCSI devices are known to be + * volatile wrt too early system reboots. While the + * right place to handle this issue is the given + * driver, we do want to have a safe RAID driver ... + */ + mdelay(1000*1); + } + return NOTIFY_DONE; +} + +static struct notifier_block md_notifier = { + .notifier_call = md_notify_reboot, + .next = NULL, + .priority = INT_MAX, /* before any real devices */ +}; + +static void md_geninit(void) +{ + struct proc_dir_entry *p; + + dprintk("md: sizeof(mdp_super_t) = %d\n", (int)sizeof(mdp_super_t)); + + p = create_proc_entry("mdstat", S_IRUGO, NULL); + if (p) + p->proc_fops = &md_seq_fops; +} + +static int __init md_init(void) +{ + int minor; + + printk(KERN_INFO "md: md driver %d.%d.%d MAX_MD_DEVS=%d," + " MD_SB_DISKS=%d\n", + MD_MAJOR_VERSION, MD_MINOR_VERSION, + MD_PATCHLEVEL_VERSION, MAX_MD_DEVS, MD_SB_DISKS); + printk(KERN_INFO "md: bitmap version %d.%d\n", BITMAP_MAJOR_HI, + BITMAP_MINOR); + + if (register_blkdev(MAJOR_NR, "md")) + return -1; + if ((mdp_major=register_blkdev(0, "mdp"))<=0) { + unregister_blkdev(MAJOR_NR, "md"); + return -1; + } + devfs_mk_dir("md"); + blk_register_region(MKDEV(MAJOR_NR, 0), MAX_MD_DEVS, THIS_MODULE, + md_probe, NULL, NULL); + blk_register_region(MKDEV(mdp_major, 0), MAX_MD_DEVS<= 0 && dev_cnt < 127) + detected_devices[dev_cnt++] = dev; +} + + +static void autostart_arrays(int part) +{ + mdk_rdev_t *rdev; + int i; + + printk(KERN_INFO "md: Autodetecting RAID arrays.\n"); + + for (i = 0; i < dev_cnt; i++) { + dev_t dev = detected_devices[i]; + + rdev = md_import_device(dev,0, 0); + if (IS_ERR(rdev)) + continue; + + if (test_bit(Faulty, &rdev->flags)) { + MD_BUG(); + continue; + } + list_add(&rdev->same_set, &pending_raid_disks); + } + dev_cnt = 0; + + autorun_devices(part); +} + +#endif + +static __exit void md_exit(void) +{ + mddev_t *mddev; + struct list_head *tmp; + int i; + blk_unregister_region(MKDEV(MAJOR_NR,0), MAX_MD_DEVS); + blk_unregister_region(MKDEV(mdp_major,0), MAX_MD_DEVS << MdpMinorShift); + for (i=0; i < MAX_MD_DEVS; i++) + devfs_remove("md/%d", i); + for (i=0; i < MAX_MD_DEVS; i++) + devfs_remove("md/d%d", i); + + devfs_remove("md"); + + unregister_blkdev(MAJOR_NR,"md"); + unregister_blkdev(mdp_major, "mdp"); + unregister_reboot_notifier(&md_notifier); + unregister_sysctl_table(raid_table_header); + remove_proc_entry("mdstat", NULL); + ITERATE_MDDEV(mddev,tmp) { + struct gendisk *disk = mddev->gendisk; + if (!disk) + continue; + export_array(mddev); + del_gendisk(disk); + put_disk(disk); + mddev->gendisk = NULL; + mddev_put(mddev); + } +} + +module_init(md_init) +module_exit(md_exit) + +static int get_ro(char *buffer, struct kernel_param *kp) +{ + return sprintf(buffer, "%d", start_readonly); +} +static int set_ro(const char *val, struct kernel_param *kp) +{ + char *e; + int num = simple_strtoul(val, &e, 10); + if (*val && (*e == '\0' || *e == '\n')) { + start_readonly = num; + return 0; + } + return -EINVAL; +} + +module_param_call(start_ro, set_ro, get_ro, NULL, S_IRUSR|S_IWUSR); +module_param(start_dirty_degraded, int, S_IRUGO|S_IWUSR); + + +EXPORT_SYMBOL(register_md_personality); +EXPORT_SYMBOL(unregister_md_personality); +EXPORT_SYMBOL(md_error); +EXPORT_SYMBOL(md_done_sync); +EXPORT_SYMBOL(md_write_start); +EXPORT_SYMBOL(md_write_end); +EXPORT_SYMBOL(md_register_thread); +EXPORT_SYMBOL(md_unregister_thread); +EXPORT_SYMBOL(md_wakeup_thread); +EXPORT_SYMBOL(md_check_recovery); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("md"); +MODULE_ALIAS_BLOCKDEV_MAJOR(MD_MAJOR); diff --git a/demo/vpatch.c b/demo/vpatch.c new file mode 100644 index 0000000..fae8714 --- /dev/null +++ b/demo/vpatch.c @@ -0,0 +1,668 @@ + +/* + * vpatch - visual front end for wiggle + * + * "files" display, lists all files with Statistics + * - can hide various lines including subdirectories + * and files without wiggles or conflicts + * "diff" display shows merged file with different parts + * in different colours + * - untouched are pale A_DIM + * - matched/remaining are regular A_NORMAL + * - matched/removed are red/ + * + * underlined A_UNDERLINE + * - unmatched in file are A_STANDOUT + * - unmatched in patch are A_STANDOUT|A_UNDERLINE ??? + * - inserted are inverse/green ?? A_REVERSE + * + * The window can be split horizontally or vertically and two different + * views displayed. They will have different parts missing + * + * So a display of NORMAL, underline, standout|underline reverse + * should show a normal patch. + * + */ + +#include "wiggle.h" +#include +#include +#include +#include +#include +#include + +#define assert(x) do { if (!(x)) abort(); } while (0) + +struct plist { + char *file; + unsigned int start, end; + int parent; + int next, prev, last; + int open; + int chunks, wiggles, conflicts; +}; + +struct plist *patch_add_file(struct plist *pl, int *np, char *file, + unsigned int start, unsigned int end) +{ + /* size of pl is 0, 16, n^2 */ + int n = *np; + int asize; + +/* printf("adding %s at %d: %u %u\n", file, n, start, end); */ + if (n==0) asize = 0; + else if (n<=16) asize = 16; + else if ((n&(n-1))==0) asize = n; + else asize = n+1; /* not accurate, but not too large */ + if (asize <= n) { + /* need to extend array */ + struct plist *npl; + if (asize < 16) asize = 16; + else asize += asize; + npl = realloc(pl, asize * sizeof(struct plist)); + if (!npl) { + fprintf(stderr, "malloc failed - skipping %s\n", file); + return pl; + } + pl = npl; + } + pl[n].file = file; + pl[n].start = start; + pl[n].end = end; + pl[n].last = pl[n].next = pl[n].prev = pl[n].parent = -1; + pl[n].chunks = pl[n].wiggles = pl[n].conflicts = 0; + pl[n].open = 1; + *np = n+1; + return pl; +} + + + +struct plist *parse_patch(FILE *f, FILE *of, int *np) +{ + /* read a multi-file patch from 'f' and record relevant + * details in a plist. + * if 'of' >= 0, fd might not be seekable so we write + * to 'of' and use lseek on 'of' to determine position + */ + struct plist *plist = NULL; + + while (!feof(f)) { + /* first, find the start of a patch: "\n+++ " + * grab the file name and scan to the end of a line + */ + char *target="\n+++ "; + char *target2="\n--- "; + char *pos = target; + int c; + char name[1024]; + unsigned start, end; + + while (*pos && (c=fgetc(f)) != EOF ) { + if (of) fputc(c, of); + if (c == *pos) + pos++; + else pos = target; + } + if (c == EOF) + break; + assert(c == ' '); + /* now read a file name */ + pos = name; + while ((c=fgetc(f)) != EOF && c != '\t' && c != '\n' && c != ' ' && + pos - name < 1023) { + *pos++ = c; + if (of) fputc(c, of); + } + *pos = 0; + if (c == EOF) + break; + if (of) fputc(c, of); + while (c != '\n' && (c=fgetc(f)) != EOF) { + if (of) fputc(c, of); + } + start = of ? ftell(of) : ftell(f); + + if (c == EOF) break; + + /* now skip to end - "\n--- " */ + pos = target2+1; + + while (*pos && (c=fgetc(f)) != EOF) { + if (of) fputc(c, of); + if (c == *pos) + pos++; + else pos = target2; + } + if (pos > target2) { + end = of ? ftell(of) : ftell(f); + end -= (pos - target2) - 1; + plist = patch_add_file(plist, np, + strdup(name), start, end); + } + } + return plist; +} +void die() +{ + fprintf(stderr,"vpatch: fatal error\n"); + abort(); + exit(3); +} + + +static struct stream load_segment(FILE *f, + unsigned int start, unsigned int end) +{ + struct stream s; + s.len = end - start; + s.body = malloc(s.len); + if (s.body) { + fseek(f, start, 0); + if (fread(s.body, 1, s.len, f) != s.len) { + free(s.body); + s.body = NULL; + } + } else + die(); + return s; +} + + +void catch(int sig) +{ + if (sig == SIGINT) { + signal(sig, catch); + return; + } + nocbreak();nl();endwin(); + printf("Died on signal %d\n", sig); + exit(2); +} + +int pl_cmp(const void *av, const void *bv) +{ + const struct plist *a = av; + const struct plist *b = bv; + return strcmp(a->file, b->file); +} + +int common_depth(char *a, char *b) +{ + /* find number of patch segments that these two have + * in common + */ + int depth = 0; + while(1) { + char *c; + int al, bl; + c = strchr(a, '/'); + if (c) al = c-a; else al = strlen(a); + c = strchr(b, '/'); + if (c) bl = c-b; else bl = strlen(b); + if (al == 0 || al != bl || strncmp(a,b,al) != 0) + return depth; + a+= al; + while (*a=='/') a++; + b+= bl; + while(*b=='/') b++; + + depth++; + } +} + +struct plist *add_dir(struct plist *pl, int *np, char *file, char *curr) +{ + /* any parent of file that is not a parent of curr + * needs to be added to pl + */ + int d = common_depth(file, curr); + char *buf = curr; + while (d) { + char *c = strchr(file, '/'); + int l; + if (c) l = c-file; else l = strlen(file); + file += l; + curr += l; + while (*file == '/') file++; + while (*curr == '/') curr++; + d--; + } + while (*file) { + if (curr > buf && curr[-1] != '/') + *curr++ = '/'; + while (*file && *file != '/') + *curr++ = *file++; + while (*file == '/') *file++; + *curr = '\0'; + if (*file) + pl = patch_add_file(pl, np, strdup(buf), + 0, 0); + } + return pl; +} + +struct plist *sort_patches(struct plist *pl, int *np) +{ + /* sort the patches, add directory names, and re-sort */ + char curr[1024]; + char *prev; + int parents[100]; + int prevnode[100]; + int i, n; + qsort(pl, *np, sizeof(struct plist), pl_cmp); + curr[0] = 0; + n = *np; + for (i=0; i -1) + pl[pl[i].prev].next = i; + prev = pl[i].file; + parents[d] = i; + prevnode[d] = i; + prevnode[d+1] = -1; + } + return pl; +} + +int get_prev(int pos, struct plist *pl, int n) +{ + if (pos == -1) return pos; + if (pl[pos].prev == -1) + return pl[pos].parent; + pos = pl[pos].prev; + while (pl[pos].open && + pl[pos].last >= 0) + pos = pl[pos].last; + return pos; +} + +int get_next(int pos, struct plist *pl, int n) +{ + if (pos == -1) return pos; + if (pl[pos].open) { + if (pos +1 < n) + return pos+1; + else + return -1; + } + while (pos >= 0 && pl[pos].next == -1) + pos = pl[pos].parent; + if (pos >= 0) + pos = pl[pos].next; + return pos; +} + +void draw_one(int row, struct plist *pl) +{ + char hdr[10]; + hdr[0] = 0; + + if (pl == NULL) { + move(row,0); + clrtoeol(); + return; + } + if (pl->chunks > 99) + strcpy(hdr, "XX"); + else sprintf(hdr, "%02d", pl->chunks); + if (pl->wiggles > 99) + strcpy(hdr, " XX"); + else sprintf(hdr+2, " %02d", pl->wiggles); + if (pl->conflicts > 99) + strcpy(hdr, " XX"); + else sprintf(hdr+5, " %02d ", pl->conflicts); + if (pl->end) + strcpy(hdr+9, "= "); + else if (pl->open) + strcpy(hdr+9, "+ "); + else strcpy(hdr+9, "- "); + + mvaddstr(row, 0, hdr); + mvaddstr(row, 11, pl->file); + clrtoeol(); +} + +void addword(struct elmnt e) +{ + addnstr(e.start, e.len); +} + +void diff_window(struct plist *p, FILE *f) +{ + /* + * I wonder what to display here .... + */ + struct stream s; + struct stream s1, s2; + struct file f1, f2; + struct csl *csl; + char buf[100]; + int ch; + s = load_segment(f, p->start, p->end); + ch = split_patch(s, &s1, &s2); + + clear(); + sprintf(buf, "Chunk count: %d\n", ch); + mvaddstr(1,1,buf); clrtoeol(); + + + f1 = split_stream(s1, ByWord, 0); + f2 = split_stream(s2, ByWord, 0); + + csl = diff(f1, f2); + + /* now try to display the diff highlighted */ + int sol = 1; + int a=0, b=0; + + while(aa) { + if (sol) { + int a1; + /* if we remove a whole line, output +line, + * else clear sol and retry + */ + sol = 0; + for (a1=a; a1a; a1++) + if (f1.list[a1].start[0] == '\n') { + sol = 1; + break; + } + if (sol) { + addch('-'); + attron(A_UNDERLINE); + for (; aa; a++) { + addword(f1.list[a]); + if (f1.list[a].start[0] == '\n') { + a++; + break; + } + } + attroff(A_UNDERLINE); + } else addch('|'); + } + if (!sol) { + attron(A_UNDERLINE); + do { + if (sol) { + attroff(A_UNDERLINE); + addch('|'); + attron(A_UNDERLINE); + } + addword(f1.list[a]); + sol = (f1.list[a].start[0] == '\n'); + a++; + } while (a < csl->a); + attroff(A_UNDERLINE); + if (sol) addch('|'); + sol = 0; + } + } else if (b < csl->b) { + if (sol) { + int b1; + sol = 0; + for (b1=b; b1b; b1++) + if (f2.list[b1].start[0] == '\n') { + sol = 1; + break; + } + if (sol) { + addch('+'); + attron(A_BOLD); + for (; bb; b++) { + addword(f2.list[b]); + if (f2.list[b].start[0] == '\n') { + b++; + break; + } + } + attroff(A_BOLD); + } else addch('|'); + } + if (!sol) { + attron(A_BOLD); + do { + if (sol) { + attroff(A_BOLD); + addch('|'); + attron(A_BOLD); + } + addword(f2.list[b]); + sol = (f2.list[b].start[0] == '\n'); + b++; + } while (b < csl->b); + attroff(A_BOLD); + if (sol) addch('|'); + sol = 0; + } + } else { + if (sol) { + int a1; + sol = 0; + for (a1=a; a1a+csl->len; a1++) + if (f1.list[a1].start[0] == '\n') + sol = 1; + if (sol) { + if (f1.list[a].start[0]) { + addch(' '); + for (; a< csl->a+csl->len; a++,b++) { + addword(f1.list[a]); + if (f1.list[a].start[0]=='\n') { + a++,b++; + break; + } + } + } else { + addstr("SEP\n"); + a++; b++; + } + } else addch('|'); + } + if (!sol) { + addword(f1.list[a]); + if (f1.list[a].start[0] == '\n') + sol = 1; + a++; + b++; + } + if (a >= csl->a+csl->len) + csl++; + } + } + + + getch(); + + free(s1.body); + free(s2.body); + free(f1.list); + free(f2.list); +} + +void main_window(struct plist *pl, int n, FILE *f) +{ + /* The main window lists all files together with summary information: + * number of chunks, number of wiggles, number of conflicts. + * The list is scrollable + * When a entry is 'selected', we switch to the 'file' window + * The list can be condensed by removing files with no conflict + * or no wiggles, or removing subdirectories + * + * We record which file in the list is 'current', and which + * screen line it is on. We try to keep things stable while + * moving. + * + * Counts are printed before the name using at most 2 digits. + * Numbers greater than 99 are XX + * Ch Wi Co File + * 27 5 1 drivers/md/md.c + * + * A directory show the sum in all children. + * + * Commands: + * select: enter, space, mouseclick + * on file, go to file window + * on directory, toggle open + * up: k, p, control-p uparrow + * Move to previous open object + * down: j, n, control-n, downarrow + * Move to next open object + * + */ + int pos=0; /* position in file */ + int row=1; /* position on screen */ + int rows; /* size of screen in rows */ + int cols; + int tpos, i; + int refresh = 2; + int c; + + while(1) { + if (refresh == 2) { + clear(); + attron(A_BOLD); + mvaddstr(0,0,"Ch Wi Co Patched Files"); + move(2,0); + attroff(A_BOLD); + refresh = 1; + } + if (row <1 || row >= rows) + refresh = 1; + if (refresh) { + refresh = 0; + getmaxyx(stdscr, rows, cols); + if (row >= rows +3) + row = (rows+1)/2; + if (row >= rows) + row = rows-1; + tpos = pos; + for (i=row; i>1; i--) { + tpos = get_prev(tpos, pl, n); + if (tpos == -1) { + row = row - i + 1; + break; + } + } + /* Ok, row and pos could be trustworthy now */ + tpos = pos; + for (i=row; i>=1; i--) { + draw_one(i, &pl[tpos]); + tpos = get_prev(tpos, pl, n); + } + tpos = pos; + for (i=row+1; i= 0) + draw_one(i, &pl[tpos]); + else + draw_one(i, NULL); + } + } + move(row, 9); + c = getch(); + switch(c) { + case 'j': + case 'n': + case 'N': + case 'N'-64: + case KEY_DOWN: + tpos = get_next(pos, pl, n); + if (tpos >= 0) { + pos = tpos; + row++; + } + break; + case 'k': + case 'p': + case 'P': + case 'P'-64: + case KEY_UP: + tpos = get_prev(pos, pl, n); + if (tpos >= 0) { + pos = tpos; + row--; + } + break; + + case ' ': + case 13: + if (pl[pos].end == 0) { + pl[pos].open = ! pl[pos].open; + refresh = 1; + } else { + diff_window(&pl[pos], f); + refresh = 2; + } + break; + case 27: /* escape */ + case 'q': + return; + } + } +} + + +int main(int argc, char *argv[]) +{ + int n = 0; + FILE *f = NULL; + FILE *in = stdin; + struct plist *pl; + + if (argc == 3) + f = fopen(argv[argc-1], "w+"); + if (argc >=2) + in = fopen(argv[1], "r"); + else { + printf("no arg...\n"); + exit(2); + } + + pl = parse_patch(in, f, &n); + pl = sort_patches(pl, &n); + + if (f) { + fclose(in); + in = f; + } +#if 0 + int i; + for (i=0; i + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Neil Brown + * Email: + * Paper: Neil Brown + * School of Computer Science and Engineering + * The University of New South Wales + * Sydney, 2052 + * Australia + */ + +/* + * Wiggle is a tool for working with patches that don't quite apply properly. + * It provides functionality similar to 'diff' and 'merge' but can + * work at the level of individual words thus allowing the merging of + * two changes that affect the same line, but not the same parts of that line. + * + * Wiggle can also read patch and merge files. Unlike 'merge' it does not + * need to be given three separate files, but can be given a file and a patch + * and it will extract the pieces of the two two other files that it needs from + * the patch. + * + * Wiggle performs one of three core function: + * --extract -x extract part of a patch or merge file + * --diff -d report differences between two files + * --merge -m merge the changes between two files into a third file + * + * To perform these, wiggle requires 1, 2, or 3 input streams respectively. + * I can get there from individual files, from a diff (unified or context) or + * from a merge file. + * + * For merge: + * If one file is given, it is a merge file (output of 'merge'). + * If two files are given, the second is assumed to be a patch, the first is a normal file. + * If three files are given, they are taken to be normal files. + * + * For diff: + * If one file is given, it is a patch + * If two files are given, they are normal files. + * + * For extract: + * Only one file can be given. -p indicates it is a patch, otherwise it is a merge. + * One of the flags -1 -2 or -3 must also be given and they indicate which + * part of the patch or merge to extract. + * + * Difference calculate and merging is performed on lines (-l) or words (-w). + * In the case of -w, an initial diff is computed based on non-trivial words. + * i.e. spaces are ignored + * This diff is computed from the ends of the file and is used to find a suitable + * starting point and range. Then a more precise diff is computed over that + * restricted range + * + * Other options available are: + * --replace -r replace first file with result of merge. + * --help -h provide help + * --version -v version + * + * Defaults are --merge --words + * + */ + +#include "wiggle.h" +#include +#include +#include +#include +#include +#include + +void die() +{ + fprintf(stderr,"wiggle: fatal error\n"); + abort(); + exit(3); +} + +void printword(FILE *f, struct elmnt e) +{ + if (e.start[0]) + fprintf(f, "%.*s", e.len, e.start); + else { + int a,b,c; + sscanf(e.start+1, "%d %d %d", &a, &b, &c); + fprintf(f, "*** %d,%d **** %d\n", b,c,a); + } +} + +static void printsep(struct elmnt e1, struct elmnt e2) +{ + int a,b,c,d,e,f; + sscanf(e1.start+1, "%d %d %d", &a, &b, &c); + sscanf(e2.start+1, "%d %d %d", &d, &e, &f); + printf("@@ -%d,%d +%d,%d @@\n", b,c,e,f); +} + + +/* Remove any entries from the common-sublist that are + * just spaces, tabs, or newlines + */ +void cleanlist(struct file a, struct file b, struct csl *list) +{ + struct csl *new = list; + + while (list->len) { + int i; + int ap; + for( ap = list->a; ap< list->a+list->len; ap++) { + for (i=0; ia+list->len) + list++; + else + *new++ = *list++; + } + *new = *list; +} + +int main(int argc, char *argv[]) +{ + int opt; + int option_index; + int mode = 0; + int obj = 0; + int replace = 0; + char *replacename=NULL, *orignew=NULL; + int which = 0; + int ispatch = 0; + int reverse = 0; + int verbose=0, quiet=0; + int i; + int chunks1=0, chunks2=0, chunks3=0; + int exit_status = 0; + FILE *outfile = stdout; + char *helpmsg; + + struct stream f, flist[3]; + struct file fl[3]; + struct csl *csl1, *csl2; + + while ((opt = getopt_long(argc, argv, + short_options, long_options, + &option_index)) != -1) + switch(opt) { + case 'h': + helpmsg = Help; + switch(mode) { + case 'x': helpmsg = HelpExtract; break; + case 'd': helpmsg = HelpDiff; break; + case 'm': helpmsg = HelpMerge; break; + } + fputs(helpmsg, stderr); + exit(0); + + case 'V': + fputs(Version, stderr); + exit(0); + case ':': + case '?': + default: + fputs(Usage, stderr); + exit(2); + + case 'x': + case 'd': + case 'm': + if (mode ==0){ + mode = opt; + continue; + } + fprintf(stderr, "wiggle: mode is '%c' - cannot set to '%c'\n", + mode, opt); + exit(2); + + case 'w': + case 'l': + if (obj == 0 || obj == opt) { + obj = opt; + continue; + } + fprintf(stderr, "wiggle: cannot select both words and lines.\n"); + exit(2); + + case 'r': + replace = 1; + continue; + case 'R': + reverse = 1; + continue; + + case '1': + case '2': + case '3': + if (which == 0 || which == opt) { + which = opt; + continue; + } + fprintf(stderr, "wiggle: can only select one of -1, -2, -3\n"); + exit(2); + + case 'p': + ispatch = 1; + continue; + + case 'v': verbose++; continue; + case 'q': quiet=1 ; continue; + } + if (!mode) + mode = 'm'; + + if (obj && mode == 'x') { + fprintf(stderr,"wiggle: cannot specify --line or --word with --extract\n"); + exit(2); + } + if (mode != 'm' && !obj) obj = 'w'; + if (replace && mode != 'm') { + fprintf(stderr, "wiggle: --replace only allowed with --merge\n"); + exit(2); + } + if (mode == 'x' && !which) { + fprintf(stderr, "wiggle: must specify -1, -2 or -3 with --extract\n"); + exit(2); + } + if (mode != 'x' && mode != 'd' && which) { + fprintf(stderr, "wiggle: -1, -2 or -3 only allowed with --extract or --diff\n"); + exit(2); + } + if (ispatch && (mode != 'x' && mode != 'd')) { + fprintf(stderr, "wiggle: --patch only allowed with --extract or --diff\n"); + exit(2); + } + if (ispatch && which == '3') { + fprintf(stderr, "wiggle: cannot extract -3 from a patch.\n"); + exit(2); + } + + switch(mode) { + case 'x': + /* extract a branch of a diff or diff3 or merge output + * We need one file + */ + if (optind == argc) { + fprintf(stderr, "wiggle: no file given for --extract\n"); + exit(2); + } + if (optind < argc-1) { + fprintf(stderr, "wiggle: only give one file for --extract\n"); + exit(2); + } + f = load_file(argv[optind]); + if (f.body==NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind], strerror(errno)); + exit(2); + } + if (ispatch) + chunks1 = chunks2 = split_patch(f, &flist[0], &flist[1]); + else { + if (!split_merge(f, &flist[0], &flist[1], &flist[2])) { + fprintf(stderr, "wiggle: merge file %s looks bad.\n", + argv[optind]); + exit(2); + } + } + if (flist[which-'1'].body == NULL) { + fprintf(stderr, "wiggle: %s has no -%c component.\n", + argv[optind], which); + exit(2); + } else { + write(1, flist[which-'1'].body, flist[which-'1'].len); + } + + break; + case 'd': + /* create a diff (line or char) of two streams */ + switch (argc-optind) { + case 0: + fprintf(stderr, "wiggle: no file given for --diff\n"); + exit(2); + case 1: + f = load_file(argv[optind]); + if (f.body == NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind], strerror(errno)); + exit(2); + } + chunks1 = chunks2 = split_patch(f, &flist[0], &flist[1]); + if (!flist[0].body || !flist[1].body) { + fprintf(stderr, "wiggle: couldn't parse patch %s\n", + argv[optind]); + exit(2); + } + break; + case 2: + flist[0] = load_file(argv[optind]); + if (flist[0].body == NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind], strerror(errno)); + exit(2); + } + if (ispatch) { + f = load_file(argv[optind+1]); + if (f.body == NULL) { + fprintf(stderr, "wiggle: cannot load patch '%s' - %s\n", + argv[optind], strerror(errno)); + exit(2); + } + if (which == '2') + chunks2 = chunks3 = split_patch(f, &flist[2], &flist[1]); + else + chunks2 = chunks3 = split_patch(f, &flist[1], &flist[2]); + + } else + flist[1] = load_file(argv[optind+1]); + if (flist[1].body == NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind+1], strerror(errno)); + exit(2); + } + break; + default: + fprintf(stderr, "wiggle: too many files given for --diff\n"); + exit(2); + } + if (reverse) { + f=flist[1]; + flist[1] = flist[2]; + flist[2]= f; + } + if (obj == 'l') { + int a,b; + fl[0] = split_stream(flist[0], ByLine, 0); + fl[1] = split_stream(flist[1], ByLine, 0); + if (chunks2 && ! chunks1) + csl1 = pdiff(fl[0], fl[1], chunks2); + else + csl1 = diff(fl[0], fl[1]); + + if (!chunks1) + printf("@@ -1,%d +1,%d @@\n", fl[0].elcnt, fl[1].elcnt); + a = b = 0; + while (aa) { + if (fl[0].list[a].start[0]) { + printf("-"); + printword(stdout, fl[0].list[a]); + } + a++; + exit_status++; + } else if (b < csl1->b) { + if (fl[1].list[b].start[0]) { + printf("+"); + printword(stdout, fl[1].list[b]); + } + b++; + exit_status++; + } else { + if (fl[0].list[a].start[0] == '\0') + printsep(fl[0].list[a], fl[1].list[b]); + else { + printf(" "); + printword(stdout, fl[0].list[a]); + } + a++; + b++; + if (a >= csl1->a+csl1->len) + csl1++; + } + } + } else { + int a,b; + int sol = 1; /* start of line */ + fl[0] = split_stream(flist[0], ByWord, 0); + fl[1] = split_stream(flist[1], ByWord, 0); + if (chunks2 && !chunks1) + csl1 = pdiff(fl[0], fl[1], chunks2); + else + csl1 = diff(fl[0], fl[1]); + + if (!chunks1) { + /* count lines in each file */ + int l1, l2, i; + l1=l2=0; + for (i=0;ia) { + exit_status++; + if (sol) { + int a1; + /* If we remove a whole line, output +line + * else clear sol and retry */ + sol = 0; + for (a1=a; a1a;a1++) + if (ends_line(fl[0].list[a1])) { + sol=1; + break; + } + if (sol) { + printf("-"); + for (; aa; a++) { + printword(stdout, fl[0].list[a]); + if (ends_line(fl[0].list[a])) { + a++; + break; + } + } + } else printf("|"); + } + if (!sol) { + printf("<<<--"); + do { + if (sol) printf("|"); + printword(stdout, fl[0].list[a]); + sol = ends_line(fl[0].list[a]); + a++; + } while (a < csl1->a); + printf("%s-->>>", sol?"|":""); + sol=0; + } + } else if (b < csl1->b) { + exit_status++; + if (sol) { + int b1; + sol = 0; + for (b1=b; b1b;b1++) + if(ends_line(fl[1].list[b1])) { + sol=1; + break; + } + if (sol) { + printf("+"); + for(; bb ; b++) { + printword(stdout, fl[1].list[b]); + if(ends_line(fl[1].list[b])) { + b++; + break; + } + } + } else printf("|"); + } + if (!sol) { + printf("<<<++"); + do { + if (sol) printf("|"); + printword(stdout, fl[1].list[b]); + sol = ends_line(fl[1].list[b]); + b++; + } while (b < csl1->b); + printf("%s++>>>",sol?"|":""); + sol=0; + } + } else { + if (sol) { + int a1; + sol = 0; + for (a1=a; a1a+csl1->len; a1++) + if (ends_line(fl[0].list[a1])) + sol=1; + if (sol) { + if (fl[0].list[a].start[0]) { + printf(" "); + for(; aa+csl1->len; a++,b++) { + printword(stdout, fl[0].list[a]); + if (ends_line(fl[0].list[a])) { + a++,b++; + break; + } + } + } else { + printsep(fl[0].list[a], fl[1].list[b]); + a++; b++; + } + } + else printf("|"); + } + if (!sol) { + printword(stdout, fl[0].list[a]); + if (ends_line(fl[0].list[a])) + sol=1; + a++; + b++; + } + if (a >= csl1->a+csl1->len) + csl1++; + } + } + + } + break; + case 'm': + /* merge three files, A B C, so changed between B and C get made to A + */ + switch (argc-optind) { + case 0: + fprintf(stderr, "wiggle: no files given for --merge\n"); + exit(2); + case 3: + case 2: + case 1: + for (i=0; i< argc-optind; i++) { + flist[i] = load_file(argv[optind+i]); + if (flist[i].body == NULL) { + fprintf(stderr, "wiggle: cannot load file '%s' - %s\n", + argv[optind+i], strerror(errno)); + exit(2); + } + } + break; + default: + fprintf(stderr, "wiggle: too many files given for --merge\n"); + exit(2); + } + switch(argc-optind) { + case 1: /* a merge file */ + f = flist[0]; + if (!split_merge(f, &flist[0], &flist[1], &flist[2])) { + fprintf(stderr,"wiggle: merge file %s looks bad.\n", + argv[optind]); + exit(2); + } + break; + case 2: /* a file and a patch */ + f = flist[1]; + chunks2 = chunks3 = split_patch(f, &flist[1], &flist[2]); + break; + case 3: /* three separate files */ + break; + } + if (reverse) { + f=flist[1]; + flist[1] = flist[2]; + flist[2]= f; + } + + for (i=0; i<3; i++) { + if (flist[i].body==NULL) { + fprintf(stderr, "wiggle: file %d missing\n", i); + exit(2); + } + } + if (replace) { + int fd; + replacename = malloc(strlen(argv[optind])+ 20); + if (!replacename) die(); + orignew = malloc(strlen(argv[optind])+20); + if (!orignew) die(); + strcpy(replacename, argv[optind]); + strcpy(orignew, argv[optind]); + strcat(orignew, ".porig"); + if (open(orignew, O_RDONLY) >= 0 || + errno != ENOENT) { + fprintf(stderr,"wiggle: %s already exists\n", + orignew); + exit(2); + } + strcat(replacename,"XXXXXX"); + fd = mkstemp(replacename); + if (fd == -1) { + fprintf(stderr,"wiggle: could not create temporary file for %s\n", + replacename); + exit(2); + } + outfile = fdopen(fd, "w"); + + } + + if (obj == 'l') { + fl[0] = split_stream(flist[0], ByLine, 0); + fl[1] = split_stream(flist[1], ByLine, 0); + fl[2] = split_stream(flist[2], ByLine, 0); + } else { + fl[0] = split_stream(flist[0], ByWord, 0); + fl[1] = split_stream(flist[1], ByWord, 0); + fl[2] = split_stream(flist[2], ByWord, 0); + } + if (chunks2 && !chunks1) + csl1 = pdiff(fl[0], fl[1], chunks2); + else + csl1 = diff(fl[0], fl[1]); + csl2 = diff(fl[1], fl[2]); + +#if 0 + cleanlist(fl[0],fl[1],csl1); + cleanlist(fl[1],fl[2],csl2); +#endif + + { + struct ci ci; + + ci = print_merge(outfile, &fl[0], &fl[1], &fl[2], + csl1, csl2, obj=='w'); + if (!quiet && ci.conflicts) + fprintf(stderr, "%d unresolved conflict%s found\n", ci.conflicts, ci.conflicts==1?"":"s"); + if (!quiet && ci.ignored) + fprintf(stderr, "%d already-applied change%s ignored\n", ci.ignored, ci.ignored==1?"":"s"); + exit_status = (ci.conflicts > 0); + } + if (replace) { + fclose(outfile); + if (rename(argv[optind], orignew) ==0 && + rename(replacename, argv[optind]) ==0) + /* all ok */; + else { + fprintf(stderr, "wiggle: failed to move new file into place.\n"); + exit(2); + } + } + break; + + } + exit(exit_status); +} diff --git a/tests/contrib/mod_tbill/merge b/tests/contrib/mod_tbill/merge new file mode 100644 index 0000000..c70263e --- /dev/null +++ b/tests/contrib/mod_tbill/merge @@ -0,0 +1,36 @@ +#ifndef MOD_TBILL_H +#define MOD_TBILL_H + +class DB; +class ServerRequest; + +#include +#include +#include "functionHash.h" + +typedef struct _tbill_state +{ + /* settable via conf file */ + char *logon; + char *template_dir; + char *graph_dir; + char *pdf_dir; + char *http_proxy_server_addr; //for reverse lookups + int http_proxy_server_port; //for reverse lookups + int auth_d_reload_interval; + char *debuglvl_path; + + /* internal state */ + DB *db; + FunctionHash * fh; + int available; + String *linkPathPrefix; + int auth_d_pipe_fd; + int auth_d_fd; + int production_mode; +} tbill_state; + +void generatePage(tbill_state *, ServerRequest *); + +#endif /* MOD_TBILL_H */ + diff --git a/tests/contrib/mod_tbill/orig b/tests/contrib/mod_tbill/orig new file mode 100644 index 0000000..8a5f0eb --- /dev/null +++ b/tests/contrib/mod_tbill/orig @@ -0,0 +1,35 @@ +#ifndef MOD_TBILL_H +#define MOD_TBILL_H + +class DB; +class ServerRequest; + +#include +#include +#include "functionHash.h" + +typedef struct _tbill_state +{ + /* settable via conf file */ + char *logon; + char *template_dir; + char *graph_dir; + char *pdf_dir; + char *http_proxy_server_addr; //for reverse lookups + int http_proxy_server_port; //for reverse lookups + int auth_d_reload_interval; + char *debuglvl_path; + + /* internal state */ + DB *db; + FunctionHash * fh; + int available; + String *linkPathPrefix; + int auth_d_pipe_fd; + int auth_d_fd; +} tbill_state; + +void generatePage(tbill_state *, ServerRequest *); + +#endif /* MOD_TBILL_H */ + diff --git a/tests/contrib/mod_tbill/patch b/tests/contrib/mod_tbill/patch new file mode 100644 index 0000000..ec53405 --- /dev/null +++ b/tests/contrib/mod_tbill/patch @@ -0,0 +1,16 @@ +*************** +*** 22,27 **** + int auth_d_reload_interval; + int auth_d_fd; + char *debuglvl_path; + } tbill_state; + + void generatePage(tbill_state *, ServerRequest *); +--- 22,28 ---- + int auth_d_reload_interval; + int auth_d_fd; + char *debuglvl_path; ++ int production_mode; + } tbill_state; + + void generatePage(tbill_state *, ServerRequest *); diff --git a/tests/contrib/nmi.c/merge b/tests/contrib/nmi.c/merge new file mode 100644 index 0000000..928c470 --- /dev/null +++ b/tests/contrib/nmi.c/merge @@ -0,0 +1,471 @@ +/* + * linux/arch/i386/nmi.c + * + * NMI watchdog support on APIC systems + * + * Started by Ingo Molnar + * + * Fixes: + * Mikael Pettersson : AMD K7 support for local APIC NMI watchdog. + * Mikael Pettersson : Power Management for local APIC NMI watchdog. + * Mikael Pettersson : Pentium 4 support for local APIC NMI watchdog. + * Pavel Machek and + * Mikael Pettersson : PM converted to driver model. Disable/enable API. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +unsigned int nmi_watchdog = NMI_NONE; +static unsigned int nmi_hz = HZ; +unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */ +extern void show_registers(struct pt_regs *regs); + +/* nmi_active: + * +1: the lapic NMI watchdog is active, but can be disabled + * 0: the lapic NMI watchdog has not been set up, and cannot + * be enabled + * -1: the lapic NMI watchdog is disabled, but can be enabled + */ +static int nmi_active; + +#define K7_EVNTSEL_ENABLE (1 << 22) +#define K7_EVNTSEL_INT (1 << 20) +#define K7_EVNTSEL_OS (1 << 17) +#define K7_EVNTSEL_USR (1 << 16) +#define K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING 0x76 +#define K7_NMI_EVENT K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING + +#define P6_EVNTSEL0_ENABLE (1 << 22) +#define P6_EVNTSEL_INT (1 << 20) +#define P6_EVNTSEL_OS (1 << 17) +#define P6_EVNTSEL_USR (1 << 16) +#define P6_EVENT_CPU_CLOCKS_NOT_HALTED 0x79 +#define P6_NMI_EVENT P6_EVENT_CPU_CLOCKS_NOT_HALTED + +#define MSR_P4_MISC_ENABLE 0x1A0 +#define MSR_P4_MISC_ENABLE_PERF_AVAIL (1<<7) +#define MSR_P4_MISC_ENABLE_PEBS_UNAVAIL (1<<12) +#define MSR_P4_PERFCTR0 0x300 +#define MSR_P4_CCCR0 0x360 +#define P4_ESCR_EVENT_SELECT(N) ((N)<<25) +#define P4_ESCR_OS (1<<3) +#define P4_ESCR_USR (1<<2) +#define P4_CCCR_OVF_PMI (1<<26) +#define P4_CCCR_THRESHOLD(N) ((N)<<20) +#define P4_CCCR_COMPLEMENT (1<<19) +#define P4_CCCR_COMPARE (1<<18) +#define P4_CCCR_REQUIRED (3<<16) +#define P4_CCCR_ESCR_SELECT(N) ((N)<<13) +#define P4_CCCR_ENABLE (1<<12) +/* Set up IQ_COUNTER0 to behave like a clock, by having IQ_CCCR0 filter + CRU_ESCR0 (with any non-null event selector) through a complemented + max threshold. [IA32-Vol3, Section 14.9.9] */ +#define MSR_P4_IQ_COUNTER0 0x30C +#define P4_NMI_CRU_ESCR0 (P4_ESCR_EVENT_SELECT(0x3F)|P4_ESCR_OS|P4_ESCR_USR) +#define P4_NMI_IQ_CCCR0 \ + (P4_CCCR_OVF_PMI|P4_CCCR_THRESHOLD(15)|P4_CCCR_COMPLEMENT| \ + P4_CCCR_COMPARE|P4_CCCR_REQUIRED|P4_CCCR_ESCR_SELECT(4)|P4_CCCR_ENABLE) + +int __init check_nmi_watchdog (void) +{ + unsigned int prev_nmi_count[NR_CPUS]; + int cpu; + + printk(KERN_INFO "testing NMI watchdog ... "); + + for (cpu = 0; cpu < NR_CPUS; cpu++) + prev_nmi_count[cpu] = irq_stat[cpu].__nmi_count; + local_irq_enable(); + mdelay((10*1000)/nmi_hz); // wait 10 ticks + + /* FIXME: Only boot CPU is online at this stage. Check CPUs + as they come up. */ + for (cpu = 0; cpu < NR_CPUS; cpu++) { + if (!cpu_online(cpu)) + continue; + if (nmi_count(cpu) - prev_nmi_count[cpu] <= 5) { + printk("CPU#%d: NMI appears to be stuck!\n", cpu); + nmi_active = 0; + return -1; + } + } + printk("OK.\n"); + + /* now that we know it works we can reduce NMI frequency to + something more reasonable; makes a difference in some configs */ + if (nmi_watchdog == NMI_LOCAL_APIC) + nmi_hz = 1; + + return 0; +} + +static int __init setup_nmi_watchdog(char *str) +{ + int nmi; + + get_option(&str, &nmi); + + if (nmi >= NMI_INVALID) + return 0; + if (nmi == NMI_NONE) + nmi_watchdog = nmi; + /* + * If any other x86 CPU has a local APIC, then + * please test the NMI stuff there and send me the + * missing bits. Right now Intel P6/P4 and AMD K7 only. + */ + if ((nmi == NMI_LOCAL_APIC) && + (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) && + (boot_cpu_data.x86 == 6 || boot_cpu_data.x86 == 15)) + nmi_watchdog = nmi; + if ((nmi == NMI_LOCAL_APIC) && + (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && + (boot_cpu_data.x86 == 6 || boot_cpu_data.x86 == 15)) + nmi_watchdog = nmi; + /* + * We can enable the IO-APIC watchdog + * unconditionally. + */ + if (nmi == NMI_IO_APIC) { + nmi_active = 1; + nmi_watchdog = nmi; + } + return 1; +} + +__setup("nmi_watchdog=", setup_nmi_watchdog); + +void disable_lapic_nmi_watchdog(void) +{ + if (nmi_active <= 0) + return; + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + wrmsr(MSR_K7_EVNTSEL0, 0, 0); + break; + case X86_VENDOR_INTEL: + switch (boot_cpu_data.x86) { + case 6: + if (boot_cpu_data.x86_model > 0xd) + break; + + wrmsr(MSR_P6_EVNTSEL0, 0, 0); + break; + case 15: + if (boot_cpu_data.x86_model > 0x3) + break; + + wrmsr(MSR_P4_IQ_CCCR0, 0, 0); + wrmsr(MSR_P4_CRU_ESCR0, 0, 0); + break; + } + break; + } + nmi_active = -1; + /* tell do_nmi() and others that we're not active any more */ + nmi_watchdog = 0; +} + +void enable_lapic_nmi_watchdog(void) +{ + if (nmi_active < 0) { + nmi_watchdog = NMI_LOCAL_APIC; + setup_apic_nmi_watchdog(); + } +} + +void disable_timer_nmi_watchdog(void) +{ + if ((nmi_watchdog != NMI_IO_APIC) || (nmi_active <= 0)) + return; + + unset_nmi_callback(); + nmi_active = -1; + nmi_watchdog = NMI_NONE; +} + +void enable_timer_nmi_watchdog(void) +{ + if (nmi_active < 0) { + nmi_watchdog = NMI_IO_APIC; + touch_nmi_watchdog(); + nmi_active = 1; + } +} + +#ifdef CONFIG_PM + +static int nmi_pm_active; /* nmi_active before suspend */ + +static int lapic_nmi_suspend(struct sys_device *dev, u32 state) +{ + nmi_pm_active = nmi_active; + disable_lapic_nmi_watchdog(); + return 0; +} + +static int lapic_nmi_resume(struct sys_device *dev) +{ + if (nmi_pm_active > 0) + enable_lapic_nmi_watchdog(); + return 0; +} + + +static struct sysdev_class nmi_sysclass = { + set_kset_name("lapic_nmi"), + .resume = lapic_nmi_resume, + .suspend = lapic_nmi_suspend, +}; + +static struct sys_device device_lapic_nmi = { + .id = 0, + .cls = &nmi_sysclass, +}; + +static int __init init_lapic_nmi_sysfs(void) +{ + int error; + + if (nmi_active == 0) + return 0; + + error = sysdev_class_register(&nmi_sysclass); + if (!error) + error = sys_device_register(&device_lapic_nmi); + return error; +} +/* must come after the local APIC's device_initcall() */ +late_initcall(init_lapic_nmi_sysfs); + +#endif /* CONFIG_PM */ + +/* + * Activate the NMI watchdog via the local APIC. + * Original code written by Keith Owens. + */ + +static void clear_msr_range(unsigned int base, unsigned int n) +{ + unsigned int i; + + for(i = 0; i < n; ++i) + wrmsr(base+i, 0, 0); +} + +static void setup_k7_watchdog(void) +{ + unsigned int evntsel; + + nmi_perfctr_msr = MSR_K7_PERFCTR0; + + clear_msr_range(MSR_K7_EVNTSEL0, 4); + clear_msr_range(MSR_K7_PERFCTR0, 4); + + evntsel = K7_EVNTSEL_INT + | K7_EVNTSEL_OS + | K7_EVNTSEL_USR + | K7_NMI_EVENT; + + wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); + Dprintk("setting K7_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000)); + wrmsr(MSR_K7_PERFCTR0, -(cpu_khz/nmi_hz*1000), -1); + apic_write(APIC_LVTPC, APIC_DM_NMI); + evntsel |= K7_EVNTSEL_ENABLE; + wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); +} + +static void setup_p6_watchdog(void) +{ + unsigned int evntsel; + + nmi_perfctr_msr = MSR_P6_PERFCTR0; + + clear_msr_range(MSR_P6_EVNTSEL0, 2); + clear_msr_range(MSR_P6_PERFCTR0, 2); + + evntsel = P6_EVNTSEL_INT + | P6_EVNTSEL_OS + | P6_EVNTSEL_USR + | P6_NMI_EVENT; + + wrmsr(MSR_P6_EVNTSEL0, evntsel, 0); + Dprintk("setting P6_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000)); + wrmsr(MSR_P6_PERFCTR0, -(cpu_khz/nmi_hz*1000), 0); + apic_write(APIC_LVTPC, APIC_DM_NMI); + evntsel |= P6_EVNTSEL0_ENABLE; + wrmsr(MSR_P6_EVNTSEL0, evntsel, 0); +} + +static int setup_p4_watchdog(void) +{ + unsigned int misc_enable, dummy; + + rdmsr(MSR_P4_MISC_ENABLE, misc_enable, dummy); + if (!(misc_enable & MSR_P4_MISC_ENABLE_PERF_AVAIL)) + return 0; + + nmi_perfctr_msr = MSR_P4_IQ_COUNTER0; + + if (!(misc_enable & MSR_P4_MISC_ENABLE_PEBS_UNAVAIL)) + clear_msr_range(0x3F1, 2); + /* MSR 0x3F0 seems to have a default value of 0xFC00, but current + docs doesn't fully define it, so leave it alone for now. */ + clear_msr_range(0x3A0, 31); + clear_msr_range(0x3C0, 6); + clear_msr_range(0x3C8, 6); + clear_msr_range(0x3E0, 2); + clear_msr_range(MSR_P4_CCCR0, 18); + clear_msr_range(MSR_P4_PERFCTR0, 18); + + wrmsr(MSR_P4_CRU_ESCR0, P4_NMI_CRU_ESCR0, 0); + wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0 & ~P4_CCCR_ENABLE, 0); + Dprintk("setting P4_IQ_COUNTER0 to 0x%08lx\n", -(cpu_khz/nmi_hz*1000)); + wrmsr(MSR_P4_IQ_COUNTER0, -(cpu_khz/nmi_hz*1000), -1); + apic_write(APIC_LVTPC, APIC_DM_NMI); + wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0, 0); + return 1; +} + +void setup_apic_nmi_watchdog (void) +{ + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 != 6 && boot_cpu_data.x86 != 15) + return; + setup_k7_watchdog(); + break; + case X86_VENDOR_INTEL: + switch (boot_cpu_data.x86) { + case 6: + if (boot_cpu_data.x86_model > 0xd) + return; + + setup_p6_watchdog(); + break; + case 15: + if (boot_cpu_data.x86_model > 0x3) + return; + + if (!setup_p4_watchdog()) + return; + break; + default: + return; + } + break; + default: + return; + } + nmi_active = 1; +} + +static spinlock_t nmi_print_lock = SPIN_LOCK_UNLOCKED; + +/* + * the best way to detect whether a CPU has a 'hard lockup' problem + * is to check it's local APIC timer IRQ counts. If they are not + * changing then that CPU has some problem. + * + * as these watchdog NMI IRQs are generated on every CPU, we only + * have to check the current processor. + * + * since NMIs don't listen to _any_ locks, we have to be extremely + * careful not to rely on unsafe variables. The printk might lock + * up though, so we have to break up any console locks first ... + * [when there will be more tty-related locks, break them up + * here too!] + */ + +static unsigned int + last_irq_sums [NR_CPUS], + alert_counter [NR_CPUS]; + +void touch_nmi_watchdog (void) +{ + int i; + + /* + * Just reset the alert counters, (other CPUs might be + * spinning on locks we hold): + */ + for (i = 0; i < NR_CPUS; i++) + alert_counter[i] = 0; +} + +void nmi_watchdog_tick (struct pt_regs * regs) +{ + + /* + * Since current_thread_info()-> is always on the stack, and we + * always switch the stack NMI-atomically, it's safe to use + * smp_processor_id(). + */ + int sum, cpu = smp_processor_id(); + + sum = irq_stat[cpu].apic_timer_irqs; + + if (last_irq_sums[cpu] == sum) { + /* + * Ayiee, looks like this CPU is stuck ... + * wait a few IRQs (5 seconds) before doing the oops ... + */ + alert_counter[cpu]++; + if (alert_counter[cpu] == 5*nmi_hz) { + spin_lock(&nmi_print_lock); + /* + * We are in trouble anyway, lets at least try + * to get a message out. + */ + bust_spinlocks(1); + printk("NMI Watchdog detected LOCKUP on CPU%d, eip %08lx, registers:\n", cpu, regs->eip); + show_registers(regs); + dump("NMI Watchdog detected LOCKUP", regs); + printk("console shuts up ...\n"); + console_silent(); + spin_unlock(&nmi_print_lock); + bust_spinlocks(0); + do_exit(SIGSEGV); + } + } else { + last_irq_sums[cpu] = sum; + alert_counter[cpu] = 0; + } + if (nmi_perfctr_msr) { + if (nmi_perfctr_msr == MSR_P4_IQ_COUNTER0) { + /* + * P4 quirks: + * - An overflown perfctr will assert its interrupt + * until the OVF flag in its CCCR is cleared. + * - LVTPC is masked on interrupt and must be + * unmasked by the LVTPC handler. + */ + wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0, 0); + apic_write(APIC_LVTPC, APIC_DM_NMI); + } + wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1); + } +} + +EXPORT_SYMBOL(nmi_watchdog); +EXPORT_SYMBOL(disable_lapic_nmi_watchdog); +EXPORT_SYMBOL(enable_lapic_nmi_watchdog); +EXPORT_SYMBOL(disable_timer_nmi_watchdog); +EXPORT_SYMBOL(enable_timer_nmi_watchdog); diff --git a/tests/contrib/nmi.c/orig b/tests/contrib/nmi.c/orig new file mode 100644 index 0000000..b7ebd8f --- /dev/null +++ b/tests/contrib/nmi.c/orig @@ -0,0 +1,470 @@ +/* + * linux/arch/i386/nmi.c + * + * NMI watchdog support on APIC systems + * + * Started by Ingo Molnar + * + * Fixes: + * Mikael Pettersson : AMD K7 support for local APIC NMI watchdog. + * Mikael Pettersson : Power Management for local APIC NMI watchdog. + * Mikael Pettersson : Pentium 4 support for local APIC NMI watchdog. + * Pavel Machek and + * Mikael Pettersson : PM converted to driver model. Disable/enable API. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +unsigned int nmi_watchdog = NMI_NONE; +static unsigned int nmi_hz = HZ; +unsigned int nmi_perfctr_msr; /* the MSR to reset in NMI handler */ +extern void show_registers(struct pt_regs *regs); + +/* nmi_active: + * +1: the lapic NMI watchdog is active, but can be disabled + * 0: the lapic NMI watchdog has not been set up, and cannot + * be enabled + * -1: the lapic NMI watchdog is disabled, but can be enabled + */ +static int nmi_active; + +#define K7_EVNTSEL_ENABLE (1 << 22) +#define K7_EVNTSEL_INT (1 << 20) +#define K7_EVNTSEL_OS (1 << 17) +#define K7_EVNTSEL_USR (1 << 16) +#define K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING 0x76 +#define K7_NMI_EVENT K7_EVENT_CYCLES_PROCESSOR_IS_RUNNING + +#define P6_EVNTSEL0_ENABLE (1 << 22) +#define P6_EVNTSEL_INT (1 << 20) +#define P6_EVNTSEL_OS (1 << 17) +#define P6_EVNTSEL_USR (1 << 16) +#define P6_EVENT_CPU_CLOCKS_NOT_HALTED 0x79 +#define P6_NMI_EVENT P6_EVENT_CPU_CLOCKS_NOT_HALTED + +#define MSR_P4_MISC_ENABLE 0x1A0 +#define MSR_P4_MISC_ENABLE_PERF_AVAIL (1<<7) +#define MSR_P4_MISC_ENABLE_PEBS_UNAVAIL (1<<12) +#define MSR_P4_PERFCTR0 0x300 +#define MSR_P4_CCCR0 0x360 +#define P4_ESCR_EVENT_SELECT(N) ((N)<<25) +#define P4_ESCR_OS (1<<3) +#define P4_ESCR_USR (1<<2) +#define P4_CCCR_OVF_PMI (1<<26) +#define P4_CCCR_THRESHOLD(N) ((N)<<20) +#define P4_CCCR_COMPLEMENT (1<<19) +#define P4_CCCR_COMPARE (1<<18) +#define P4_CCCR_REQUIRED (3<<16) +#define P4_CCCR_ESCR_SELECT(N) ((N)<<13) +#define P4_CCCR_ENABLE (1<<12) +/* Set up IQ_COUNTER0 to behave like a clock, by having IQ_CCCR0 filter + CRU_ESCR0 (with any non-null event selector) through a complemented + max threshold. [IA32-Vol3, Section 14.9.9] */ +#define MSR_P4_IQ_COUNTER0 0x30C +#define P4_NMI_CRU_ESCR0 (P4_ESCR_EVENT_SELECT(0x3F)|P4_ESCR_OS|P4_ESCR_USR) +#define P4_NMI_IQ_CCCR0 \ + (P4_CCCR_OVF_PMI|P4_CCCR_THRESHOLD(15)|P4_CCCR_COMPLEMENT| \ + P4_CCCR_COMPARE|P4_CCCR_REQUIRED|P4_CCCR_ESCR_SELECT(4)|P4_CCCR_ENABLE) + +int __init check_nmi_watchdog (void) +{ + unsigned int prev_nmi_count[NR_CPUS]; + int cpu; + + printk(KERN_INFO "testing NMI watchdog ... "); + + for (cpu = 0; cpu < NR_CPUS; cpu++) + prev_nmi_count[cpu] = irq_stat[cpu].__nmi_count; + local_irq_enable(); + mdelay((10*1000)/nmi_hz); // wait 10 ticks + + /* FIXME: Only boot CPU is online at this stage. Check CPUs + as they come up. */ + for (cpu = 0; cpu < NR_CPUS; cpu++) { + if (!cpu_online(cpu)) + continue; + if (nmi_count(cpu) - prev_nmi_count[cpu] <= 5) { + printk("CPU#%d: NMI appears to be stuck!\n", cpu); + nmi_active = 0; + return -1; + } + } + printk("OK.\n"); + + /* now that we know it works we can reduce NMI frequency to + something more reasonable; makes a difference in some configs */ + if (nmi_watchdog == NMI_LOCAL_APIC) + nmi_hz = 1; + + return 0; +} + +static int __init setup_nmi_watchdog(char *str) +{ + int nmi; + + get_option(&str, &nmi); + + if (nmi >= NMI_INVALID) + return 0; + if (nmi == NMI_NONE) + nmi_watchdog = nmi; + /* + * If any other x86 CPU has a local APIC, then + * please test the NMI stuff there and send me the + * missing bits. Right now Intel P6/P4 and AMD K7 only. + */ + if ((nmi == NMI_LOCAL_APIC) && + (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) && + (boot_cpu_data.x86 == 6 || boot_cpu_data.x86 == 15)) + nmi_watchdog = nmi; + if ((nmi == NMI_LOCAL_APIC) && + (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && + (boot_cpu_data.x86 == 6 || boot_cpu_data.x86 == 15)) + nmi_watchdog = nmi; + /* + * We can enable the IO-APIC watchdog + * unconditionally. + */ + if (nmi == NMI_IO_APIC) { + nmi_active = 1; + nmi_watchdog = nmi; + } + return 1; +} + +__setup("nmi_watchdog=", setup_nmi_watchdog); + +void disable_lapic_nmi_watchdog(void) +{ + if (nmi_active <= 0) + return; + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + wrmsr(MSR_K7_EVNTSEL0, 0, 0); + break; + case X86_VENDOR_INTEL: + switch (boot_cpu_data.x86) { + case 6: + if (boot_cpu_data.x86_model > 0xd) + break; + + wrmsr(MSR_P6_EVNTSEL0, 0, 0); + break; + case 15: + if (boot_cpu_data.x86_model > 0x3) + break; + + wrmsr(MSR_P4_IQ_CCCR0, 0, 0); + wrmsr(MSR_P4_CRU_ESCR0, 0, 0); + break; + } + break; + } + nmi_active = -1; + /* tell do_nmi() and others that we're not active any more */ + nmi_watchdog = 0; +} + +void enable_lapic_nmi_watchdog(void) +{ + if (nmi_active < 0) { + nmi_watchdog = NMI_LOCAL_APIC; + setup_apic_nmi_watchdog(); + } +} + +void disable_timer_nmi_watchdog(void) +{ + if ((nmi_watchdog != NMI_IO_APIC) || (nmi_active <= 0)) + return; + + unset_nmi_callback(); + nmi_active = -1; + nmi_watchdog = NMI_NONE; +} + +void enable_timer_nmi_watchdog(void) +{ + if (nmi_active < 0) { + nmi_watchdog = NMI_IO_APIC; + touch_nmi_watchdog(); + nmi_active = 1; + } +} + +#ifdef CONFIG_PM + +static int nmi_pm_active; /* nmi_active before suspend */ + +static int lapic_nmi_suspend(struct sys_device *dev, u32 state) +{ + nmi_pm_active = nmi_active; + disable_lapic_nmi_watchdog(); + return 0; +} + +static int lapic_nmi_resume(struct sys_device *dev) +{ + if (nmi_pm_active > 0) + enable_lapic_nmi_watchdog(); + return 0; +} + + +static struct sysdev_class nmi_sysclass = { + set_kset_name("lapic_nmi"), + .resume = lapic_nmi_resume, + .suspend = lapic_nmi_suspend, +}; + +static struct sys_device device_lapic_nmi = { + .id = 0, + .cls = &nmi_sysclass, +}; + +static int __init init_lapic_nmi_sysfs(void) +{ + int error; + + if (nmi_active == 0) + return 0; + + error = sysdev_class_register(&nmi_sysclass); + if (!error) + error = sys_device_register(&device_lapic_nmi); + return error; +} +/* must come after the local APIC's device_initcall() */ +late_initcall(init_lapic_nmi_sysfs); + +#endif /* CONFIG_PM */ + +/* + * Activate the NMI watchdog via the local APIC. + * Original code written by Keith Owens. + */ + +static void clear_msr_range(unsigned int base, unsigned int n) +{ + unsigned int i; + + for(i = 0; i < n; ++i) + wrmsr(base+i, 0, 0); +} + +static void setup_k7_watchdog(void) +{ + unsigned int evntsel; + + nmi_perfctr_msr = MSR_K7_PERFCTR0; + + clear_msr_range(MSR_K7_EVNTSEL0, 4); + clear_msr_range(MSR_K7_PERFCTR0, 4); + + evntsel = K7_EVNTSEL_INT + | K7_EVNTSEL_OS + | K7_EVNTSEL_USR + | K7_NMI_EVENT; + + wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); + Dprintk("setting K7_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000)); + wrmsr(MSR_K7_PERFCTR0, -(cpu_khz/nmi_hz*1000), -1); + apic_write(APIC_LVTPC, APIC_DM_NMI); + evntsel |= K7_EVNTSEL_ENABLE; + wrmsr(MSR_K7_EVNTSEL0, evntsel, 0); +} + +static void setup_p6_watchdog(void) +{ + unsigned int evntsel; + + nmi_perfctr_msr = MSR_P6_PERFCTR0; + + clear_msr_range(MSR_P6_EVNTSEL0, 2); + clear_msr_range(MSR_P6_PERFCTR0, 2); + + evntsel = P6_EVNTSEL_INT + | P6_EVNTSEL_OS + | P6_EVNTSEL_USR + | P6_NMI_EVENT; + + wrmsr(MSR_P6_EVNTSEL0, evntsel, 0); + Dprintk("setting P6_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000)); + wrmsr(MSR_P6_PERFCTR0, -(cpu_khz/nmi_hz*1000), 0); + apic_write(APIC_LVTPC, APIC_DM_NMI); + evntsel |= P6_EVNTSEL0_ENABLE; + wrmsr(MSR_P6_EVNTSEL0, evntsel, 0); +} + +static int setup_p4_watchdog(void) +{ + unsigned int misc_enable, dummy; + + rdmsr(MSR_P4_MISC_ENABLE, misc_enable, dummy); + if (!(misc_enable & MSR_P4_MISC_ENABLE_PERF_AVAIL)) + return 0; + + nmi_perfctr_msr = MSR_P4_IQ_COUNTER0; + + if (!(misc_enable & MSR_P4_MISC_ENABLE_PEBS_UNAVAIL)) + clear_msr_range(0x3F1, 2); + /* MSR 0x3F0 seems to have a default value of 0xFC00, but current + docs doesn't fully define it, so leave it alone for now. */ + clear_msr_range(0x3A0, 31); + clear_msr_range(0x3C0, 6); + clear_msr_range(0x3C8, 6); + clear_msr_range(0x3E0, 2); + clear_msr_range(MSR_P4_CCCR0, 18); + clear_msr_range(MSR_P4_PERFCTR0, 18); + + wrmsr(MSR_P4_CRU_ESCR0, P4_NMI_CRU_ESCR0, 0); + wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0 & ~P4_CCCR_ENABLE, 0); + Dprintk("setting P4_IQ_COUNTER0 to 0x%08lx\n", -(cpu_khz/nmi_hz*1000)); + wrmsr(MSR_P4_IQ_COUNTER0, -(cpu_khz/nmi_hz*1000), -1); + apic_write(APIC_LVTPC, APIC_DM_NMI); + wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0, 0); + return 1; +} + +void setup_apic_nmi_watchdog (void) +{ + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if (boot_cpu_data.x86 != 6 && boot_cpu_data.x86 != 15) + return; + setup_k7_watchdog(); + break; + case X86_VENDOR_INTEL: + switch (boot_cpu_data.x86) { + case 6: + if (boot_cpu_data.x86_model > 0xd) + return; + + setup_p6_watchdog(); + break; + case 15: + if (boot_cpu_data.x86_model > 0x3) + return; + + if (!setup_p4_watchdog()) + return; + break; + default: + return; + } + break; + default: + return; + } + nmi_active = 1; +} + +static spinlock_t nmi_print_lock = SPIN_LOCK_UNLOCKED; + +/* + * the best way to detect whether a CPU has a 'hard lockup' problem + * is to check it's local APIC timer IRQ counts. If they are not + * changing then that CPU has some problem. + * + * as these watchdog NMI IRQs are generated on every CPU, we only + * have to check the current processor. + * + * since NMIs don't listen to _any_ locks, we have to be extremely + * careful not to rely on unsafe variables. The printk might lock + * up though, so we have to break up any console locks first ... + * [when there will be more tty-related locks, break them up + * here too!] + */ + +static unsigned int + last_irq_sums [NR_CPUS], + alert_counter [NR_CPUS]; + +void touch_nmi_watchdog (void) +{ + int i; + + /* + * Just reset the alert counters, (other CPUs might be + * spinning on locks we hold): + */ + for (i = 0; i < NR_CPUS; i++) + alert_counter[i] = 0; +} + +void nmi_watchdog_tick (struct pt_regs * regs) +{ + + /* + * Since current_thread_info()-> is always on the stack, and we + * always switch the stack NMI-atomically, it's safe to use + * smp_processor_id(). + */ + int sum, cpu = smp_processor_id(); + + sum = irq_stat[cpu].apic_timer_irqs; + + if (last_irq_sums[cpu] == sum) { + /* + * Ayiee, looks like this CPU is stuck ... + * wait a few IRQs (5 seconds) before doing the oops ... + */ + alert_counter[cpu]++; + if (alert_counter[cpu] == 5*nmi_hz) { + spin_lock(&nmi_print_lock); + /* + * We are in trouble anyway, lets at least try + * to get a message out. + */ + bust_spinlocks(1); + printk("NMI Watchdog detected LOCKUP on CPU%d, eip %08lx, registers:\n", cpu, regs->eip); + show_registers(regs); + dump("NMI Watchdog detected LOCKUP", regs); + printk("console shuts up ...\n"); + console_silent(); + spin_unlock(&nmi_print_lock); + bust_spinlocks(0); + do_exit(SIGSEGV); + } + } else { + last_irq_sums[cpu] = sum; + alert_counter[cpu] = 0; + } + if (nmi_perfctr_msr) { + if (nmi_perfctr_msr == MSR_P4_IQ_COUNTER0) { + /* + * P4 quirks: + * - An overflown perfctr will assert its interrupt + * until the OVF flag in its CCCR is cleared. + * - LVTPC is masked on interrupt and must be + * unmasked by the LVTPC handler. + */ + wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0, 0); + apic_write(APIC_LVTPC, APIC_DM_NMI); + } + wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1); + } +} + +EXPORT_SYMBOL(nmi_watchdog); +EXPORT_SYMBOL(disable_lapic_nmi_watchdog); +EXPORT_SYMBOL(enable_lapic_nmi_watchdog); +EXPORT_SYMBOL(disable_timer_nmi_watchdog); +EXPORT_SYMBOL(enable_timer_nmi_watchdog); diff --git a/tests/contrib/nmi.c/patch b/tests/contrib/nmi.c/patch new file mode 100644 index 0000000..225b6df --- /dev/null +++ b/tests/contrib/nmi.c/patch @@ -0,0 +1,8 @@ +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + + #include + #include diff --git a/tests/contrib/pfkey_v2_parse.c/merge b/tests/contrib/pfkey_v2_parse.c/merge new file mode 100644 index 0000000..b943e7c --- /dev/null +++ b/tests/contrib/pfkey_v2_parse.c/merge @@ -0,0 +1,1789 @@ +/* + * RFC2367 PF_KEYv2 Key management API message parser + * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * RCSID $Id: pfkey_v2_parse.c,v 1.53 2003/01/30 02:32:09 rgb Exp $ + */ + +/* + * Template from klips/net/ipsec/ipsec/ipsec_parser.c. + */ + +char pfkey_v2_parse_c_version[] = "$Id: pfkey_v2_parse.c,v 1.53 2003/01/30 02:32:09 rgb Exp $"; + +/* + * Some ugly stuff to allow consistent debugging code for use in the + * kernel and in user space +*/ + +#ifdef __KERNEL__ + +# include /* for printk */ + +#include "freeswan/ipsec_kversion.h" /* for malloc switch */ + +# ifdef MALLOC_SLAB +# include /* kmalloc() */ +# else /* MALLOC_SLAB */ +# include /* kmalloc() */ +# endif /* MALLOC_SLAB */ +# include /* error codes */ +# include /* size_t */ +# include /* mark_bh */ + +# include /* struct device, and other headers */ +# include /* eth_type_trans */ +# include /* struct iphdr */ +# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +# include /* struct ipv6hdr */ +# endif /* if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ +extern int debug_pfkey; + +# include + +#include "freeswan/ipsec_encap.h" + +#else /* __KERNEL__ */ + +# include +# include +# include + +# include +# include "programs/pluto/constants.h" +# include "programs/pluto/defs.h" /* for PRINTF_LIKE */ +# include "programs/pluto/log.h" /* for debugging and DBG_log */ + +/* #define PLUTO */ + +# ifdef PLUTO +# define DEBUGGING(level, args...) { DBG_log("pfkey_lib_debug:" args); } +# else +# define DEBUGGING(level, args...) if(pfkey_lib_debug & level) { printf("pfkey_lib_debug:" args); } else { ; } +# endif + +#endif /* __KERNEL__ */ + + +#include +#include + +#ifdef __KERNEL__ +# include "freeswan/ipsec_netlink.h" /* KLIPS_PRINT */ +extern int sysctl_ipsec_debug_verbose; +# define DEBUGGING(level, args...) \ + KLIPS_PRINT( \ + ((debug_pfkey & level & (PF_KEY_DEBUG_PARSE_STRUCT | PF_KEY_DEBUG_PARSE_PROBLEM)) \ + || (sysctl_ipsec_debug_verbose && (debug_pfkey & level & PF_KEY_DEBUG_PARSE_FLOW))) \ + , "klips_debug:" args) +#endif /* __KERNEL__ */ +#include "freeswan/ipsec_sa.h" /* IPSEC_SAREF_NULL, IPSEC_SA_REF_TABLE_IDX_WIDTH */ + + +#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) + +struct satype_tbl { + uint8_t proto; + uint8_t satype; + char* name; +} static satype_tbl[] = { +#ifdef __KERNEL__ + { IPPROTO_ESP, SADB_SATYPE_ESP, "ESP" }, + { IPPROTO_AH, SADB_SATYPE_AH, "AH" }, + { IPPROTO_IPIP, SADB_X_SATYPE_IPIP, "IPIP" }, +#ifdef CONFIG_IPSEC_IPCOMP + { IPPROTO_COMP, SADB_X_SATYPE_COMP, "COMP" }, +#endif /* CONFIG_IPSEC_IPCOMP */ + { IPPROTO_INT, SADB_X_SATYPE_INT, "INT" }, +#else /* __KERNEL__ */ + { SA_ESP, SADB_SATYPE_ESP, "ESP" }, + { SA_AH, SADB_SATYPE_AH, "AH" }, + { SA_IPIP, SADB_X_SATYPE_IPIP, "IPIP" }, + { SA_COMP, SADB_X_SATYPE_COMP, "COMP" }, + { SA_INT, SADB_X_SATYPE_INT, "INT" }, +#endif /* __KERNEL__ */ + { 0, 0, "UNKNOWN" } +}; + +uint8_t +satype2proto(uint8_t satype) +{ + int i =0; + + while(satype_tbl[i].satype != satype && satype_tbl[i].satype != 0) { + i++; + } + return satype_tbl[i].proto; +} + +uint8_t +proto2satype(uint8_t proto) +{ + int i = 0; + + while(satype_tbl[i].proto != proto && satype_tbl[i].proto != 0) { + i++; + } + return satype_tbl[i].satype; +} + +char* +satype2name(uint8_t satype) +{ + int i = 0; + + while(satype_tbl[i].satype != satype && satype_tbl[i].satype != 0) { + i++; + } + return satype_tbl[i].name; +} + +char* +proto2name(uint8_t proto) +{ + int i = 0; + + while(satype_tbl[i].proto != proto && satype_tbl[i].proto != 0) { + i++; + } + return satype_tbl[i].name; +} + +/* Default extension parsers taken from the KLIPS code */ + +DEBUG_NO_STATIC int +pfkey_sa_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_sa *pfkey_sa = (struct sadb_sa *)pfkey_ext; +#if 0 + struct sadb_sa sav2; +#endif + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_sa_parse: entry\n"); + /* sanity checks... */ + if(!pfkey_sa) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "NULL pointer passed in.\n"); + SENDERR(EINVAL); + } + +#if 0 + /* check if this structure is short, and if so, fix it up. + * XXX this is NOT the way to do things. + */ + if(pfkey_sa->sadb_sa_len == sizeof(struct sadb_sa_v1)/IPSEC_PFKEYv2_ALIGN) { + + /* yes, so clear out a temporary structure, and copy first */ + memset(&sav2, 0, sizeof(sav2)); + memcpy(&sav2, pfkey_sa, sizeof(struct sadb_sa_v1)); + sav2.sadb_x_sa_ref=-1; + sav2.sadb_sa_len = sizeof(struct sadb_sa) / IPSEC_PFKEYv2_ALIGN; + + pfkey_sa = &sav2; + } +#endif + + + if(pfkey_sa->sadb_sa_len != sizeof(struct sadb_sa) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "length wrong pfkey_sa->sadb_sa_len=%d sizeof(struct sadb_sa)=%d.\n", + pfkey_sa->sadb_sa_len, + (int)sizeof(struct sadb_sa)); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_encrypt > SADB_EALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "pfkey_sa->sadb_sa_encrypt=%d > SADB_EALG_MAX=%d.\n", + pfkey_sa->sadb_sa_encrypt, + SADB_EALG_MAX); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_auth > SADB_AALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "pfkey_sa->sadb_sa_auth=%d > SADB_AALG_MAX=%d.\n", + pfkey_sa->sadb_sa_auth, + SADB_AALG_MAX); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_state > SADB_SASTATE_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "state=%d exceeds MAX=%d.\n", + pfkey_sa->sadb_sa_state, + SADB_SASTATE_MAX); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_state == SADB_SASTATE_DEAD) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "state=%d is DEAD=%d.\n", + pfkey_sa->sadb_sa_state, + SADB_SASTATE_DEAD); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_replay > 64) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "replay window size: %d -- must be 0 <= size <= 64\n", + pfkey_sa->sadb_sa_replay); + SENDERR(EINVAL); + } + + if(! ((pfkey_sa->sadb_sa_exttype == SADB_EXT_SA) || + (pfkey_sa->sadb_sa_exttype == SADB_X_EXT_SA2))) + { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "unknown exttype=%d, expecting SADB_EXT_SA=%d or SADB_X_EXT_SA2=%d.\n", + pfkey_sa->sadb_sa_exttype, + SADB_EXT_SA, + SADB_X_EXT_SA2); + SENDERR(EINVAL); + } + + if((IPSEC_SAREF_NULL != pfkey_sa->sadb_x_sa_ref) && (pfkey_sa->sadb_x_sa_ref >= (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "SAref=%d must be (SAref == IPSEC_SAREF_NULL(%d) || SAref < IPSEC_SA_REF_TABLE_NUM_ENTRIES(%d)).\n", + pfkey_sa->sadb_x_sa_ref, + IPSEC_SAREF_NULL, + IPSEC_SA_REF_TABLE_NUM_ENTRIES); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_sa_parse: " + "successfully found len=%d exttype=%d(%s) spi=%08lx replay=%d state=%d auth=%d encrypt=%d flags=%d ref=%d.\n", + pfkey_sa->sadb_sa_len, + pfkey_sa->sadb_sa_exttype, + pfkey_v2_sadb_ext_string(pfkey_sa->sadb_sa_exttype), + (long unsigned int)ntohl(pfkey_sa->sadb_sa_spi), + pfkey_sa->sadb_sa_replay, + pfkey_sa->sadb_sa_state, + pfkey_sa->sadb_sa_auth, + pfkey_sa->sadb_sa_encrypt, + pfkey_sa->sadb_sa_flags, + pfkey_sa->sadb_x_sa_ref); + + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_lifetime_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)pfkey_ext; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_lifetime_parse:enter\n"); + /* sanity checks... */ + if(!pfkey_lifetime) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_lifetime_parse: " + "NULL pointer passed in.\n"); + SENDERR(EINVAL); + } + + if(pfkey_lifetime->sadb_lifetime_len != + sizeof(struct sadb_lifetime) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_lifetime_parse: " + "length wrong pfkey_lifetime->sadb_lifetime_len=%d sizeof(struct sadb_lifetime)=%d.\n", + pfkey_lifetime->sadb_lifetime_len, + (int)sizeof(struct sadb_lifetime)); + SENDERR(EINVAL); + } + + if((pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_HARD) && + (pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_SOFT) && + (pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_CURRENT)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_lifetime_parse: " + "unexpected ext_type=%d.\n", + pfkey_lifetime->sadb_lifetime_exttype); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_lifetime_parse: " + "life_type=%d(%s) alloc=%u bytes=%u add=%u use=%u pkts=%u.\n", + pfkey_lifetime->sadb_lifetime_exttype, + pfkey_v2_sadb_ext_string(pfkey_lifetime->sadb_lifetime_exttype), + pfkey_lifetime->sadb_lifetime_allocations, + (unsigned)pfkey_lifetime->sadb_lifetime_bytes, + (unsigned)pfkey_lifetime->sadb_lifetime_addtime, + (unsigned)pfkey_lifetime->sadb_lifetime_usetime, + pfkey_lifetime->sadb_x_lifetime_packets); +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_address_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + int saddr_len = 0; + struct sadb_address *pfkey_address = (struct sadb_address *)pfkey_ext; + struct sockaddr* s = (struct sockaddr*)((char*)pfkey_address + sizeof(*pfkey_address)); + char ipaddr_txt[ADDRTOT_BUF]; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_address_parse:enter\n"); + /* sanity checks... */ + if(!pfkey_address) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "NULL pointer passed in.\n"); + SENDERR(EINVAL); + } + + if(pfkey_address->sadb_address_len < + (sizeof(struct sadb_address) + sizeof(struct sockaddr))/ + IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "size wrong 1 ext_len=%d, adr_ext_len=%d, saddr_len=%d.\n", + pfkey_address->sadb_address_len, + (int)sizeof(struct sadb_address), + (int)sizeof(struct sockaddr)); + SENDERR(EINVAL); + } + + if(pfkey_address->sadb_address_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "res=%d, must be zero.\n", + pfkey_address->sadb_address_reserved); + SENDERR(EINVAL); + } + + switch(pfkey_address->sadb_address_exttype) { + case SADB_EXT_ADDRESS_SRC: + case SADB_EXT_ADDRESS_DST: + case SADB_EXT_ADDRESS_PROXY: + case SADB_X_EXT_ADDRESS_DST2: + case SADB_X_EXT_ADDRESS_SRC_FLOW: + case SADB_X_EXT_ADDRESS_DST_FLOW: + case SADB_X_EXT_ADDRESS_SRC_MASK: + case SADB_X_EXT_ADDRESS_DST_MASK: +#ifdef NAT_TRAVERSAL + case SADB_X_EXT_NAT_T_OA: +#endif + break; + default: + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "unexpected ext_type=%d.\n", + pfkey_address->sadb_address_exttype); + SENDERR(EINVAL); + } + + switch(s->sa_family) { + case AF_INET: + saddr_len = sizeof(struct sockaddr_in); + sprintf(ipaddr_txt, "%d.%d.%d.%d" + , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 0) & 0xFF + , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 8) & 0xFF + , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 16) & 0xFF + , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 24) & 0xFF); + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_address_parse: " + "found exttype=%u(%s) family=%d(AF_INET) address=%s proto=%u port=%u.\n", + pfkey_address->sadb_address_exttype, + pfkey_v2_sadb_ext_string(pfkey_address->sadb_address_exttype), + s->sa_family, + ipaddr_txt, + pfkey_address->sadb_address_proto, + ((struct sockaddr_in*)s)->sin_port); + break; + case AF_INET6: + saddr_len = sizeof(struct sockaddr_in6); + sprintf(ipaddr_txt, "%x:%x:%x:%x:%x:%x:%x:%x" + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[0]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[1]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[2]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[3]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[4]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[5]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[6]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[7])); + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_address_parse: " + "found exttype=%u(%s) family=%d(AF_INET6) address=%s proto=%u port=%u.\n", + pfkey_address->sadb_address_exttype, + pfkey_v2_sadb_ext_string(pfkey_address->sadb_address_exttype), + s->sa_family, + ipaddr_txt, + pfkey_address->sadb_address_proto, + ((struct sockaddr_in6*)s)->sin6_port); + break; + default: + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "s->sa_family=%d not supported.\n", + s->sa_family); + SENDERR(EPFNOSUPPORT); + } + + if(pfkey_address->sadb_address_len != + DIVUP(sizeof(struct sadb_address) + saddr_len, IPSEC_PFKEYv2_ALIGN)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "size wrong 2 ext_len=%d, adr_ext_len=%d, saddr_len=%d.\n", + pfkey_address->sadb_address_len, + (int)sizeof(struct sadb_address), + saddr_len); + SENDERR(EINVAL); + } + + if(pfkey_address->sadb_address_prefixlen != 0) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "address prefixes not supported yet.\n"); + SENDERR(EAFNOSUPPORT); /* not supported yet */ + } + + /* XXX check if port!=0 */ + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_address_parse: successful.\n"); + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_key_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_key *pfkey_key = (struct sadb_key *)pfkey_ext; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_key_parse:enter\n"); + /* sanity checks... */ + + if(!pfkey_key) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "NULL pointer passed in.\n"); + SENDERR(EINVAL); + } + + if(pfkey_key->sadb_key_len < sizeof(struct sadb_key) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_key->sadb_key_len, + (int)sizeof(struct sadb_key)); + SENDERR(EINVAL); + } + + if(!pfkey_key->sadb_key_bits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "key length set to zero, must be non-zero.\n"); + SENDERR(EINVAL); + } + + if(pfkey_key->sadb_key_len != + DIVUP(sizeof(struct sadb_key) * OCTETBITS + pfkey_key->sadb_key_bits, + PFKEYBITS)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "key length=%d does not agree with extension length=%d.\n", + pfkey_key->sadb_key_bits, + pfkey_key->sadb_key_len); + SENDERR(EINVAL); + } + + if(pfkey_key->sadb_key_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "res=%d, must be zero.\n", + pfkey_key->sadb_key_reserved); + SENDERR(EINVAL); + } + + if(! ( (pfkey_key->sadb_key_exttype == SADB_EXT_KEY_AUTH) || + (pfkey_key->sadb_key_exttype == SADB_EXT_KEY_ENCRYPT))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "expecting extension type AUTH or ENCRYPT, got %d.\n", + pfkey_key->sadb_key_exttype); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_key_parse: " + "success, found len=%d exttype=%d(%s) bits=%d reserved=%d.\n", + pfkey_key->sadb_key_len, + pfkey_key->sadb_key_exttype, + pfkey_v2_sadb_ext_string(pfkey_key->sadb_key_exttype), + pfkey_key->sadb_key_bits, + pfkey_key->sadb_key_reserved); + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_ident_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_ident *pfkey_ident = (struct sadb_ident *)pfkey_ext; + + /* sanity checks... */ + if(pfkey_ident->sadb_ident_len < sizeof(struct sadb_ident) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_ident_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_ident->sadb_ident_len, + (int)sizeof(struct sadb_ident)); + SENDERR(EINVAL); + } + + if(pfkey_ident->sadb_ident_type > SADB_IDENTTYPE_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_ident_parse: " + "ident_type=%d out of range, must be less than %d.\n", + pfkey_ident->sadb_ident_type, + SADB_IDENTTYPE_MAX); + SENDERR(EINVAL); + } + + if(pfkey_ident->sadb_ident_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_ident_parse: " + "res=%d, must be zero.\n", + pfkey_ident->sadb_ident_reserved); + SENDERR(EINVAL); + } + + /* string terminator/padding must be zero */ + if(pfkey_ident->sadb_ident_len > sizeof(struct sadb_ident) / IPSEC_PFKEYv2_ALIGN) { + if(*((char*)pfkey_ident + pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - 1)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_ident_parse: " + "string padding must be zero, last is 0x%02x.\n", + *((char*)pfkey_ident + + pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - 1)); + SENDERR(EINVAL); + } + } + + if( ! ((pfkey_ident->sadb_ident_exttype == SADB_EXT_IDENTITY_SRC) || + (pfkey_ident->sadb_ident_exttype == SADB_EXT_IDENTITY_DST))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "expecting extension type IDENTITY_SRC or IDENTITY_DST, got %d.\n", + pfkey_ident->sadb_ident_exttype); + SENDERR(EINVAL); + } + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_sens_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_sens *pfkey_sens = (struct sadb_sens *)pfkey_ext; + + /* sanity checks... */ + if(pfkey_sens->sadb_sens_len < sizeof(struct sadb_sens) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sens_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_sens->sadb_sens_len, + (int)sizeof(struct sadb_sens)); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sens_parse: " + "Sorry, I can't parse exttype=%d yet.\n", + pfkey_ext->sadb_ext_type); +#if 0 + SENDERR(EINVAL); /* don't process these yet */ +#endif + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_prop_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + int i, num_comb; + struct sadb_prop *pfkey_prop = (struct sadb_prop *)pfkey_ext; + struct sadb_comb *pfkey_comb = (struct sadb_comb *)((char*)pfkey_ext + sizeof(struct sadb_prop)); + + /* sanity checks... */ + if((pfkey_prop->sadb_prop_len < sizeof(struct sadb_prop) / IPSEC_PFKEYv2_ALIGN) || + (((pfkey_prop->sadb_prop_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_prop)) % sizeof(struct sadb_comb))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "size wrong ext_len=%d, prop_ext_len=%d comb_ext_len=%d.\n", + pfkey_prop->sadb_prop_len, + (int)sizeof(struct sadb_prop), + (int)sizeof(struct sadb_comb)); + SENDERR(EINVAL); + } + + if(pfkey_prop->sadb_prop_replay > 64) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "replay window size: %d -- must be 0 <= size <= 64\n", + pfkey_prop->sadb_prop_replay); + SENDERR(EINVAL); + } + + for(i=0; i<3; i++) { + if(pfkey_prop->sadb_prop_reserved[i]) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "res[%d]=%d, must be zero.\n", + i, pfkey_prop->sadb_prop_reserved[i]); + SENDERR(EINVAL); + } + } + + num_comb = ((pfkey_prop->sadb_prop_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_prop)) / sizeof(struct sadb_comb); + + for(i = 0; i < num_comb; i++) { + if(pfkey_comb->sadb_comb_auth > SADB_AALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth=%d > SADB_AALG_MAX=%d.\n", + i, + pfkey_comb->sadb_comb_auth, + SADB_AALG_MAX); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_auth) { + if(!pfkey_comb->sadb_comb_auth_minbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_minbits=0, fatal.\n", + i); + SENDERR(EINVAL); + } + if(!pfkey_comb->sadb_comb_auth_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_maxbits=0, fatal.\n", + i); + SENDERR(EINVAL); + } + if(pfkey_comb->sadb_comb_auth_minbits > pfkey_comb->sadb_comb_auth_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_minbits=%d > maxbits=%d, fatal.\n", + i, + pfkey_comb->sadb_comb_auth_minbits, + pfkey_comb->sadb_comb_auth_maxbits); + SENDERR(EINVAL); + } + } else { + if(pfkey_comb->sadb_comb_auth_minbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_minbits=%d != 0, fatal.\n", + i, + pfkey_comb->sadb_comb_auth_minbits); + SENDERR(EINVAL); + } + if(pfkey_comb->sadb_comb_auth_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_maxbits=%d != 0, fatal.\n", + i, + pfkey_comb->sadb_comb_auth_maxbits); + SENDERR(EINVAL); + } + } + + if(pfkey_comb->sadb_comb_encrypt > SADB_EALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_comb_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt=%d > SADB_EALG_MAX=%d.\n", + i, + pfkey_comb->sadb_comb_encrypt, + SADB_EALG_MAX); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_encrypt) { + if(!pfkey_comb->sadb_comb_encrypt_minbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_minbits=0, fatal.\n", + i); + SENDERR(EINVAL); + } + if(!pfkey_comb->sadb_comb_encrypt_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_maxbits=0, fatal.\n", + i); + SENDERR(EINVAL); + } + if(pfkey_comb->sadb_comb_encrypt_minbits > pfkey_comb->sadb_comb_encrypt_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_minbits=%d > maxbits=%d, fatal.\n", + i, + pfkey_comb->sadb_comb_encrypt_minbits, + pfkey_comb->sadb_comb_encrypt_maxbits); + SENDERR(EINVAL); + } + } else { + if(pfkey_comb->sadb_comb_encrypt_minbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_minbits=%d != 0, fatal.\n", + i, + pfkey_comb->sadb_comb_encrypt_minbits); + SENDERR(EINVAL); + } + if(pfkey_comb->sadb_comb_encrypt_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_maxbits=%d != 0, fatal.\n", + i, + pfkey_comb->sadb_comb_encrypt_maxbits); + SENDERR(EINVAL); + } + } + + /* XXX do sanity check on flags */ + + if(pfkey_comb->sadb_comb_hard_allocations && pfkey_comb->sadb_comb_soft_allocations > pfkey_comb->sadb_comb_hard_allocations) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_soft_allocations=%d > hard_allocations=%d, fatal.\n", + i, + pfkey_comb->sadb_comb_soft_allocations, + pfkey_comb->sadb_comb_hard_allocations); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_hard_bytes && pfkey_comb->sadb_comb_soft_bytes > pfkey_comb->sadb_comb_hard_bytes) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_soft_bytes=%Ld > hard_bytes=%Ld, fatal.\n", + i, + (unsigned long long int)pfkey_comb->sadb_comb_soft_bytes, + (unsigned long long int)pfkey_comb->sadb_comb_hard_bytes); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_hard_addtime && pfkey_comb->sadb_comb_soft_addtime > pfkey_comb->sadb_comb_hard_addtime) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_soft_addtime=%Ld > hard_addtime=%Ld, fatal.\n", + i, + (unsigned long long int)pfkey_comb->sadb_comb_soft_addtime, + (unsigned long long int)pfkey_comb->sadb_comb_hard_addtime); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_hard_usetime && pfkey_comb->sadb_comb_soft_usetime > pfkey_comb->sadb_comb_hard_usetime) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_soft_usetime=%Ld > hard_usetime=%Ld, fatal.\n", + i, + (unsigned long long int)pfkey_comb->sadb_comb_soft_usetime, + (unsigned long long int)pfkey_comb->sadb_comb_hard_usetime); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_x_comb_hard_packets && pfkey_comb->sadb_x_comb_soft_packets > pfkey_comb->sadb_x_comb_hard_packets) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_x_comb_soft_packets=%d > hard_packets=%d, fatal.\n", + i, + pfkey_comb->sadb_x_comb_soft_packets, + pfkey_comb->sadb_x_comb_hard_packets); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "comb[%d].res=%d, must be zero.\n", + i, + pfkey_comb->sadb_comb_reserved); + SENDERR(EINVAL); + } + pfkey_comb++; + } + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_supported_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + unsigned int i, num_alg; + struct sadb_supported *pfkey_supported = (struct sadb_supported *)pfkey_ext; + struct sadb_alg *pfkey_alg = (struct sadb_alg*)((char*)pfkey_ext + sizeof(struct sadb_supported)); + + /* sanity checks... */ + if((pfkey_supported->sadb_supported_len < + sizeof(struct sadb_supported) / IPSEC_PFKEYv2_ALIGN) || + (((pfkey_supported->sadb_supported_len * IPSEC_PFKEYv2_ALIGN) - + sizeof(struct sadb_supported)) % sizeof(struct sadb_alg))) { + + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "size wrong ext_len=%d, supported_ext_len=%d alg_ext_len=%d.\n", + pfkey_supported->sadb_supported_len, + (int)sizeof(struct sadb_supported), + (int)sizeof(struct sadb_alg)); + SENDERR(EINVAL); + } + + if(pfkey_supported->sadb_supported_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "res=%d, must be zero.\n", + pfkey_supported->sadb_supported_reserved); + SENDERR(EINVAL); + } + + num_alg = ((pfkey_supported->sadb_supported_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_supported)) / sizeof(struct sadb_alg); + + for(i = 0; i < num_alg; i++) { + /* process algo description */ + if(pfkey_alg->sadb_alg_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "alg[%d], id=%d, ivlen=%d, minbits=%d, maxbits=%d, res=%d, must be zero.\n", + i, + pfkey_alg->sadb_alg_id, + pfkey_alg->sadb_alg_ivlen, + pfkey_alg->sadb_alg_minbits, + pfkey_alg->sadb_alg_maxbits, + pfkey_alg->sadb_alg_reserved); + SENDERR(EINVAL); + } + + /* XXX can alg_id auth/enc be determined from info given? + Yes, but OpenBSD's method does not iteroperate with rfc2367. + rgb, 2000-04-06 */ + + switch(pfkey_supported->sadb_supported_exttype) { + case SADB_EXT_SUPPORTED_AUTH: + if(pfkey_alg->sadb_alg_id > SADB_AALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "alg[%d], alg_id=%d > SADB_AALG_MAX=%d, fatal.\n", + i, + pfkey_alg->sadb_alg_id, + SADB_AALG_MAX); + SENDERR(EINVAL); + } + break; + case SADB_EXT_SUPPORTED_ENCRYPT: + if(pfkey_alg->sadb_alg_id > SADB_EALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "alg[%d], alg_id=%d > SADB_EALG_MAX=%d, fatal.\n", + i, + pfkey_alg->sadb_alg_id, + SADB_EALG_MAX); + SENDERR(EINVAL); + } + break; + default: + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "alg[%d], alg_id=%d > SADB_EALG_MAX=%d, fatal.\n", + i, + pfkey_alg->sadb_alg_id, + SADB_EALG_MAX); + SENDERR(EINVAL); + } + pfkey_alg++; + } + + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_spirange_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_spirange *pfkey_spirange = (struct sadb_spirange *)pfkey_ext; + + /* sanity checks... */ + if(pfkey_spirange->sadb_spirange_len != + sizeof(struct sadb_spirange) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_spirange_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_spirange->sadb_spirange_len, + (int)sizeof(struct sadb_spirange)); + SENDERR(EINVAL); + } + + if(pfkey_spirange->sadb_spirange_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_spirange_parse: " + "reserved=%d must be set to zero.\n", + pfkey_spirange->sadb_spirange_reserved); + SENDERR(EINVAL); + } + + if(ntohl(pfkey_spirange->sadb_spirange_max) < ntohl(pfkey_spirange->sadb_spirange_min)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_spirange_parse: " + "minspi=%08x must be < maxspi=%08x.\n", + ntohl(pfkey_spirange->sadb_spirange_min), + ntohl(pfkey_spirange->sadb_spirange_max)); + SENDERR(EINVAL); + } + + if(ntohl(pfkey_spirange->sadb_spirange_min) <= 255) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_spirange_parse: " + "minspi=%08x must be > 255.\n", + ntohl(pfkey_spirange->sadb_spirange_min)); + SENDERR(EEXIST); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_spirange_parse: " + "ext_len=%u ext_type=%u(%s) min=%u max=%u res=%u.\n", + pfkey_spirange->sadb_spirange_len, + pfkey_spirange->sadb_spirange_exttype, + pfkey_v2_sadb_ext_string(pfkey_spirange->sadb_spirange_exttype), + pfkey_spirange->sadb_spirange_min, + pfkey_spirange->sadb_spirange_max, + pfkey_spirange->sadb_spirange_reserved); + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_kmprivate_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_x_kmprivate *pfkey_x_kmprivate = (struct sadb_x_kmprivate *)pfkey_ext; + + /* sanity checks... */ + if(pfkey_x_kmprivate->sadb_x_kmprivate_len < + sizeof(struct sadb_x_kmprivate) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_kmprivate_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_x_kmprivate->sadb_x_kmprivate_len, + (int)sizeof(struct sadb_x_kmprivate)); + SENDERR(EINVAL); + } + + if(pfkey_x_kmprivate->sadb_x_kmprivate_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_kmprivate_parse: " + "reserved=%d must be set to zero.\n", + pfkey_x_kmprivate->sadb_x_kmprivate_reserved); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_kmprivate_parse: " + "Sorry, I can't parse exttype=%d yet.\n", + pfkey_ext->sadb_ext_type); + SENDERR(EINVAL); /* don't process these yet */ + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_satype_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + int i; + struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)pfkey_ext; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_x_satype_parse: enter\n"); + /* sanity checks... */ + if(pfkey_x_satype->sadb_x_satype_len != + sizeof(struct sadb_x_satype) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_x_satype->sadb_x_satype_len, + (int)sizeof(struct sadb_x_satype)); + SENDERR(EINVAL); + } + + if(!pfkey_x_satype->sadb_x_satype_satype) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "satype is zero, must be non-zero.\n"); + SENDERR(EINVAL); + } + + if(pfkey_x_satype->sadb_x_satype_satype > SADB_SATYPE_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "satype %d > max %d, invalid.\n", + pfkey_x_satype->sadb_x_satype_satype, SADB_SATYPE_MAX); + SENDERR(EINVAL); + } + + if(!(satype2proto(pfkey_x_satype->sadb_x_satype_satype))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "proto lookup from satype=%d failed.\n", + pfkey_x_satype->sadb_x_satype_satype); + SENDERR(EINVAL); + } + + for(i = 0; i < 3; i++) { + if(pfkey_x_satype->sadb_x_satype_reserved[i]) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "reserved[%d]=%d must be set to zero.\n", + i, pfkey_x_satype->sadb_x_satype_reserved[i]); + SENDERR(EINVAL); + } + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_x_satype_parse: " + "len=%u ext=%u(%s) satype=%u(%s) res=%u,%u,%u.\n", + pfkey_x_satype->sadb_x_satype_len, + pfkey_x_satype->sadb_x_satype_exttype, + pfkey_v2_sadb_ext_string(pfkey_x_satype->sadb_x_satype_exttype), + pfkey_x_satype->sadb_x_satype_satype, + satype2name(pfkey_x_satype->sadb_x_satype_satype), + pfkey_x_satype->sadb_x_satype_reserved[0], + pfkey_x_satype->sadb_x_satype_reserved[1], + pfkey_x_satype->sadb_x_satype_reserved[2]); +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_ext_debug_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + int i; + struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)pfkey_ext; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_x_debug_parse: enter\n"); + /* sanity checks... */ + if(pfkey_x_debug->sadb_x_debug_len != + sizeof(struct sadb_x_debug) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_debug_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_x_debug->sadb_x_debug_len, + (int)sizeof(struct sadb_x_debug)); + SENDERR(EINVAL); + } + + for(i = 0; i < 4; i++) { + if(pfkey_x_debug->sadb_x_debug_reserved[i]) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_debug_parse: " + "reserved[%d]=%d must be set to zero.\n", + i, pfkey_x_debug->sadb_x_debug_reserved[i]); + SENDERR(EINVAL); + } + } + +errlab: + return error; +} + +#ifdef NAT_TRAVERSAL +DEBUG_NO_STATIC int +pfkey_x_ext_nat_t_type_parse(struct sadb_ext *pfkey_ext) +{ + return 0; +} +DEBUG_NO_STATIC int +pfkey_x_ext_nat_t_port_parse(struct sadb_ext *pfkey_ext) +{ + return 0; +} +#endif + +#define DEFINEPARSER(NAME) static struct pf_key_ext_parsers_def NAME##_def={NAME, #NAME}; + +DEFINEPARSER(pfkey_sa_parse); +DEFINEPARSER(pfkey_lifetime_parse); +DEFINEPARSER(pfkey_address_parse); +DEFINEPARSER(pfkey_key_parse); +DEFINEPARSER(pfkey_ident_parse); +DEFINEPARSER(pfkey_sens_parse); +DEFINEPARSER(pfkey_prop_parse); +DEFINEPARSER(pfkey_supported_parse); +DEFINEPARSER(pfkey_spirange_parse); +DEFINEPARSER(pfkey_x_kmprivate_parse); +DEFINEPARSER(pfkey_x_satype_parse); +DEFINEPARSER(pfkey_x_ext_debug_parse); +#ifdef NAT_TRAVERSAL +DEFINEPARSER(pfkey_x_ext_nat_t_type_parse); +DEFINEPARSER(pfkey_x_ext_nat_t_port_parse); +#endif + +struct pf_key_ext_parsers_def *ext_default_parsers[]= +{ + NULL, /* pfkey_msg_parse, */ + &pfkey_sa_parse_def, + &pfkey_lifetime_parse_def, + &pfkey_lifetime_parse_def, + &pfkey_lifetime_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_key_parse_def, + &pfkey_key_parse_def, + &pfkey_ident_parse_def, + &pfkey_ident_parse_def, + &pfkey_sens_parse_def, + &pfkey_prop_parse_def, + &pfkey_supported_parse_def, + &pfkey_supported_parse_def, + &pfkey_spirange_parse_def, + &pfkey_x_kmprivate_parse_def, + &pfkey_x_satype_parse_def, + &pfkey_sa_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_x_ext_debug_parse_def +#ifdef NAT_TRAVERSAL + , + &pfkey_x_ext_nat_t_type_parse_def, + &pfkey_x_ext_nat_t_port_parse_def, + &pfkey_x_ext_nat_t_port_parse_def, + &pfkey_address_parse_def +#endif +}; + +int +pfkey_msg_parse(struct sadb_msg *pfkey_msg, + struct pf_key_ext_parsers_def *ext_parsers[], + struct sadb_ext *extensions[], + int dir) +{ + int error = 0; + int remain; + struct sadb_ext *pfkey_ext; + int extensions_seen = 0; + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_msg_parse: " + "parsing message ver=%d, type=%d(%s), errno=%d, satype=%d(%s), len=%d, res=%d, seq=%d, pid=%d.\n", + pfkey_msg->sadb_msg_version, + pfkey_msg->sadb_msg_type, + pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type), + pfkey_msg->sadb_msg_errno, + pfkey_msg->sadb_msg_satype, + satype2name(pfkey_msg->sadb_msg_satype), + pfkey_msg->sadb_msg_len, + pfkey_msg->sadb_msg_reserved, + pfkey_msg->sadb_msg_seq, + pfkey_msg->sadb_msg_pid); + + if(ext_parsers == NULL) ext_parsers = ext_default_parsers; + + pfkey_extensions_init(extensions); + + remain = pfkey_msg->sadb_msg_len; + remain -= sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN; + + pfkey_ext = (struct sadb_ext*)((char*)pfkey_msg + + sizeof(struct sadb_msg)); + + extensions[0] = (struct sadb_ext *) pfkey_msg; + + + if(pfkey_msg->sadb_msg_version != PF_KEY_V2) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "not PF_KEY_V2 msg, found %d, should be %d.\n", + pfkey_msg->sadb_msg_version, + PF_KEY_V2); + SENDERR(EINVAL); + } + + if(!pfkey_msg->sadb_msg_type) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "msg type not set, must be non-zero..\n"); + SENDERR(EINVAL); + } + + if(pfkey_msg->sadb_msg_type > SADB_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "msg type=%d > max=%d.\n", + pfkey_msg->sadb_msg_type, + SADB_MAX); + SENDERR(EINVAL); + } + + switch(pfkey_msg->sadb_msg_type) { + case SADB_GETSPI: + case SADB_UPDATE: + case SADB_ADD: + case SADB_DELETE: + case SADB_GET: + case SADB_X_GRPSA: + case SADB_X_ADDFLOW: + if(!satype2proto(pfkey_msg->sadb_msg_satype)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "satype %d conversion to proto failed for msg_type %d (%s).\n", + pfkey_msg->sadb_msg_satype, + pfkey_msg->sadb_msg_type, + pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type)); + SENDERR(EINVAL); + } else { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "satype %d(%s) conversion to proto gives %d for msg_type %d(%s).\n", + pfkey_msg->sadb_msg_satype, + satype2name(pfkey_msg->sadb_msg_satype), + satype2proto(pfkey_msg->sadb_msg_satype), + pfkey_msg->sadb_msg_type, + pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type)); + } + case SADB_ACQUIRE: + case SADB_REGISTER: + case SADB_EXPIRE: + if(!pfkey_msg->sadb_msg_satype) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "satype is zero, must be non-zero for msg_type %d(%s).\n", + pfkey_msg->sadb_msg_type, + pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type)); + SENDERR(EINVAL); + } + default: + break; + } + + /* errno must not be set in downward messages */ + /* this is not entirely true... a response to an ACQUIRE could return an error */ + if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type != SADB_ACQUIRE) && pfkey_msg->sadb_msg_errno) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "errno set to %d.\n", + pfkey_msg->sadb_msg_errno); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_msg_parse: " + "remain=%d, ext_type=%d(%s), ext_len=%d.\n", + remain, + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + pfkey_ext->sadb_ext_len); + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_msg_parse: " + "extensions permitted=%08x, required=%08x.\n", + extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type], + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]); + + extensions_seen = 1; + + while( (remain * IPSEC_PFKEYv2_ALIGN) >= sizeof(struct sadb_ext) ) { + /* Is there enough message left to support another extension header? */ + if(remain < pfkey_ext->sadb_ext_len) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "remain %d less than ext len %d.\n", + remain, pfkey_ext->sadb_ext_len); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_msg_parse: " + "parsing ext type=%d(%s) remain=%d.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + remain); + + /* Is the extension header type valid? */ + if((pfkey_ext->sadb_ext_type > SADB_EXT_MAX) || (!pfkey_ext->sadb_ext_type)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ext type %d(%s) invalid, SADB_EXT_MAX=%d.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + SADB_EXT_MAX); + SENDERR(EINVAL); + } + + /* Have we already seen this type of extension? */ + if((extensions_seen & ( 1 << pfkey_ext->sadb_ext_type )) != 0) + { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ext type %d(%s) already seen.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type)); + SENDERR(EINVAL); + } + + /* Do I even know about this type of extension? */ + if(ext_parsers[pfkey_ext->sadb_ext_type]==NULL) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ext type %d(%s) unknown, ignoring.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type)); + goto next_ext; + } + + /* Is this type of extension permitted for this type of message? */ + if(!(extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type] & + 1<sadb_ext_type)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ext type %d(%s) not permitted, exts_perm_in=%08x, 1<sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type], + 1<sadb_ext_type); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_msg_parse: " + "remain=%d ext_type=%d(%s) ext_len=%d parsing ext 0p%p with parser %s.\n", + remain, + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + pfkey_ext->sadb_ext_len, + pfkey_ext, + ext_parsers[pfkey_ext->sadb_ext_type]->parser_name); + + /* Parse the extension */ + if((error = + (*ext_parsers[pfkey_ext->sadb_ext_type]->parser)(pfkey_ext))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "extension parsing for type %d(%s) failed with error %d.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + error); + SENDERR(-error); + } + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_msg_parse: " + "Extension %d(%s) parsed.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type)); + + /* Mark that we have seen this extension and remember the header location */ + extensions_seen |= ( 1 << pfkey_ext->sadb_ext_type ); + extensions[pfkey_ext->sadb_ext_type] = pfkey_ext; + + next_ext: + /* Calculate how much message remains */ + remain -= pfkey_ext->sadb_ext_len; + + if(!remain) { + break; + } + /* Find the next extension header */ + pfkey_ext = (struct sadb_ext*)((char*)pfkey_ext + + pfkey_ext->sadb_ext_len * IPSEC_PFKEYv2_ALIGN); + } + + if(remain) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "unexpected remainder of %d.\n", + remain); + /* why is there still something remaining? */ + SENDERR(EINVAL); + } + + /* check required extensions */ + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_msg_parse: " + "extensions permitted=%08x, seen=%08x, required=%08x.\n", + extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type], + extensions_seen, + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]); + + /* don't check further if it is an error return message since it + may not have a body */ + if(pfkey_msg->sadb_msg_errno) { + SENDERR(-error); + } + + if((extensions_seen & + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) != + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "required extensions missing:%08x.\n", + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type] - + (extensions_seen & + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type])); + SENDERR(EINVAL); + } + + if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type == SADB_X_DELFLOW) + && ((extensions_seen & SADB_X_EXT_ADDRESS_DELFLOW) + != SADB_X_EXT_ADDRESS_DELFLOW) + && (((extensions_seen & (1<sadb_sa_flags + & SADB_X_SAFLAGS_CLEARFLOW) + != SADB_X_SAFLAGS_CLEARFLOW))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "required SADB_X_DELFLOW extensions missing: either %08x must be present or %08x must be present with SADB_X_SAFLAGS_CLEARFLOW set.\n", + SADB_X_EXT_ADDRESS_DELFLOW + - (extensions_seen & SADB_X_EXT_ADDRESS_DELFLOW), + (1<sadb_msg_type) { + case SADB_ADD: + case SADB_UPDATE: + /* check maturity */ + if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state != + SADB_SASTATE_MATURE) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "state=%d for add or update should be MATURE=%d.\n", + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state, + SADB_SASTATE_MATURE); + SENDERR(EINVAL); + } + + /* check AH and ESP */ + switch(((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype) { + case SADB_SATYPE_AH: + if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) && + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_auth != + SADB_AALG_NONE)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "auth alg is zero, must be non-zero for AH SAs.\n"); + SENDERR(EINVAL); + } + if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt != + SADB_EALG_NONE) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "AH handed encalg=%d, must be zero.\n", + ((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt); + SENDERR(EINVAL); + } + break; + case SADB_SATYPE_ESP: + if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) && + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt != + SADB_EALG_NONE)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "encrypt alg=%d is zero, must be non-zero for ESP=%d SAs.\n", + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype); + SENDERR(EINVAL); + } + if((((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt == + SADB_EALG_NULL) && + (((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth == + SADB_AALG_NONE) ) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ESP handed encNULL+authNONE, illegal combination.\n"); + SENDERR(EINVAL); + } + break; + case SADB_X_SATYPE_COMP: + if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) && + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt != + SADB_EALG_NONE)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "encrypt alg=%d is zero, must be non-zero for COMP=%d SAs.\n", + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype); + SENDERR(EINVAL); + } + if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth != + SADB_AALG_NONE) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "COMP handed auth=%d, must be zero.\n", + ((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth); + SENDERR(EINVAL); + } + break; + default: + break; + } + if(ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi) <= 255) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "spi=%08x must be > 255.\n", + ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi)); + SENDERR(EINVAL); + } + default: + break; + } +errlab: + + return error; +} + +/* + * $Log: pfkey_v2_parse.c,v $ + * Revision 1.53 2003/01/30 02:32:09 rgb + * + * Rename SAref table macro names for clarity. + * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug. + * + * Revision 1.52 2002/12/30 06:53:07 mcr + * deal with short SA structures... #if 0 out for now. Probably + * not quite the right way. + * + * Revision 1.51 2002/12/13 18:16:02 mcr + * restored sa_ref code + * + * Revision 1.50 2002/12/13 18:06:52 mcr + * temporarily removed sadb_x_sa_ref reference for 2.xx + * + * Revision 1.49 2002/10/05 05:02:58 dhr + * + * C labels go on statements + * + * Revision 1.48 2002/09/20 15:40:45 rgb + * Added sadb_x_sa_ref to struct sadb_sa. + * + * Revision 1.47 2002/09/20 05:01:31 rgb + * Fixed usage of pfkey_lib_debug. + * Format for function declaration style consistency. + * Added text labels to elucidate numeric values presented. + * Re-organised debug output to reduce noise in output. + * + * Revision 1.46 2002/07/24 18:44:54 rgb + * Type fiddling to tame ia64 compiler. + * + * Revision 1.45 2002/05/23 07:14:11 rgb + * Cleaned up %p variants to 0p%p for test suite cleanup. + * + * Revision 1.44 2002/04/24 07:55:32 mcr + * #include patches and Makefiles for post-reorg compilation. + * + * Revision 1.43 2002/04/24 07:36:40 mcr + * Moved from ./lib/pfkey_v2_parse.c,v + * + * Revision 1.42 2002/01/29 22:25:36 rgb + * Re-add ipsec_kversion.h to keep MALLOC happy. + * + * Revision 1.41 2002/01/29 01:59:10 mcr + * removal of kversions.h - sources that needed it now use ipsec_param.h. + * updating of IPv6 structures to match latest in6.h version. + * removed dead code from freeswan.h that also duplicated kversions.h + * code. + * + * Revision 1.40 2002/01/20 20:34:50 mcr + * added pfkey_v2_sadb_type_string to decode sadb_type to string. + * + * Revision 1.39 2001/11/27 05:29:22 mcr + * pfkey parses are now maintained by a structure + * that includes their name for debug purposes. + * DEBUGGING() macro changed so that it takes a debug + * level so that pf_key() can use this to decode the + * structures without innundanting humans. + * Also uses pfkey_v2_sadb_ext_string() in messages. + * + * Revision 1.38 2001/11/06 19:47:47 rgb + * Added packet parameter to lifetime and comb structures. + * + * Revision 1.37 2001/10/18 04:45:24 rgb + * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, + * lib/freeswan.h version macros moved to lib/kversions.h. + * Other compiler directive cleanups. + * + * Revision 1.36 2001/06/14 19:35:16 rgb + * Update copyright date. + * + * Revision 1.35 2001/05/03 19:44:51 rgb + * Standardise on SENDERR() macro. + * + * Revision 1.34 2001/03/16 07:41:51 rgb + * Put freeswan.h include before pluto includes. + * + * Revision 1.33 2001/02/27 07:13:51 rgb + * Added satype2name() function. + * Added text to default satype_tbl entry. + * Added satype2name() conversions for most satype debug output. + * + * Revision 1.32 2001/02/26 20:01:09 rgb + * Added internal IP protocol 61 for magic SAs. + * Ditch unused sadb_satype2proto[], replaced by satype2proto(). + * Re-formatted debug output (split lines, consistent spacing). + * Removed acquire, register and expire requirements for a known satype. + * Changed message type checking to a switch structure. + * Verify expected NULL auth for IPCOMP. + * Enforced spi > 0x100 requirement, now that pass uses a magic SA for + * appropriate message types. + * + * Revision 1.31 2000/12/01 07:09:00 rgb + * Added ipcomp sanity check to require encalgo is set. + * + * Revision 1.30 2000/11/17 18:10:30 rgb + * Fixed bugs mostly relating to spirange, to treat all spi variables as + * network byte order since this is the way PF_KEYv2 stored spis. + * + * Revision 1.29 2000/10/12 00:02:39 rgb + * Removed 'format, ##' nonsense from debug macros for RH7.0. + * + * Revision 1.28 2000/09/20 16:23:04 rgb + * Remove over-paranoid extension check in the presence of sadb_msg_errno. + * + * Revision 1.27 2000/09/20 04:04:21 rgb + * Changed static functions to DEBUG_NO_STATIC to reveal function names in + * oopsen. + * + * Revision 1.26 2000/09/15 11:37:02 rgb + * Merge in heavily modified Svenning Soerensen's + * IPCOMP zlib deflate code. + * + * Revision 1.25 2000/09/12 22:35:37 rgb + * Restructured to remove unused extensions from CLEARFLOW messages. + * + * Revision 1.24 2000/09/12 18:59:54 rgb + * Added Gerhard's IPv6 support to pfkey parts of libfreeswan. + * + * Revision 1.23 2000/09/12 03:27:00 rgb + * Moved DEBUGGING definition to compile kernel with debug off. + * + * Revision 1.22 2000/09/09 06:39:27 rgb + * Restrict pfkey errno check to downward messages only. + * + * Revision 1.21 2000/09/08 19:22:34 rgb + * Enabled pfkey_sens_parse(). + * Added check for errno on downward acquire messages only. + * + * Revision 1.20 2000/09/01 18:48:23 rgb + * Fixed reserved check bug and added debug output in + * pfkey_supported_parse(). + * Fixed debug output label bug in pfkey_ident_parse(). + * + * Revision 1.19 2000/08/27 01:55:26 rgb + * Define OCTETBITS and PFKEYBITS to avoid using 'magic' numbers in code. + * + * Revision 1.18 2000/08/24 17:00:36 rgb + * Ignore unknown extensions instead of failing. + * + * Revision 1.17 2000/06/02 22:54:14 rgb + * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support. + * + * Revision 1.16 2000/05/10 19:25:11 rgb + * Fleshed out proposal and supported extensions. + * + * Revision 1.15 2000/01/24 21:15:31 rgb + * Added disabled pluto pfkey lib debug flag. + * Added algo debugging reporting. + * + * Revision 1.14 2000/01/22 23:24:29 rgb + * Added new functions proto2satype() and satype2proto() and lookup + * table satype_tbl. Also added proto2name() since it was easy. + * + * Revision 1.13 2000/01/21 09:43:59 rgb + * Cast ntohl(spi) as (unsigned long int) to shut up compiler. + * + * Revision 1.12 2000/01/21 06:28:19 rgb + * Added address cases for eroute flows. + * Indented compiler directives for readability. + * Added klipsdebug switching capability. + * + * Revision 1.11 1999/12/29 21:14:59 rgb + * Fixed debug text cut and paste typo. + * + * Revision 1.10 1999/12/10 17:45:24 rgb + * Added address debugging. + * + * Revision 1.9 1999/12/09 23:11:42 rgb + * Ditched include since we no longer use memset(). + * Use new pfkey_extensions_init() instead of memset(). + * Added check for SATYPE in pfkey_msg_build(). + * Tidy up comments and debugging comments. + * + * Revision 1.8 1999/12/07 19:55:26 rgb + * Removed unused first argument from extension parsers. + * Removed static pluto debug flag. + * Moved message type and state checking to pfkey_msg_parse(). + * Changed print[fk] type from lx to x to quiet compiler. + * Removed redundant remain check. + * Changed __u* types to uint* to avoid use of asm/types.h and + * sys/types.h in userspace code. + * + * Revision 1.7 1999/12/01 22:20:51 rgb + * Moved pfkey_lib_debug variable into the library. + * Added pfkey version check into header parsing. + * Added check for SATYPE only for those extensions that require a + * non-zero value. + * + * Revision 1.6 1999/11/27 11:58:05 rgb + * Added ipv6 headers. + * Moved sadb_satype2proto protocol lookup table from + * klips/net/ipsec/pfkey_v2_parser.c. + * Enable lifetime_current checking. + * Debugging error messages added. + * Add argument to pfkey_msg_parse() for direction. + * Consolidated the 4 1-d extension bitmap arrays into one 4-d array. + * Add CVS log entry to bottom of file. + * Moved auth and enc alg check to pfkey_msg_parse(). + * Enable accidentally disabled spirange parsing. + * Moved protocol/algorithm checks from klips/net/ipsec/pfkey_v2_parser.c + * + * Local variables: + * c-file-style: "linux" + * End: + * + */ diff --git a/tests/contrib/pfkey_v2_parse.c/orig b/tests/contrib/pfkey_v2_parse.c/orig new file mode 100644 index 0000000..8d09797 --- /dev/null +++ b/tests/contrib/pfkey_v2_parse.c/orig @@ -0,0 +1,1778 @@ +/* + * RFC2367 PF_KEYv2 Key management API message parser + * Copyright (C) 1999, 2000, 2001 Richard Guy Briggs. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * RCSID $Id: pfkey_v2_parse.c,v 1.53 2003/01/30 02:32:09 rgb Exp $ + */ + +/* + * Template from klips/net/ipsec/ipsec/ipsec_parser.c. + */ + +char pfkey_v2_parse_c_version[] = "$Id: pfkey_v2_parse.c,v 1.53 2003/01/30 02:32:09 rgb Exp $"; + +/* + * Some ugly stuff to allow consistent debugging code for use in the + * kernel and in user space +*/ + +#ifdef __KERNEL__ + +# include /* for printk */ + +#include "freeswan/ipsec_kversion.h" /* for malloc switch */ + +# ifdef MALLOC_SLAB +# include /* kmalloc() */ +# else /* MALLOC_SLAB */ +# include /* kmalloc() */ +# endif /* MALLOC_SLAB */ +# include /* error codes */ +# include /* size_t */ +# include /* mark_bh */ + +# include /* struct device, and other headers */ +# include /* eth_type_trans */ +# include /* struct iphdr */ +# if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +# include /* struct ipv6hdr */ +# endif /* if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ +extern int debug_pfkey; + +# include + +#include "freeswan/ipsec_encap.h" + +#else /* __KERNEL__ */ + +# include +# include +# include + +# include +# include "programs/pluto/constants.h" +# include "programs/pluto/defs.h" /* for PRINTF_LIKE */ +# include "programs/pluto/log.h" /* for debugging and DBG_log */ + +/* #define PLUTO */ + +# ifdef PLUTO +# define DEBUGGING(level, args...) { DBG_log("pfkey_lib_debug:" args); } +# else +# define DEBUGGING(level, args...) if(pfkey_lib_debug & level) { printf("pfkey_lib_debug:" args); } else { ; } +# endif + +#endif /* __KERNEL__ */ + + +#include +#include + +#ifdef __KERNEL__ +# include "freeswan/ipsec_netlink.h" /* KLIPS_PRINT */ +extern int sysctl_ipsec_debug_verbose; +# define DEBUGGING(level, args...) \ + KLIPS_PRINT( \ + ((debug_pfkey & level & (PF_KEY_DEBUG_PARSE_STRUCT | PF_KEY_DEBUG_PARSE_PROBLEM)) \ + || (sysctl_ipsec_debug_verbose && (debug_pfkey & level & PF_KEY_DEBUG_PARSE_FLOW))) \ + , "klips_debug:" args) +#endif /* __KERNEL__ */ +#include "freeswan/ipsec_sa.h" /* IPSEC_SAREF_NULL, IPSEC_SA_REF_TABLE_IDX_WIDTH */ + + +#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0) + +struct satype_tbl { + uint8_t proto; + uint8_t satype; + char* name; +} static satype_tbl[] = { +#ifdef __KERNEL__ + { IPPROTO_ESP, SADB_SATYPE_ESP, "ESP" }, + { IPPROTO_AH, SADB_SATYPE_AH, "AH" }, + { IPPROTO_IPIP, SADB_X_SATYPE_IPIP, "IPIP" }, +#ifdef CONFIG_IPSEC_IPCOMP + { IPPROTO_COMP, SADB_X_SATYPE_COMP, "COMP" }, +#endif /* CONFIG_IPSEC_IPCOMP */ + { IPPROTO_INT, SADB_X_SATYPE_INT, "INT" }, +#else /* __KERNEL__ */ + { SA_ESP, SADB_SATYPE_ESP, "ESP" }, + { SA_AH, SADB_SATYPE_AH, "AH" }, + { SA_IPIP, SADB_X_SATYPE_IPIP, "IPIP" }, + { SA_COMP, SADB_X_SATYPE_COMP, "COMP" }, + { SA_INT, SADB_X_SATYPE_INT, "INT" }, +#endif /* __KERNEL__ */ + { 0, 0, "UNKNOWN" } +}; + +uint8_t +satype2proto(uint8_t satype) +{ + int i =0; + + while(satype_tbl[i].satype != satype && satype_tbl[i].satype != 0) { + i++; + } + return satype_tbl[i].proto; +} + +uint8_t +proto2satype(uint8_t proto) +{ + int i = 0; + + while(satype_tbl[i].proto != proto && satype_tbl[i].proto != 0) { + i++; + } + return satype_tbl[i].satype; +} + +char* +satype2name(uint8_t satype) +{ + int i = 0; + + while(satype_tbl[i].satype != satype && satype_tbl[i].satype != 0) { + i++; + } + return satype_tbl[i].name; +} + +char* +proto2name(uint8_t proto) +{ + int i = 0; + + while(satype_tbl[i].proto != proto && satype_tbl[i].proto != 0) { + i++; + } + return satype_tbl[i].name; +} + +/* Default extension parsers taken from the KLIPS code */ + +DEBUG_NO_STATIC int +pfkey_sa_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_sa *pfkey_sa = (struct sadb_sa *)pfkey_ext; +#if 0 + struct sadb_sa sav2; +#endif + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_sa_parse: entry\n"); + /* sanity checks... */ + if(!pfkey_sa) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "NULL pointer passed in.\n"); + SENDERR(EINVAL); + } + +#if 0 + /* check if this structure is short, and if so, fix it up. + * XXX this is NOT the way to do things. + */ + if(pfkey_sa->sadb_sa_len == sizeof(struct sadb_sa_v1)/IPSEC_PFKEYv2_ALIGN) { + + /* yes, so clear out a temporary structure, and copy first */ + memset(&sav2, 0, sizeof(sav2)); + memcpy(&sav2, pfkey_sa, sizeof(struct sadb_sa_v1)); + sav2.sadb_x_sa_ref=-1; + sav2.sadb_sa_len = sizeof(struct sadb_sa) / IPSEC_PFKEYv2_ALIGN; + + pfkey_sa = &sav2; + } +#endif + + + if(pfkey_sa->sadb_sa_len != sizeof(struct sadb_sa) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "length wrong pfkey_sa->sadb_sa_len=%d sizeof(struct sadb_sa)=%d.\n", + pfkey_sa->sadb_sa_len, + (int)sizeof(struct sadb_sa)); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_encrypt > SADB_EALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "pfkey_sa->sadb_sa_encrypt=%d > SADB_EALG_MAX=%d.\n", + pfkey_sa->sadb_sa_encrypt, + SADB_EALG_MAX); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_auth > SADB_AALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "pfkey_sa->sadb_sa_auth=%d > SADB_AALG_MAX=%d.\n", + pfkey_sa->sadb_sa_auth, + SADB_AALG_MAX); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_state > SADB_SASTATE_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "state=%d exceeds MAX=%d.\n", + pfkey_sa->sadb_sa_state, + SADB_SASTATE_MAX); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_state == SADB_SASTATE_DEAD) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "state=%d is DEAD=%d.\n", + pfkey_sa->sadb_sa_state, + SADB_SASTATE_DEAD); + SENDERR(EINVAL); + } + + if(pfkey_sa->sadb_sa_replay > 64) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "replay window size: %d -- must be 0 <= size <= 64\n", + pfkey_sa->sadb_sa_replay); + SENDERR(EINVAL); + } + + if(! ((pfkey_sa->sadb_sa_exttype == SADB_EXT_SA) || + (pfkey_sa->sadb_sa_exttype == SADB_X_EXT_SA2))) + { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "unknown exttype=%d, expecting SADB_EXT_SA=%d or SADB_X_EXT_SA2=%d.\n", + pfkey_sa->sadb_sa_exttype, + SADB_EXT_SA, + SADB_X_EXT_SA2); + SENDERR(EINVAL); + } + + if((IPSEC_SAREF_NULL != pfkey_sa->sadb_x_sa_ref) && (pfkey_sa->sadb_x_sa_ref >= (1 << IPSEC_SA_REF_TABLE_IDX_WIDTH))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sa_parse: " + "SAref=%d must be (SAref == IPSEC_SAREF_NULL(%d) || SAref < IPSEC_SA_REF_TABLE_NUM_ENTRIES(%d)).\n", + pfkey_sa->sadb_x_sa_ref, + IPSEC_SAREF_NULL, + IPSEC_SA_REF_TABLE_NUM_ENTRIES); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_sa_parse: " + "successfully found len=%d exttype=%d(%s) spi=%08lx replay=%d state=%d auth=%d encrypt=%d flags=%d ref=%d.\n", + pfkey_sa->sadb_sa_len, + pfkey_sa->sadb_sa_exttype, + pfkey_v2_sadb_ext_string(pfkey_sa->sadb_sa_exttype), + (long unsigned int)ntohl(pfkey_sa->sadb_sa_spi), + pfkey_sa->sadb_sa_replay, + pfkey_sa->sadb_sa_state, + pfkey_sa->sadb_sa_auth, + pfkey_sa->sadb_sa_encrypt, + pfkey_sa->sadb_sa_flags, + pfkey_sa->sadb_x_sa_ref); + + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_lifetime_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)pfkey_ext; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_lifetime_parse:enter\n"); + /* sanity checks... */ + if(!pfkey_lifetime) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_lifetime_parse: " + "NULL pointer passed in.\n"); + SENDERR(EINVAL); + } + + if(pfkey_lifetime->sadb_lifetime_len != + sizeof(struct sadb_lifetime) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_lifetime_parse: " + "length wrong pfkey_lifetime->sadb_lifetime_len=%d sizeof(struct sadb_lifetime)=%d.\n", + pfkey_lifetime->sadb_lifetime_len, + (int)sizeof(struct sadb_lifetime)); + SENDERR(EINVAL); + } + + if((pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_HARD) && + (pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_SOFT) && + (pfkey_lifetime->sadb_lifetime_exttype != SADB_EXT_LIFETIME_CURRENT)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_lifetime_parse: " + "unexpected ext_type=%d.\n", + pfkey_lifetime->sadb_lifetime_exttype); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_lifetime_parse: " + "life_type=%d(%s) alloc=%u bytes=%u add=%u use=%u pkts=%u.\n", + pfkey_lifetime->sadb_lifetime_exttype, + pfkey_v2_sadb_ext_string(pfkey_lifetime->sadb_lifetime_exttype), + pfkey_lifetime->sadb_lifetime_allocations, + (unsigned)pfkey_lifetime->sadb_lifetime_bytes, + (unsigned)pfkey_lifetime->sadb_lifetime_addtime, + (unsigned)pfkey_lifetime->sadb_lifetime_usetime, + pfkey_lifetime->sadb_x_lifetime_packets); +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_address_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + int saddr_len = 0; + struct sadb_address *pfkey_address = (struct sadb_address *)pfkey_ext; + struct sockaddr* s = (struct sockaddr*)((char*)pfkey_address + sizeof(*pfkey_address)); + char ipaddr_txt[ADDRTOT_BUF]; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_address_parse:enter\n"); + /* sanity checks... */ + if(!pfkey_address) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "NULL pointer passed in.\n"); + SENDERR(EINVAL); + } + + if(pfkey_address->sadb_address_len < + (sizeof(struct sadb_address) + sizeof(struct sockaddr))/ + IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "size wrong 1 ext_len=%d, adr_ext_len=%d, saddr_len=%d.\n", + pfkey_address->sadb_address_len, + (int)sizeof(struct sadb_address), + (int)sizeof(struct sockaddr)); + SENDERR(EINVAL); + } + + if(pfkey_address->sadb_address_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "res=%d, must be zero.\n", + pfkey_address->sadb_address_reserved); + SENDERR(EINVAL); + } + + switch(pfkey_address->sadb_address_exttype) { + case SADB_EXT_ADDRESS_SRC: + case SADB_EXT_ADDRESS_DST: + case SADB_EXT_ADDRESS_PROXY: + case SADB_X_EXT_ADDRESS_DST2: + case SADB_X_EXT_ADDRESS_SRC_FLOW: + case SADB_X_EXT_ADDRESS_DST_FLOW: + case SADB_X_EXT_ADDRESS_SRC_MASK: + case SADB_X_EXT_ADDRESS_DST_MASK: +#ifdef NAT_TRAVERSAL + case SADB_X_EXT_NAT_T_OA: +#endif + break; + default: + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "unexpected ext_type=%d.\n", + pfkey_address->sadb_address_exttype); + SENDERR(EINVAL); + } + + switch(s->sa_family) { + case AF_INET: + saddr_len = sizeof(struct sockaddr_in); + sprintf(ipaddr_txt, "%d.%d.%d.%d" + , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 0) & 0xFF + , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 8) & 0xFF + , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 16) & 0xFF + , (((struct sockaddr_in*)s)->sin_addr.s_addr >> 24) & 0xFF); + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_address_parse: " + "found exttype=%u(%s) family=%d(AF_INET) address=%s proto=%u port=%u.\n", + pfkey_address->sadb_address_exttype, + pfkey_v2_sadb_ext_string(pfkey_address->sadb_address_exttype), + s->sa_family, + ipaddr_txt, + pfkey_address->sadb_address_proto, + ((struct sockaddr_in*)s)->sin_port); + break; + case AF_INET6: + saddr_len = sizeof(struct sockaddr_in6); + sprintf(ipaddr_txt, "%x:%x:%x:%x:%x:%x:%x:%x" + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[0]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[1]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[2]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[3]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[4]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[5]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[6]) + , ntohs(((struct sockaddr_in6*)s)->sin6_addr.s6_addr16[7])); + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_address_parse: " + "found exttype=%u(%s) family=%d(AF_INET6) address=%s proto=%u port=%u.\n", + pfkey_address->sadb_address_exttype, + pfkey_v2_sadb_ext_string(pfkey_address->sadb_address_exttype), + s->sa_family, + ipaddr_txt, + pfkey_address->sadb_address_proto, + ((struct sockaddr_in6*)s)->sin6_port); + break; + default: + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "s->sa_family=%d not supported.\n", + s->sa_family); + SENDERR(EPFNOSUPPORT); + } + + if(pfkey_address->sadb_address_len != + DIVUP(sizeof(struct sadb_address) + saddr_len, IPSEC_PFKEYv2_ALIGN)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "size wrong 2 ext_len=%d, adr_ext_len=%d, saddr_len=%d.\n", + pfkey_address->sadb_address_len, + (int)sizeof(struct sadb_address), + saddr_len); + SENDERR(EINVAL); + } + + if(pfkey_address->sadb_address_prefixlen != 0) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_address_parse: " + "address prefixes not supported yet.\n"); + SENDERR(EAFNOSUPPORT); /* not supported yet */ + } + + /* XXX check if port!=0 */ + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_address_parse: successful.\n"); + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_key_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_key *pfkey_key = (struct sadb_key *)pfkey_ext; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_key_parse:enter\n"); + /* sanity checks... */ + + if(!pfkey_key) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "NULL pointer passed in.\n"); + SENDERR(EINVAL); + } + + if(pfkey_key->sadb_key_len < sizeof(struct sadb_key) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_key->sadb_key_len, + (int)sizeof(struct sadb_key)); + SENDERR(EINVAL); + } + + if(!pfkey_key->sadb_key_bits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "key length set to zero, must be non-zero.\n"); + SENDERR(EINVAL); + } + + if(pfkey_key->sadb_key_len != + DIVUP(sizeof(struct sadb_key) * OCTETBITS + pfkey_key->sadb_key_bits, + PFKEYBITS)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "key length=%d does not agree with extension length=%d.\n", + pfkey_key->sadb_key_bits, + pfkey_key->sadb_key_len); + SENDERR(EINVAL); + } + + if(pfkey_key->sadb_key_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "res=%d, must be zero.\n", + pfkey_key->sadb_key_reserved); + SENDERR(EINVAL); + } + + if(! ( (pfkey_key->sadb_key_exttype == SADB_EXT_KEY_AUTH) || + (pfkey_key->sadb_key_exttype == SADB_EXT_KEY_ENCRYPT))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "expecting extension type AUTH or ENCRYPT, got %d.\n", + pfkey_key->sadb_key_exttype); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_key_parse: " + "success, found len=%d exttype=%d(%s) bits=%d reserved=%d.\n", + pfkey_key->sadb_key_len, + pfkey_key->sadb_key_exttype, + pfkey_v2_sadb_ext_string(pfkey_key->sadb_key_exttype), + pfkey_key->sadb_key_bits, + pfkey_key->sadb_key_reserved); + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_ident_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_ident *pfkey_ident = (struct sadb_ident *)pfkey_ext; + + /* sanity checks... */ + if(pfkey_ident->sadb_ident_len < sizeof(struct sadb_ident) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_ident_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_ident->sadb_ident_len, + (int)sizeof(struct sadb_ident)); + SENDERR(EINVAL); + } + + if(pfkey_ident->sadb_ident_type > SADB_IDENTTYPE_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_ident_parse: " + "ident_type=%d out of range, must be less than %d.\n", + pfkey_ident->sadb_ident_type, + SADB_IDENTTYPE_MAX); + SENDERR(EINVAL); + } + + if(pfkey_ident->sadb_ident_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_ident_parse: " + "res=%d, must be zero.\n", + pfkey_ident->sadb_ident_reserved); + SENDERR(EINVAL); + } + + /* string terminator/padding must be zero */ + if(pfkey_ident->sadb_ident_len > sizeof(struct sadb_ident) / IPSEC_PFKEYv2_ALIGN) { + if(*((char*)pfkey_ident + pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - 1)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_ident_parse: " + "string padding must be zero, last is 0x%02x.\n", + *((char*)pfkey_ident + + pfkey_ident->sadb_ident_len * IPSEC_PFKEYv2_ALIGN - 1)); + SENDERR(EINVAL); + } + } + + if( ! ((pfkey_ident->sadb_ident_exttype == SADB_EXT_IDENTITY_SRC) || + (pfkey_ident->sadb_ident_exttype == SADB_EXT_IDENTITY_DST))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_key_parse: " + "expecting extension type IDENTITY_SRC or IDENTITY_DST, got %d.\n", + pfkey_ident->sadb_ident_exttype); + SENDERR(EINVAL); + } + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_sens_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_sens *pfkey_sens = (struct sadb_sens *)pfkey_ext; + + /* sanity checks... */ + if(pfkey_sens->sadb_sens_len < sizeof(struct sadb_sens) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sens_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_sens->sadb_sens_len, + (int)sizeof(struct sadb_sens)); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_sens_parse: " + "Sorry, I can't parse exttype=%d yet.\n", + pfkey_ext->sadb_ext_type); +#if 0 + SENDERR(EINVAL); /* don't process these yet */ +#endif + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_prop_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + int i, num_comb; + struct sadb_prop *pfkey_prop = (struct sadb_prop *)pfkey_ext; + struct sadb_comb *pfkey_comb = (struct sadb_comb *)((char*)pfkey_ext + sizeof(struct sadb_prop)); + + /* sanity checks... */ + if((pfkey_prop->sadb_prop_len < sizeof(struct sadb_prop) / IPSEC_PFKEYv2_ALIGN) || + (((pfkey_prop->sadb_prop_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_prop)) % sizeof(struct sadb_comb))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "size wrong ext_len=%d, prop_ext_len=%d comb_ext_len=%d.\n", + pfkey_prop->sadb_prop_len, + (int)sizeof(struct sadb_prop), + (int)sizeof(struct sadb_comb)); + SENDERR(EINVAL); + } + + if(pfkey_prop->sadb_prop_replay > 64) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "replay window size: %d -- must be 0 <= size <= 64\n", + pfkey_prop->sadb_prop_replay); + SENDERR(EINVAL); + } + + for(i=0; i<3; i++) { + if(pfkey_prop->sadb_prop_reserved[i]) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "res[%d]=%d, must be zero.\n", + i, pfkey_prop->sadb_prop_reserved[i]); + SENDERR(EINVAL); + } + } + + num_comb = ((pfkey_prop->sadb_prop_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_prop)) / sizeof(struct sadb_comb); + + for(i = 0; i < num_comb; i++) { + if(pfkey_comb->sadb_comb_auth > SADB_AALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth=%d > SADB_AALG_MAX=%d.\n", + i, + pfkey_comb->sadb_comb_auth, + SADB_AALG_MAX); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_auth) { + if(!pfkey_comb->sadb_comb_auth_minbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_minbits=0, fatal.\n", + i); + SENDERR(EINVAL); + } + if(!pfkey_comb->sadb_comb_auth_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_maxbits=0, fatal.\n", + i); + SENDERR(EINVAL); + } + if(pfkey_comb->sadb_comb_auth_minbits > pfkey_comb->sadb_comb_auth_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_minbits=%d > maxbits=%d, fatal.\n", + i, + pfkey_comb->sadb_comb_auth_minbits, + pfkey_comb->sadb_comb_auth_maxbits); + SENDERR(EINVAL); + } + } else { + if(pfkey_comb->sadb_comb_auth_minbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_minbits=%d != 0, fatal.\n", + i, + pfkey_comb->sadb_comb_auth_minbits); + SENDERR(EINVAL); + } + if(pfkey_comb->sadb_comb_auth_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_auth_maxbits=%d != 0, fatal.\n", + i, + pfkey_comb->sadb_comb_auth_maxbits); + SENDERR(EINVAL); + } + } + + if(pfkey_comb->sadb_comb_encrypt > SADB_EALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_comb_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt=%d > SADB_EALG_MAX=%d.\n", + i, + pfkey_comb->sadb_comb_encrypt, + SADB_EALG_MAX); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_encrypt) { + if(!pfkey_comb->sadb_comb_encrypt_minbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_minbits=0, fatal.\n", + i); + SENDERR(EINVAL); + } + if(!pfkey_comb->sadb_comb_encrypt_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_maxbits=0, fatal.\n", + i); + SENDERR(EINVAL); + } + if(pfkey_comb->sadb_comb_encrypt_minbits > pfkey_comb->sadb_comb_encrypt_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_minbits=%d > maxbits=%d, fatal.\n", + i, + pfkey_comb->sadb_comb_encrypt_minbits, + pfkey_comb->sadb_comb_encrypt_maxbits); + SENDERR(EINVAL); + } + } else { + if(pfkey_comb->sadb_comb_encrypt_minbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_minbits=%d != 0, fatal.\n", + i, + pfkey_comb->sadb_comb_encrypt_minbits); + SENDERR(EINVAL); + } + if(pfkey_comb->sadb_comb_encrypt_maxbits) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_encrypt_maxbits=%d != 0, fatal.\n", + i, + pfkey_comb->sadb_comb_encrypt_maxbits); + SENDERR(EINVAL); + } + } + + /* XXX do sanity check on flags */ + + if(pfkey_comb->sadb_comb_hard_allocations && pfkey_comb->sadb_comb_soft_allocations > pfkey_comb->sadb_comb_hard_allocations) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_soft_allocations=%d > hard_allocations=%d, fatal.\n", + i, + pfkey_comb->sadb_comb_soft_allocations, + pfkey_comb->sadb_comb_hard_allocations); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_hard_bytes && pfkey_comb->sadb_comb_soft_bytes > pfkey_comb->sadb_comb_hard_bytes) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_soft_bytes=%Ld > hard_bytes=%Ld, fatal.\n", + i, + (unsigned long long int)pfkey_comb->sadb_comb_soft_bytes, + (unsigned long long int)pfkey_comb->sadb_comb_hard_bytes); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_hard_addtime && pfkey_comb->sadb_comb_soft_addtime > pfkey_comb->sadb_comb_hard_addtime) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_soft_addtime=%Ld > hard_addtime=%Ld, fatal.\n", + i, + (unsigned long long int)pfkey_comb->sadb_comb_soft_addtime, + (unsigned long long int)pfkey_comb->sadb_comb_hard_addtime); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_hard_usetime && pfkey_comb->sadb_comb_soft_usetime > pfkey_comb->sadb_comb_hard_usetime) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_comb_soft_usetime=%Ld > hard_usetime=%Ld, fatal.\n", + i, + (unsigned long long int)pfkey_comb->sadb_comb_soft_usetime, + (unsigned long long int)pfkey_comb->sadb_comb_hard_usetime); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_x_comb_hard_packets && pfkey_comb->sadb_x_comb_soft_packets > pfkey_comb->sadb_x_comb_hard_packets) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "pfkey_comb[%d]->sadb_x_comb_soft_packets=%d > hard_packets=%d, fatal.\n", + i, + pfkey_comb->sadb_x_comb_soft_packets, + pfkey_comb->sadb_x_comb_hard_packets); + SENDERR(EINVAL); + } + + if(pfkey_comb->sadb_comb_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_prop_parse: " + "comb[%d].res=%d, must be zero.\n", + i, + pfkey_comb->sadb_comb_reserved); + SENDERR(EINVAL); + } + pfkey_comb++; + } + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_supported_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + unsigned int i, num_alg; + struct sadb_supported *pfkey_supported = (struct sadb_supported *)pfkey_ext; + struct sadb_alg *pfkey_alg = (struct sadb_alg*)((char*)pfkey_ext + sizeof(struct sadb_supported)); + + /* sanity checks... */ + if((pfkey_supported->sadb_supported_len < + sizeof(struct sadb_supported) / IPSEC_PFKEYv2_ALIGN) || + (((pfkey_supported->sadb_supported_len * IPSEC_PFKEYv2_ALIGN) - + sizeof(struct sadb_supported)) % sizeof(struct sadb_alg))) { + + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "size wrong ext_len=%d, supported_ext_len=%d alg_ext_len=%d.\n", + pfkey_supported->sadb_supported_len, + (int)sizeof(struct sadb_supported), + (int)sizeof(struct sadb_alg)); + SENDERR(EINVAL); + } + + if(pfkey_supported->sadb_supported_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "res=%d, must be zero.\n", + pfkey_supported->sadb_supported_reserved); + SENDERR(EINVAL); + } + + num_alg = ((pfkey_supported->sadb_supported_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_supported)) / sizeof(struct sadb_alg); + + for(i = 0; i < num_alg; i++) { + /* process algo description */ + if(pfkey_alg->sadb_alg_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "alg[%d], id=%d, ivlen=%d, minbits=%d, maxbits=%d, res=%d, must be zero.\n", + i, + pfkey_alg->sadb_alg_id, + pfkey_alg->sadb_alg_ivlen, + pfkey_alg->sadb_alg_minbits, + pfkey_alg->sadb_alg_maxbits, + pfkey_alg->sadb_alg_reserved); + SENDERR(EINVAL); + } + + /* XXX can alg_id auth/enc be determined from info given? + Yes, but OpenBSD's method does not iteroperate with rfc2367. + rgb, 2000-04-06 */ + + switch(pfkey_supported->sadb_supported_exttype) { + case SADB_EXT_SUPPORTED_AUTH: + if(pfkey_alg->sadb_alg_id > SADB_AALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "alg[%d], alg_id=%d > SADB_AALG_MAX=%d, fatal.\n", + i, + pfkey_alg->sadb_alg_id, + SADB_AALG_MAX); + SENDERR(EINVAL); + } + break; + case SADB_EXT_SUPPORTED_ENCRYPT: + if(pfkey_alg->sadb_alg_id > SADB_EALG_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "alg[%d], alg_id=%d > SADB_EALG_MAX=%d, fatal.\n", + i, + pfkey_alg->sadb_alg_id, + SADB_EALG_MAX); + SENDERR(EINVAL); + } + break; + default: + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_supported_parse: " + "alg[%d], alg_id=%d > SADB_EALG_MAX=%d, fatal.\n", + i, + pfkey_alg->sadb_alg_id, + SADB_EALG_MAX); + SENDERR(EINVAL); + } + pfkey_alg++; + } + + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_spirange_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_spirange *pfkey_spirange = (struct sadb_spirange *)pfkey_ext; + + /* sanity checks... */ + if(pfkey_spirange->sadb_spirange_len != + sizeof(struct sadb_spirange) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_spirange_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_spirange->sadb_spirange_len, + (int)sizeof(struct sadb_spirange)); + SENDERR(EINVAL); + } + + if(pfkey_spirange->sadb_spirange_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_spirange_parse: " + "reserved=%d must be set to zero.\n", + pfkey_spirange->sadb_spirange_reserved); + SENDERR(EINVAL); + } + + if(ntohl(pfkey_spirange->sadb_spirange_max) < ntohl(pfkey_spirange->sadb_spirange_min)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_spirange_parse: " + "minspi=%08x must be < maxspi=%08x.\n", + ntohl(pfkey_spirange->sadb_spirange_min), + ntohl(pfkey_spirange->sadb_spirange_max)); + SENDERR(EINVAL); + } + + if(ntohl(pfkey_spirange->sadb_spirange_min) <= 255) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_spirange_parse: " + "minspi=%08x must be > 255.\n", + ntohl(pfkey_spirange->sadb_spirange_min)); + SENDERR(EEXIST); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_spirange_parse: " + "ext_len=%u ext_type=%u(%s) min=%u max=%u res=%u.\n", + pfkey_spirange->sadb_spirange_len, + pfkey_spirange->sadb_spirange_exttype, + pfkey_v2_sadb_ext_string(pfkey_spirange->sadb_spirange_exttype), + pfkey_spirange->sadb_spirange_min, + pfkey_spirange->sadb_spirange_max, + pfkey_spirange->sadb_spirange_reserved); + errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_kmprivate_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + struct sadb_x_kmprivate *pfkey_x_kmprivate = (struct sadb_x_kmprivate *)pfkey_ext; + + /* sanity checks... */ + if(pfkey_x_kmprivate->sadb_x_kmprivate_len < + sizeof(struct sadb_x_kmprivate) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_kmprivate_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_x_kmprivate->sadb_x_kmprivate_len, + (int)sizeof(struct sadb_x_kmprivate)); + SENDERR(EINVAL); + } + + if(pfkey_x_kmprivate->sadb_x_kmprivate_reserved) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_kmprivate_parse: " + "reserved=%d must be set to zero.\n", + pfkey_x_kmprivate->sadb_x_kmprivate_reserved); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_kmprivate_parse: " + "Sorry, I can't parse exttype=%d yet.\n", + pfkey_ext->sadb_ext_type); + SENDERR(EINVAL); /* don't process these yet */ + +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_satype_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + int i; + struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)pfkey_ext; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_x_satype_parse: enter\n"); + /* sanity checks... */ + if(pfkey_x_satype->sadb_x_satype_len != + sizeof(struct sadb_x_satype) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_x_satype->sadb_x_satype_len, + (int)sizeof(struct sadb_x_satype)); + SENDERR(EINVAL); + } + + if(!pfkey_x_satype->sadb_x_satype_satype) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "satype is zero, must be non-zero.\n"); + SENDERR(EINVAL); + } + + if(pfkey_x_satype->sadb_x_satype_satype > SADB_SATYPE_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "satype %d > max %d, invalid.\n", + pfkey_x_satype->sadb_x_satype_satype, SADB_SATYPE_MAX); + SENDERR(EINVAL); + } + + if(!(satype2proto(pfkey_x_satype->sadb_x_satype_satype))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "proto lookup from satype=%d failed.\n", + pfkey_x_satype->sadb_x_satype_satype); + SENDERR(EINVAL); + } + + for(i = 0; i < 3; i++) { + if(pfkey_x_satype->sadb_x_satype_reserved[i]) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_satype_parse: " + "reserved[%d]=%d must be set to zero.\n", + i, pfkey_x_satype->sadb_x_satype_reserved[i]); + SENDERR(EINVAL); + } + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_x_satype_parse: " + "len=%u ext=%u(%s) satype=%u(%s) res=%u,%u,%u.\n", + pfkey_x_satype->sadb_x_satype_len, + pfkey_x_satype->sadb_x_satype_exttype, + pfkey_v2_sadb_ext_string(pfkey_x_satype->sadb_x_satype_exttype), + pfkey_x_satype->sadb_x_satype_satype, + satype2name(pfkey_x_satype->sadb_x_satype_satype), + pfkey_x_satype->sadb_x_satype_reserved[0], + pfkey_x_satype->sadb_x_satype_reserved[1], + pfkey_x_satype->sadb_x_satype_reserved[2]); +errlab: + return error; +} + +DEBUG_NO_STATIC int +pfkey_x_ext_debug_parse(struct sadb_ext *pfkey_ext) +{ + int error = 0; + int i; + struct sadb_x_debug *pfkey_x_debug = (struct sadb_x_debug *)pfkey_ext; + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_x_debug_parse: enter\n"); + /* sanity checks... */ + if(pfkey_x_debug->sadb_x_debug_len != + sizeof(struct sadb_x_debug) / IPSEC_PFKEYv2_ALIGN) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_debug_parse: " + "size wrong ext_len=%d, key_ext_len=%d.\n", + pfkey_x_debug->sadb_x_debug_len, + (int)sizeof(struct sadb_x_debug)); + SENDERR(EINVAL); + } + + for(i = 0; i < 4; i++) { + if(pfkey_x_debug->sadb_x_debug_reserved[i]) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_x_debug_parse: " + "reserved[%d]=%d must be set to zero.\n", + i, pfkey_x_debug->sadb_x_debug_reserved[i]); + SENDERR(EINVAL); + } + } + +errlab: + return error; +} + +#ifdef NAT_TRAVERSAL +DEBUG_NO_STATIC int +pfkey_x_ext_nat_t_type_parse(struct sadb_ext *pfkey_ext) +{ + return 0; +} +DEBUG_NO_STATIC int +pfkey_x_ext_nat_t_port_parse(struct sadb_ext *pfkey_ext) +{ + return 0; +} +#endif + +#define DEFINEPARSER(NAME) static struct pf_key_ext_parsers_def NAME##_def={NAME, #NAME}; + +DEFINEPARSER(pfkey_sa_parse); +DEFINEPARSER(pfkey_lifetime_parse); +DEFINEPARSER(pfkey_address_parse); +DEFINEPARSER(pfkey_key_parse); +DEFINEPARSER(pfkey_ident_parse); +DEFINEPARSER(pfkey_sens_parse); +DEFINEPARSER(pfkey_prop_parse); +DEFINEPARSER(pfkey_supported_parse); +DEFINEPARSER(pfkey_spirange_parse); +DEFINEPARSER(pfkey_x_kmprivate_parse); +DEFINEPARSER(pfkey_x_satype_parse); +DEFINEPARSER(pfkey_x_ext_debug_parse); + +struct pf_key_ext_parsers_def *ext_default_parsers[]= +{ + NULL, /* pfkey_msg_parse, */ + &pfkey_sa_parse_def, + &pfkey_lifetime_parse_def, + &pfkey_lifetime_parse_def, + &pfkey_lifetime_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_key_parse_def, + &pfkey_key_parse_def, + &pfkey_ident_parse_def, + &pfkey_ident_parse_def, + &pfkey_sens_parse_def, + &pfkey_prop_parse_def, + &pfkey_supported_parse_def, + &pfkey_supported_parse_def, + &pfkey_spirange_parse_def, + &pfkey_x_kmprivate_parse_def, + &pfkey_x_satype_parse_def, + &pfkey_sa_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_x_ext_debug_parse_def +}; + +int +pfkey_msg_parse(struct sadb_msg *pfkey_msg, + struct pf_key_ext_parsers_def *ext_parsers[], + struct sadb_ext *extensions[], + int dir) +{ + int error = 0; + int remain; + struct sadb_ext *pfkey_ext; + int extensions_seen = 0; + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_msg_parse: " + "parsing message ver=%d, type=%d(%s), errno=%d, satype=%d(%s), len=%d, res=%d, seq=%d, pid=%d.\n", + pfkey_msg->sadb_msg_version, + pfkey_msg->sadb_msg_type, + pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type), + pfkey_msg->sadb_msg_errno, + pfkey_msg->sadb_msg_satype, + satype2name(pfkey_msg->sadb_msg_satype), + pfkey_msg->sadb_msg_len, + pfkey_msg->sadb_msg_reserved, + pfkey_msg->sadb_msg_seq, + pfkey_msg->sadb_msg_pid); + + if(ext_parsers == NULL) ext_parsers = ext_default_parsers; + + pfkey_extensions_init(extensions); + + remain = pfkey_msg->sadb_msg_len; + remain -= sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN; + + pfkey_ext = (struct sadb_ext*)((char*)pfkey_msg + + sizeof(struct sadb_msg)); + + extensions[0] = (struct sadb_ext *) pfkey_msg; + + + if(pfkey_msg->sadb_msg_version != PF_KEY_V2) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "not PF_KEY_V2 msg, found %d, should be %d.\n", + pfkey_msg->sadb_msg_version, + PF_KEY_V2); + SENDERR(EINVAL); + } + + if(!pfkey_msg->sadb_msg_type) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "msg type not set, must be non-zero..\n"); + SENDERR(EINVAL); + } + + if(pfkey_msg->sadb_msg_type > SADB_MAX) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "msg type=%d > max=%d.\n", + pfkey_msg->sadb_msg_type, + SADB_MAX); + SENDERR(EINVAL); + } + + switch(pfkey_msg->sadb_msg_type) { + case SADB_GETSPI: + case SADB_UPDATE: + case SADB_ADD: + case SADB_DELETE: + case SADB_GET: + case SADB_X_GRPSA: + case SADB_X_ADDFLOW: + if(!satype2proto(pfkey_msg->sadb_msg_satype)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "satype %d conversion to proto failed for msg_type %d (%s).\n", + pfkey_msg->sadb_msg_satype, + pfkey_msg->sadb_msg_type, + pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type)); + SENDERR(EINVAL); + } else { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "satype %d(%s) conversion to proto gives %d for msg_type %d(%s).\n", + pfkey_msg->sadb_msg_satype, + satype2name(pfkey_msg->sadb_msg_satype), + satype2proto(pfkey_msg->sadb_msg_satype), + pfkey_msg->sadb_msg_type, + pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type)); + } + case SADB_ACQUIRE: + case SADB_REGISTER: + case SADB_EXPIRE: + if(!pfkey_msg->sadb_msg_satype) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "satype is zero, must be non-zero for msg_type %d(%s).\n", + pfkey_msg->sadb_msg_type, + pfkey_v2_sadb_type_string(pfkey_msg->sadb_msg_type)); + SENDERR(EINVAL); + } + default: + break; + } + + /* errno must not be set in downward messages */ + /* this is not entirely true... a response to an ACQUIRE could return an error */ + if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type != SADB_ACQUIRE) && pfkey_msg->sadb_msg_errno) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "errno set to %d.\n", + pfkey_msg->sadb_msg_errno); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_msg_parse: " + "remain=%d, ext_type=%d(%s), ext_len=%d.\n", + remain, + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + pfkey_ext->sadb_ext_len); + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_msg_parse: " + "extensions permitted=%08x, required=%08x.\n", + extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type], + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]); + + extensions_seen = 1; + + while( (remain * IPSEC_PFKEYv2_ALIGN) >= sizeof(struct sadb_ext) ) { + /* Is there enough message left to support another extension header? */ + if(remain < pfkey_ext->sadb_ext_len) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "remain %d less than ext len %d.\n", + remain, pfkey_ext->sadb_ext_len); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_msg_parse: " + "parsing ext type=%d(%s) remain=%d.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + remain); + + /* Is the extension header type valid? */ + if((pfkey_ext->sadb_ext_type > SADB_EXT_MAX) || (!pfkey_ext->sadb_ext_type)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ext type %d(%s) invalid, SADB_EXT_MAX=%d.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + SADB_EXT_MAX); + SENDERR(EINVAL); + } + + /* Have we already seen this type of extension? */ + if((extensions_seen & ( 1 << pfkey_ext->sadb_ext_type )) != 0) + { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ext type %d(%s) already seen.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type)); + SENDERR(EINVAL); + } + + /* Do I even know about this type of extension? */ + if(ext_parsers[pfkey_ext->sadb_ext_type]==NULL) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ext type %d(%s) unknown, ignoring.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type)); + goto next_ext; + } + + /* Is this type of extension permitted for this type of message? */ + if(!(extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type] & + 1<sadb_ext_type)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ext type %d(%s) not permitted, exts_perm_in=%08x, 1<sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type], + 1<sadb_ext_type); + SENDERR(EINVAL); + } + + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_msg_parse: " + "remain=%d ext_type=%d(%s) ext_len=%d parsing ext 0p%p with parser %s.\n", + remain, + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + pfkey_ext->sadb_ext_len, + pfkey_ext, + ext_parsers[pfkey_ext->sadb_ext_type]->parser_name); + + /* Parse the extension */ + if((error = + (*ext_parsers[pfkey_ext->sadb_ext_type]->parser)(pfkey_ext))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "extension parsing for type %d(%s) failed with error %d.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type), + error); + SENDERR(-error); + } + DEBUGGING(PF_KEY_DEBUG_PARSE_FLOW, + "pfkey_msg_parse: " + "Extension %d(%s) parsed.\n", + pfkey_ext->sadb_ext_type, + pfkey_v2_sadb_ext_string(pfkey_ext->sadb_ext_type)); + + /* Mark that we have seen this extension and remember the header location */ + extensions_seen |= ( 1 << pfkey_ext->sadb_ext_type ); + extensions[pfkey_ext->sadb_ext_type] = pfkey_ext; + + next_ext: + /* Calculate how much message remains */ + remain -= pfkey_ext->sadb_ext_len; + + if(!remain) { + break; + } + /* Find the next extension header */ + pfkey_ext = (struct sadb_ext*)((char*)pfkey_ext + + pfkey_ext->sadb_ext_len * IPSEC_PFKEYv2_ALIGN); + } + + if(remain) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "unexpected remainder of %d.\n", + remain); + /* why is there still something remaining? */ + SENDERR(EINVAL); + } + + /* check required extensions */ + DEBUGGING(PF_KEY_DEBUG_PARSE_STRUCT, + "pfkey_msg_parse: " + "extensions permitted=%08x, seen=%08x, required=%08x.\n", + extensions_bitmaps[dir][EXT_BITS_PERM][pfkey_msg->sadb_msg_type], + extensions_seen, + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]); + + /* don't check further if it is an error return message since it + may not have a body */ + if(pfkey_msg->sadb_msg_errno) { + SENDERR(-error); + } + + if((extensions_seen & + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) != + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type]) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "required extensions missing:%08x.\n", + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type] - + (extensions_seen & + extensions_bitmaps[dir][EXT_BITS_REQ][pfkey_msg->sadb_msg_type])); + SENDERR(EINVAL); + } + + if((dir == EXT_BITS_IN) && (pfkey_msg->sadb_msg_type == SADB_X_DELFLOW) + && ((extensions_seen & SADB_X_EXT_ADDRESS_DELFLOW) + != SADB_X_EXT_ADDRESS_DELFLOW) + && (((extensions_seen & (1<sadb_sa_flags + & SADB_X_SAFLAGS_CLEARFLOW) + != SADB_X_SAFLAGS_CLEARFLOW))) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "required SADB_X_DELFLOW extensions missing: either %08x must be present or %08x must be present with SADB_X_SAFLAGS_CLEARFLOW set.\n", + SADB_X_EXT_ADDRESS_DELFLOW + - (extensions_seen & SADB_X_EXT_ADDRESS_DELFLOW), + (1<sadb_msg_type) { + case SADB_ADD: + case SADB_UPDATE: + /* check maturity */ + if(((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state != + SADB_SASTATE_MATURE) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "state=%d for add or update should be MATURE=%d.\n", + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_state, + SADB_SASTATE_MATURE); + SENDERR(EINVAL); + } + + /* check AH and ESP */ + switch(((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype) { + case SADB_SATYPE_AH: + if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) && + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_auth != + SADB_AALG_NONE)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "auth alg is zero, must be non-zero for AH SAs.\n"); + SENDERR(EINVAL); + } + if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt != + SADB_EALG_NONE) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "AH handed encalg=%d, must be zero.\n", + ((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt); + SENDERR(EINVAL); + } + break; + case SADB_SATYPE_ESP: + if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) && + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt != + SADB_EALG_NONE)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "encrypt alg=%d is zero, must be non-zero for ESP=%d SAs.\n", + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype); + SENDERR(EINVAL); + } + if((((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_encrypt == + SADB_EALG_NULL) && + (((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth == + SADB_AALG_NONE) ) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "ESP handed encNULL+authNONE, illegal combination.\n"); + SENDERR(EINVAL); + } + break; + case SADB_X_SATYPE_COMP: + if(!(((struct sadb_sa*)extensions[SADB_EXT_SA]) && + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt != + SADB_EALG_NONE)) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "encrypt alg=%d is zero, must be non-zero for COMP=%d SAs.\n", + ((struct sadb_sa*)extensions[SADB_EXT_SA])->sadb_sa_encrypt, + ((struct sadb_msg*)extensions[SADB_EXT_RESERVED])->sadb_msg_satype); + SENDERR(EINVAL); + } + if(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth != + SADB_AALG_NONE) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "COMP handed auth=%d, must be zero.\n", + ((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_auth); + SENDERR(EINVAL); + } + break; + default: + break; + } + if(ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi) <= 255) { + DEBUGGING(PF_KEY_DEBUG_PARSE_PROBLEM, + "pfkey_msg_parse: " + "spi=%08x must be > 255.\n", + ntohl(((struct sadb_sa*)(extensions[SADB_EXT_SA]))->sadb_sa_spi)); + SENDERR(EINVAL); + } + default: + break; + } +errlab: + + return error; +} + +/* + * $Log: pfkey_v2_parse.c,v $ + * Revision 1.53 2003/01/30 02:32:09 rgb + * + * Rename SAref table macro names for clarity. + * Convert IPsecSAref_t from signed to unsigned to fix apparent SAref exhaustion bug. + * + * Revision 1.52 2002/12/30 06:53:07 mcr + * deal with short SA structures... #if 0 out for now. Probably + * not quite the right way. + * + * Revision 1.51 2002/12/13 18:16:02 mcr + * restored sa_ref code + * + * Revision 1.50 2002/12/13 18:06:52 mcr + * temporarily removed sadb_x_sa_ref reference for 2.xx + * + * Revision 1.49 2002/10/05 05:02:58 dhr + * + * C labels go on statements + * + * Revision 1.48 2002/09/20 15:40:45 rgb + * Added sadb_x_sa_ref to struct sadb_sa. + * + * Revision 1.47 2002/09/20 05:01:31 rgb + * Fixed usage of pfkey_lib_debug. + * Format for function declaration style consistency. + * Added text labels to elucidate numeric values presented. + * Re-organised debug output to reduce noise in output. + * + * Revision 1.46 2002/07/24 18:44:54 rgb + * Type fiddling to tame ia64 compiler. + * + * Revision 1.45 2002/05/23 07:14:11 rgb + * Cleaned up %p variants to 0p%p for test suite cleanup. + * + * Revision 1.44 2002/04/24 07:55:32 mcr + * #include patches and Makefiles for post-reorg compilation. + * + * Revision 1.43 2002/04/24 07:36:40 mcr + * Moved from ./lib/pfkey_v2_parse.c,v + * + * Revision 1.42 2002/01/29 22:25:36 rgb + * Re-add ipsec_kversion.h to keep MALLOC happy. + * + * Revision 1.41 2002/01/29 01:59:10 mcr + * removal of kversions.h - sources that needed it now use ipsec_param.h. + * updating of IPv6 structures to match latest in6.h version. + * removed dead code from freeswan.h that also duplicated kversions.h + * code. + * + * Revision 1.40 2002/01/20 20:34:50 mcr + * added pfkey_v2_sadb_type_string to decode sadb_type to string. + * + * Revision 1.39 2001/11/27 05:29:22 mcr + * pfkey parses are now maintained by a structure + * that includes their name for debug purposes. + * DEBUGGING() macro changed so that it takes a debug + * level so that pf_key() can use this to decode the + * structures without innundanting humans. + * Also uses pfkey_v2_sadb_ext_string() in messages. + * + * Revision 1.38 2001/11/06 19:47:47 rgb + * Added packet parameter to lifetime and comb structures. + * + * Revision 1.37 2001/10/18 04:45:24 rgb + * 2.4.9 kernel deprecates linux/malloc.h in favour of linux/slab.h, + * lib/freeswan.h version macros moved to lib/kversions.h. + * Other compiler directive cleanups. + * + * Revision 1.36 2001/06/14 19:35:16 rgb + * Update copyright date. + * + * Revision 1.35 2001/05/03 19:44:51 rgb + * Standardise on SENDERR() macro. + * + * Revision 1.34 2001/03/16 07:41:51 rgb + * Put freeswan.h include before pluto includes. + * + * Revision 1.33 2001/02/27 07:13:51 rgb + * Added satype2name() function. + * Added text to default satype_tbl entry. + * Added satype2name() conversions for most satype debug output. + * + * Revision 1.32 2001/02/26 20:01:09 rgb + * Added internal IP protocol 61 for magic SAs. + * Ditch unused sadb_satype2proto[], replaced by satype2proto(). + * Re-formatted debug output (split lines, consistent spacing). + * Removed acquire, register and expire requirements for a known satype. + * Changed message type checking to a switch structure. + * Verify expected NULL auth for IPCOMP. + * Enforced spi > 0x100 requirement, now that pass uses a magic SA for + * appropriate message types. + * + * Revision 1.31 2000/12/01 07:09:00 rgb + * Added ipcomp sanity check to require encalgo is set. + * + * Revision 1.30 2000/11/17 18:10:30 rgb + * Fixed bugs mostly relating to spirange, to treat all spi variables as + * network byte order since this is the way PF_KEYv2 stored spis. + * + * Revision 1.29 2000/10/12 00:02:39 rgb + * Removed 'format, ##' nonsense from debug macros for RH7.0. + * + * Revision 1.28 2000/09/20 16:23:04 rgb + * Remove over-paranoid extension check in the presence of sadb_msg_errno. + * + * Revision 1.27 2000/09/20 04:04:21 rgb + * Changed static functions to DEBUG_NO_STATIC to reveal function names in + * oopsen. + * + * Revision 1.26 2000/09/15 11:37:02 rgb + * Merge in heavily modified Svenning Soerensen's + * IPCOMP zlib deflate code. + * + * Revision 1.25 2000/09/12 22:35:37 rgb + * Restructured to remove unused extensions from CLEARFLOW messages. + * + * Revision 1.24 2000/09/12 18:59:54 rgb + * Added Gerhard's IPv6 support to pfkey parts of libfreeswan. + * + * Revision 1.23 2000/09/12 03:27:00 rgb + * Moved DEBUGGING definition to compile kernel with debug off. + * + * Revision 1.22 2000/09/09 06:39:27 rgb + * Restrict pfkey errno check to downward messages only. + * + * Revision 1.21 2000/09/08 19:22:34 rgb + * Enabled pfkey_sens_parse(). + * Added check for errno on downward acquire messages only. + * + * Revision 1.20 2000/09/01 18:48:23 rgb + * Fixed reserved check bug and added debug output in + * pfkey_supported_parse(). + * Fixed debug output label bug in pfkey_ident_parse(). + * + * Revision 1.19 2000/08/27 01:55:26 rgb + * Define OCTETBITS and PFKEYBITS to avoid using 'magic' numbers in code. + * + * Revision 1.18 2000/08/24 17:00:36 rgb + * Ignore unknown extensions instead of failing. + * + * Revision 1.17 2000/06/02 22:54:14 rgb + * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support. + * + * Revision 1.16 2000/05/10 19:25:11 rgb + * Fleshed out proposal and supported extensions. + * + * Revision 1.15 2000/01/24 21:15:31 rgb + * Added disabled pluto pfkey lib debug flag. + * Added algo debugging reporting. + * + * Revision 1.14 2000/01/22 23:24:29 rgb + * Added new functions proto2satype() and satype2proto() and lookup + * table satype_tbl. Also added proto2name() since it was easy. + * + * Revision 1.13 2000/01/21 09:43:59 rgb + * Cast ntohl(spi) as (unsigned long int) to shut up compiler. + * + * Revision 1.12 2000/01/21 06:28:19 rgb + * Added address cases for eroute flows. + * Indented compiler directives for readability. + * Added klipsdebug switching capability. + * + * Revision 1.11 1999/12/29 21:14:59 rgb + * Fixed debug text cut and paste typo. + * + * Revision 1.10 1999/12/10 17:45:24 rgb + * Added address debugging. + * + * Revision 1.9 1999/12/09 23:11:42 rgb + * Ditched include since we no longer use memset(). + * Use new pfkey_extensions_init() instead of memset(). + * Added check for SATYPE in pfkey_msg_build(). + * Tidy up comments and debugging comments. + * + * Revision 1.8 1999/12/07 19:55:26 rgb + * Removed unused first argument from extension parsers. + * Removed static pluto debug flag. + * Moved message type and state checking to pfkey_msg_parse(). + * Changed print[fk] type from lx to x to quiet compiler. + * Removed redundant remain check. + * Changed __u* types to uint* to avoid use of asm/types.h and + * sys/types.h in userspace code. + * + * Revision 1.7 1999/12/01 22:20:51 rgb + * Moved pfkey_lib_debug variable into the library. + * Added pfkey version check into header parsing. + * Added check for SATYPE only for those extensions that require a + * non-zero value. + * + * Revision 1.6 1999/11/27 11:58:05 rgb + * Added ipv6 headers. + * Moved sadb_satype2proto protocol lookup table from + * klips/net/ipsec/pfkey_v2_parser.c. + * Enable lifetime_current checking. + * Debugging error messages added. + * Add argument to pfkey_msg_parse() for direction. + * Consolidated the 4 1-d extension bitmap arrays into one 4-d array. + * Add CVS log entry to bottom of file. + * Moved auth and enc alg check to pfkey_msg_parse(). + * Enable accidentally disabled spirange parsing. + * Moved protocol/algorithm checks from klips/net/ipsec/pfkey_v2_parser.c + * + * Local variables: + * c-file-style: "linux" + * End: + * + */ diff --git a/tests/contrib/pfkey_v2_parse.c/patch b/tests/contrib/pfkey_v2_parse.c/patch new file mode 100644 index 0000000..39b0b53 --- /dev/null +++ b/tests/contrib/pfkey_v2_parse.c/patch @@ -0,0 +1,57 @@ +*************** +*** 1140,1149 **** + DEFINEPARSER(pfkey_spirange_parse); + DEFINEPARSER(pfkey_x_kmprivate_parse); + DEFINEPARSER(pfkey_x_satype_parse); + DEFINEPARSER(pfkey_x_ext_debug_parse); + DEFINEPARSER(pfkey_x_ext_protocol_parse); + + struct pf_key_ext_parsers_def *ext_default_parsers[]= + { + NULL, /* pfkey_msg_parse, */ + &pfkey_sa_parse_def, +--- 1156,1169 ---- + DEFINEPARSER(pfkey_spirange_parse); + DEFINEPARSER(pfkey_x_kmprivate_parse); + DEFINEPARSER(pfkey_x_satype_parse); + DEFINEPARSER(pfkey_x_ext_debug_parse); + DEFINEPARSER(pfkey_x_ext_protocol_parse); ++ #ifdef NAT_TRAVERSAL ++ DEFINEPARSER(pfkey_x_ext_nat_t_type_parse); ++ DEFINEPARSER(pfkey_x_ext_nat_t_port_parse); ++ #endif + + struct pf_key_ext_parsers_def *ext_default_parsers[]= + { + NULL, /* pfkey_msg_parse, */ + &pfkey_sa_parse_def, +*************** +*** 1170,1179 **** + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_x_ext_debug_parse_def, + &pfkey_x_ext_protocol_parse_def + }; + + int + pfkey_msg_parse(struct sadb_msg *pfkey_msg, + struct pf_key_ext_parsers_def *ext_parsers[], +--- 1190,1206 ---- + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_address_parse_def, + &pfkey_x_ext_debug_parse_def, + &pfkey_x_ext_protocol_parse_def ++ #ifdef NAT_TRAVERSAL ++ , ++ &pfkey_x_ext_nat_t_type_parse_def, ++ &pfkey_x_ext_nat_t_port_parse_def, ++ &pfkey_x_ext_nat_t_port_parse_def, ++ &pfkey_address_parse_def ++ #endif + }; + + int + pfkey_msg_parse(struct sadb_msg *pfkey_msg, + struct pf_key_ext_parsers_def *ext_parsers[], diff --git a/tests/simple/bothadd/lmerge b/tests/simple/bothadd/lmerge new file mode 100644 index 0000000..163d8fb --- /dev/null +++ b/tests/simple/bothadd/lmerge @@ -0,0 +1,4 @@ +this is a +line of text +that was added +to the file diff --git a/tests/simple/bothadd/merge b/tests/simple/bothadd/merge new file mode 100644 index 0000000..163d8fb --- /dev/null +++ b/tests/simple/bothadd/merge @@ -0,0 +1,4 @@ +this is a +line of text +that was added +to the file diff --git a/tests/simple/bothadd/new b/tests/simple/bothadd/new new file mode 100644 index 0000000..b28b04f --- /dev/null +++ b/tests/simple/bothadd/new @@ -0,0 +1,3 @@ + + + diff --git a/tests/simple/bothadd/new2 b/tests/simple/bothadd/new2 new file mode 100644 index 0000000..163d8fb --- /dev/null +++ b/tests/simple/bothadd/new2 @@ -0,0 +1,4 @@ +this is a +line of text +that was added +to the file diff --git a/tests/simple/bothadd/orig b/tests/simple/bothadd/orig new file mode 100644 index 0000000..163d8fb --- /dev/null +++ b/tests/simple/bothadd/orig @@ -0,0 +1,4 @@ +this is a +line of text +that was added +to the file diff --git a/tests/simple/trivial-conflict/merge b/tests/simple/trivial-conflict/merge new file mode 100644 index 0000000..8308b25 --- /dev/null +++ b/tests/simple/trivial-conflict/merge @@ -0,0 +1,9 @@ +<<<<<<< +c +||||||| +*** 1,1 **** 1 +a +======= +*** 1,1 **** 1 +b +>>>>>>> diff --git a/tests/simple/trivial-conflict/orig b/tests/simple/trivial-conflict/orig new file mode 100644 index 0000000..f2ad6c7 --- /dev/null +++ b/tests/simple/trivial-conflict/orig @@ -0,0 +1 @@ +c diff --git a/tests/simple/trivial-conflict/patch b/tests/simple/trivial-conflict/patch new file mode 100644 index 0000000..3f94cc8 --- /dev/null +++ b/tests/simple/trivial-conflict/patch @@ -0,0 +1,5 @@ +--- a 2009-03-05 16:33:02.000000000 +1100 ++++ b 2009-03-05 16:33:04.000000000 +1100 +@@ -1 +1 @@ +-a ++b -- 2.39.5