diff options
author | Ori Bernstein <ori@eigenstate.org> | 2023-03-04 20:03:54 +0000 |
---|---|---|
committer | Ori Bernstein <ori@eigenstate.org> | 2023-03-04 20:03:54 +0000 |
commit | 40486d3641407466dfc08c7cddbee4a2f230758a (patch) | |
tree | f9f69cf127f0b57e6c1257b758ca47e4d5aaf8d0 /sys/src/cmd/diff | |
parent | 835d20a095ef12d9c6054d752241ed8b7d3cd20c (diff) |
diff, merge3: refactor diff, implement merge3
Refactor diff internals to allow multiple diffs
to be done in the same process. This allows a
merge3 to be implemented off the guts of
diff.
Tests are added, files with no end of line
terminator are currently broken.
Diffstat (limited to 'sys/src/cmd/diff')
93 files changed, 14715 insertions, 494 deletions
diff --git a/sys/src/cmd/diff/diff.c b/sys/src/cmd/diff/diff.c new file mode 100644 index 000000000..0efae9e5c --- /dev/null +++ b/sys/src/cmd/diff/diff.c @@ -0,0 +1,84 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include "diff.h" + +void +done(int status) +{ + switch(status) + { + case 0: + exits(""); + case 1: + exits("some"); + default: + exits("error"); + } +} + +void +usage(void) +{ + fprint(2, "usage: %s [-abcefmnrw] file1 ... file2\n", argv0); + exits("usage"); +} + +void +main(int argc, char *argv[]) +{ + int i; + Dir *fsb, *tsb; + + Binit(&stdout, 1, OWRITE); + ARGBEGIN{ + case 'e': + case 'f': + case 'n': + case 'c': + case 'a': + case 'u': + mode = ARGC(); + break; + case 'w': + bflag = 2; + break; + + case 'b': + bflag = 1; + break; + + case 'r': + rflag = 1; + break; + + case 'm': + mflag = 1; + break; + + case 'h': + default: + usage(); + }ARGEND; + if (argc < 2) + usage(); + if ((tsb = dirstat(argv[argc-1])) == nil) + sysfatal("can't stat %s", argv[argc-1]); + if (argc > 2) { + if (!DIRECTORY(tsb)) + sysfatal("not directory: %s", argv[argc-1]); + mflag = 1; + } else { + if ((fsb = dirstat(argv[0])) == nil) + sysfatal("can't stat %s", argv[0]); + if (DIRECTORY(fsb) && DIRECTORY(tsb)) + mflag = 1; + free(fsb); + } + free(tsb); + for (i = 0; i < argc-1; i++) + diff(argv[i], argv[argc-1], 0); + + done(anychange); + /*NOTREACHED*/ +} diff --git a/sys/src/cmd/diff/diff.h b/sys/src/cmd/diff/diff.h index e1c8263d6..df1038356 100644 --- a/sys/src/cmd/diff/diff.h +++ b/sys/src/cmd/diff/diff.h @@ -1,32 +1,77 @@ -typedef struct Line Line; +typedef struct Line Line; +typedef struct Cand Cand; +typedef struct Diff Diff; +typedef struct Change Change; struct Line { int serial; int value; }; -extern Line *file[2]; -extern int len[2]; -extern long *ixold, *ixnew; -extern int *J; + +struct Cand { + int x; + int y; + int pred; +}; + +struct Change +{ + int a; + int b; + int c; + int d; +}; + +struct Diff { + Cand cand; + Line *file[2], line; + int len[2]; + int binary; + Line *sfile[2]; /*shortened by pruning common prefix and suffix*/ + int slen[2]; + int pref, suff; /*length of prefix and suffix*/ + int *class; /*will be overlaid on file[0]*/ + int *member; /*will be overlaid on file[1]*/ + int *klist; /*will be overlaid on file[0] after class*/ + Cand *clist; /* merely a free storage pot for candidates */ + int clen; + int *J; /*will be overlaid on class*/ + long *ixold; /*will be overlaid on klist*/ + long *ixnew; /*will be overlaid on file[1]*/ + char *file1; + char *file2; + Biobuf *input[2]; + Biobuf *b0; + Biobuf *b1; + int firstchange; + Change *changes; + int nchanges; +}; + extern char mode; extern char bflag; extern char rflag; extern char mflag; extern int anychange; extern Biobuf stdout; -extern int binary; #define MAXPATHLEN 1024 +#define DIRECTORY(s) ((s)->qid.type&QTDIR) +#define REGULAR_FILE(s) ((s)->type == 'M' && !DIRECTORY(s)) + int mkpathname(char *, char *, char *); +char *mktmpfile(int, Dir **); +char *statfile(char *, Dir **); void *emalloc(unsigned); void *erealloc(void *, unsigned); void diff(char *, char *, int); +void diffreg(char*, char*, char*, char*); void diffdir(char *, char *, int); -void diffreg(char *, char *, char *, char *); -Biobuf *prepare(int, char *, char *); -void panic(int, char *, ...); -void check(Biobuf *, Biobuf *); -void change(int, int, int, int); -void flushchanges(void); - +void calcdiff(Diff *, char *, char *, char *, char *); +Biobuf *prepare(Diff*, int, char *, char *); +void check(Diff *, Biobuf *, Biobuf *); +void change(Diff *, int, int, int, int); +void freediff(Diff *); +void flushchanges(Diff *); +void fetch(Diff *d, long *f, int a, int b, Biobuf *bp, char *s); diff --git a/sys/src/cmd/diff/diffdir.c b/sys/src/cmd/diff/diffdir.c index 9f2b46e45..419a26eea 100644 --- a/sys/src/cmd/diff/diffdir.c +++ b/sys/src/cmd/diff/diffdir.c @@ -111,3 +111,45 @@ diffdir(char *f, char *t, int level) free(dirf); free(dirt); } + +void +diff(char *f, char *t, int level) +{ + char *fp, *tp, *p, fb[MAXPATHLEN+1], tb[MAXPATHLEN+1]; + Dir *fsb, *tsb; + + fsb = nil; + tsb = nil; + if ((fp = statfile(f, &fsb)) == 0) + goto Return; + if ((tp = statfile(t, &tsb)) == 0) + goto Return; + if (DIRECTORY(fsb) && DIRECTORY(tsb)) { + if (rflag || level == 0) + diffdir(fp, tp, level); + else + Bprint(&stdout, "Common subdirectories: %s and %s\n", fp, tp); + } + else if (REGULAR_FILE(fsb) && REGULAR_FILE(tsb)) + diffreg(fp, f, tp, t); + else { + if (REGULAR_FILE(fsb)) { + if ((p = utfrrune(f, '/')) == 0) + p = f; + else + p++; + if (mkpathname(tb, tp, p) == 0) + diffreg(fp, f, tb, t); + } else { + if ((p = utfrrune(t, '/')) == 0) + p = t; + else + p++; + if (mkpathname(fb, fp, p) == 0) + diffreg(fb, f, tp, t); + } + } +Return: + free(fsb); + free(tsb); +} diff --git a/sys/src/cmd/diff/diffio.c b/sys/src/cmd/diff/diffio.c index 1d4d4d6bb..e8f9232a0 100644 --- a/sys/src/cmd/diff/diffio.c +++ b/sys/src/cmd/diff/diffio.c @@ -4,10 +4,6 @@ #include <ctype.h> #include "diff.h" -static Biobuf *input[2]; -static char *file1, *file2; -static int firstchange; - #define MAXLINELEN 4096 #define MIN(x, y) ((x) < (y) ? (x): (y)) @@ -104,7 +100,7 @@ readhash(Biobuf *bp, char *buf) } Biobuf * -prepare(int i, char *arg, char *orig) +prepare(Diff *d, int i, char *arg, char *orig) { Line *p; int j, h; @@ -115,10 +111,10 @@ prepare(int i, char *arg, char *orig) bp = Bopen(arg, OREAD); if (!bp) { - panic(mflag ? 0: 2, "cannot open %s: %r\n", arg); + sysfatal("cannot open %s: %r", arg); return 0; } - if (binary) + if (d->binary) return bp; nbytes = Bread(bp, buf, MIN(1024, MAXLINELEN)); if (nbytes > 0) { @@ -130,7 +126,7 @@ prepare(int i, char *arg, char *orig) */ cp += chartorune(&r, cp); if (r == 0 || (r > 0x7f && r <= 0xa0)) { - binary++; + d->binary++; return bp; } } @@ -139,14 +135,14 @@ prepare(int i, char *arg, char *orig) p = emalloc(3*sizeof(Line)); for (j = 0; h = readhash(bp, buf); p[j].value = h) p = erealloc(p, (++j+3)*sizeof(Line)); - len[i] = j; - file[i] = p; - input[i] = bp; + d->len[i] = j; + d->file[i] = p; + d->input[i] = bp; if (i == 0) { - file1 = orig; - firstchange = 0; + d->file1 = orig; + d->firstchange = 0; } else - file2 = orig; + d->file2 = orig; return bp; } @@ -175,31 +171,32 @@ squishspace(char *buf) * need to fix up for unexpected EOF's */ void -check(Biobuf *bf, Biobuf *bt) +check(Diff *d, Biobuf *bf, Biobuf *bt) { int f, t, flen, tlen; char fbuf[MAXLINELEN], tbuf[MAXLINELEN]; - ixold[0] = ixnew[0] = 0; - for (f = t = 1; f < len[0]; f++) { + d->ixold[0] = 0; + d->ixnew[0] = 0; + for (f = t = 1; f < d->len[0]; f++) { flen = readline(bf, fbuf); - ixold[f] = ixold[f-1] + flen + 1; /* ftell(bf) */ - if (J[f] == 0) + d->ixold[f] = d->ixold[f-1] + flen + 1; /* ftell(bf) */ + if (d->J[f] == 0) continue; do { tlen = readline(bt, tbuf); - ixnew[t] = ixnew[t-1] + tlen + 1; /* ftell(bt) */ - } while (t++ < J[f]); + d->ixnew[t] = d->ixnew[t-1] + tlen + 1; /* ftell(bt) */ + } while (t++ < d->J[f]); if (bflag) { flen = squishspace(fbuf); tlen = squishspace(tbuf); } if (flen != tlen || strcmp(fbuf, tbuf)) - J[f] = 0; + d->J[f] = 0; } - while (t < len[1]) { + while (t < d->len[1]) { tlen = readline(bt, tbuf); - ixnew[t] = ixnew[t-1] + tlen + 1; /* fseek(bt) */ + d->ixnew[t] = d->ixnew[t-1] + tlen + 1; /* fseek(bt) */ t++; } } @@ -212,18 +209,18 @@ range(int a, int b, char *separator) Bprint(&stdout, "%s%d", separator, b); } -static void -fetch(long *f, int a, int b, Biobuf *bp, char *s) +void +fetch(Diff *d, long *f, int a, int b, Biobuf *bp, char *s) { char buf[MAXLINELEN]; int maxb; if(a <= 1) a = 1; - if(bp == input[0]) - maxb = len[0]; + if(bp == d->input[0]) + maxb = d->len[0]; else - maxb = len[1]; + maxb = d->len[1]; if(b > maxb) b = maxb; if(a > maxb) @@ -232,23 +229,12 @@ fetch(long *f, int a, int b, Biobuf *bp, char *s) while (a++ <= b) { readline(bp, buf); Bprint(&stdout, "%s%s\n", s, buf); + Bflush(&stdout); } } -typedef struct Change Change; -struct Change -{ - int a; - int b; - int c; - int d; -}; - -Change *changes; -int nchanges; - void -change(int a, int b, int c, int d) +change(Diff *df, int a, int b, int c, int d) { char verb; char buf[4]; @@ -257,7 +243,7 @@ change(int a, int b, int c, int d) if (a > b && c > d) return; anychange = 1; - if (mflag && firstchange == 0) { + if (mflag && df->firstchange == 0) { if(mode) { buf[0] = '-'; buf[1] = mode; @@ -266,8 +252,8 @@ change(int a, int b, int c, int d) } else { buf[0] = '\0'; } - Bprint(&stdout, "diff %s%s %s\n", buf, file1, file2); - firstchange = 1; + Bprint(&stdout, "diff %s%s %s\n", buf, df->file1, df->file2); + df->firstchange = 1; } verb = a > b ? 'a': c > d ? 'd': 'c'; switch(mode) { @@ -281,10 +267,10 @@ change(int a, int b, int c, int d) range(c, d, ","); break; case 'n': - Bprint(&stdout, "%s:", file1); + Bprint(&stdout, "%s:", df->file1); range(a, b, ","); Bprint(&stdout, " %c ", verb); - Bprint(&stdout, "%s:", file2); + Bprint(&stdout, "%s:", df->file2); range(c, d, ","); break; case 'f': @@ -294,9 +280,9 @@ change(int a, int b, int c, int d) case 'c': case 'a': case 'u': - if(nchanges%1024 == 0) - changes = erealloc(changes, (nchanges+1024)*sizeof(changes[0])); - ch = &changes[nchanges++]; + if(df->nchanges%1024 == 0) + df->changes = erealloc(df->changes, (df->nchanges+1024)*sizeof(df->changes[0])); + ch = &df->changes[df->nchanges++]; ch->a = a; ch->b = b; ch->c = c; @@ -305,11 +291,11 @@ change(int a, int b, int c, int d) } Bputc(&stdout, '\n'); if (mode == 0 || mode == 'n') { - fetch(ixold, a, b, input[0], "< "); + fetch(df, df->ixold, a, b, df->input[0], "< "); if (a <= b && c <= d) Bprint(&stdout, "---\n"); } - fetch(ixnew, c, d, input[1], mode == 0 || mode == 'n' ? "> ": ""); + fetch(df, df->ixnew, c, d, df->input[1], mode == 0 || mode == 'n' ? "> ": ""); if (mode != 0 && mode != 'n' && c <= d) Bprint(&stdout, ".\n"); } @@ -320,69 +306,69 @@ enum }; int -changeset(int i) +changeset(Diff *d, int i) { - while(i<nchanges && changes[i].b+1+2*Lines > changes[i+1].a) + while(i < d->nchanges && d->changes[i].b + 1 + 2*Lines > d->changes[i+1].a) i++; - if(i<nchanges) + if(i < d->nchanges) return i+1; - return nchanges; + return d->nchanges; } void -flushchanges(void) +flushchanges(Diff *df) { - int a, b, c, d, at, hdr; - int i, j; + vlong a, b, c, d, at, hdr; + vlong i, j; - if(nchanges == 0) + if(df->nchanges == 0) return; hdr = 0; - for(i=0; i<nchanges; ){ - j = changeset(i); - a = changes[i].a-Lines; - b = changes[j-1].b+Lines; - c = changes[i].c-Lines; - d = changes[j-1].d+Lines; + for(i=0; i < df->nchanges; ){ + j = changeset(df, i); + a = df->changes[i].a - Lines; + b = df->changes[j-1].b + Lines; + c = df->changes[i].c - Lines; + d = df->changes[j-1].d + Lines; if(a < 1) a = 1; if(c < 1) c = 1; - if(b > len[0]) - b = len[0]; - if(d > len[1]) - d = len[1]; + if(b > df->len[0]) + b = df->len[0]; + if(d > df->len[1]) + d = df->len[1]; if(mode == 'a'){ a = 1; - b = len[0]; + b = df->len[0]; c = 1; - d = len[1]; - j = nchanges; + d = df->len[1]; + j = df->nchanges; } if(mode == 'u'){ if(!hdr){ - Bprint(&stdout, "--- %s\n", file1); - Bprint(&stdout, "+++ %s\n", file2); + Bprint(&stdout, "--- %s\n", df->file1); + Bprint(&stdout, "+++ %s\n", df->file2); hdr = 1; } - Bprint(&stdout, "@@ -%d,%d +%d,%d @@\n", a, b-a+1, c, d-c+1); + Bprint(&stdout, "@@ -%lld,%lld +%lld,%lld @@\n", a, b-a+1, c, d-c+1); }else{ - Bprint(&stdout, "%s:", file1); + Bprint(&stdout, "%s:", df->file1); range(a, b, ","); Bprint(&stdout, " - "); - Bprint(&stdout, "%s:", file2); + Bprint(&stdout, "%s:", df->file2); range(c, d, ","); Bputc(&stdout, '\n'); } at = a; for(; i<j; i++){ - fetch(ixold, at, changes[i].a-1, input[0], mode == 'u' ? " " : " "); - fetch(ixold, changes[i].a, changes[i].b, input[0], mode == 'u' ? "-" : "- "); - fetch(ixnew, changes[i].c, changes[i].d, input[1], mode == 'u' ? "+" : "+ "); - at = changes[i].b+1; + fetch(df, df->ixold, at, df->changes[i].a-1, df->input[0], mode == 'u' ? " " : " "); + fetch(df, df->ixold, df->changes[i].a, df->changes[i].b, df->input[0], mode == 'u' ? "-" : "- "); + fetch(df, df->ixnew, df->changes[i].c, df->changes[i].d, df->input[1], mode == 'u' ? "+" : "+ "); + at = df->changes[i].b+1; } - fetch(ixold, at, b, input[0], mode == 'u' ? " " : " "); + fetch(df, df->ixold, at, b, df->input[0], mode == 'u' ? " " : " "); } - nchanges = 0; + df->nchanges = 0; } diff --git a/sys/src/cmd/diff/diffreg.c b/sys/src/cmd/diff/diffreg.c index e37ffa7a3..a89a4ef26 100644 --- a/sys/src/cmd/diff/diffreg.c +++ b/sys/src/cmd/diff/diffreg.c @@ -66,29 +66,6 @@ * 3*(number of k-candidates installed), typically about * 6n words for files of length n. */ -typedef struct Cand Cand; - -struct Cand { - int x; - int y; - int pred; -}; - -Cand cand; -Line *file[2], line; -int len[2]; -int binary; -Line *sfile[2]; /*shortened by pruning common prefix and suffix*/ -int slen[2]; -int pref, suff; /*length of prefix and suffix*/ -int *class; /*will be overlaid on file[0]*/ -int *member; /*will be overlaid on file[1]*/ -int *klist; /*will be overlaid on file[0] after class*/ -Cand *clist; /* merely a free storage pot for candidates */ -int clen; -int *J; /*will be overlaid on class*/ -long *ixold; /*will be overlaid on klist*/ -long *ixnew; /*will be overlaid on file[1]*/ static void sort(Line *a, int n) /*shellsort CACM #201*/ @@ -137,21 +114,21 @@ unsort(Line *f, int l, int *b) } static void -prune(void) +prune(Diff *d) { int i,j; - for(pref=0;pref<len[0]&&pref<len[1]&& - file[0][pref+1].value==file[1][pref+1].value; - pref++ ) ; - for(suff=0;suff<len[0]-pref&&suff<len[1]-pref&& - file[0][len[0]-suff].value==file[1][len[1]-suff].value; - suff++) ; + for(d->pref = 0; d->pref < d->len[0] && d->pref < d->len[1] && + d->file[0][d->pref+1].value == d->file[1][d->pref+1].value; + d->pref++) ; + for(d->suff=0; d->suff < d->len[0] - d->pref && d->suff < d->len[1] - d->pref && + d->file[0][d->len[0] - d->suff].value == d->file[1][d->len[1] - d->suff].value; + d->suff++) ; for(j=0;j<2;j++) { - sfile[j] = file[j]+pref; - slen[j] = len[j]-pref-suff; - for(i=0;i<=slen[j];i++) - sfile[j][i].serial = i; + d->sfile[j] = d->file[j]+d->pref; + d->slen[j] = d->len[j]-d->pref-d->suff; + for(i=0;i<=d->slen[j];i++) + d->sfile[j][i].serial = i; } } @@ -184,30 +161,30 @@ equiv(Line *a, int n, Line *b, int m, int *c) } static int -newcand(int x, int y, int pred) +newcand(Diff *d, int x, int y, int pred) { Cand *q; - clist = erealloc(clist, (clen+1)*sizeof(Cand)); - q = clist + clen; + d->clist = erealloc(d->clist, (d->clen+1)*sizeof(Cand)); + q = d->clist + d->clen; q->x = x; q->y = y; q->pred = pred; - return clen++; + return d->clen++; } static int -search(int *c, int k, int y) +search(Diff *d, int *c, int k, int y) { int i, j, l; int t; - if(clist[c[k]].y < y) /*quick look for typical case*/ + if(d->clist[c[k]].y < y) /*quick look for typical case*/ return k+1; i = 0; j = k+1; while((l=(i+j)/2) > i) { - t = clist[c[l]].y; + t = d->clist[c[l]].y; if(t > y) j = l; else if(t < y) @@ -219,7 +196,7 @@ search(int *c, int k, int y) } static int -stone(int *a, int n, int *b, int *c) +stone(Diff *d, int *a, int n, int *b, int *c) { int i, k,y; int j, l; @@ -227,7 +204,7 @@ stone(int *a, int n, int *b, int *c) int oldl; k = 0; - c[0] = newcand(0,0,0); + c[0] = newcand(d, 0, 0, 0); for(i=1; i<=n; i++) { j = a[i]; if(j==0) @@ -236,20 +213,20 @@ stone(int *a, int n, int *b, int *c) oldl = 0; oldc = c[0]; do { - if(y <= clist[oldc].y) + if(y <= d->clist[oldc].y) continue; - l = search(c, k, y); + l = search(d, c, k, y); if(l!=oldl+1) oldc = c[l-1]; if(l<=k) { - if(clist[c[l]].y <= y) + if(d->clist[c[l]].y <= y) continue; tc = c[l]; - c[l] = newcand(i,y,oldc); + c[l] = newcand(d, i, y, oldc); oldc = tc; oldl = l; } else { - c[l] = newcand(i,y,oldc); + c[l] = newcand(d, i,y,oldc); k++; break; } @@ -259,59 +236,21 @@ stone(int *a, int n, int *b, int *c) } static void -unravel(int p) +unravel(Diff *d, int p) { int i; Cand *q; - for(i=0; i<=len[0]; i++) { - if (i <= pref) - J[i] = i; - else if (i > len[0]-suff) - J[i] = i+len[1]-len[0]; + for(i=0; i<=d->len[0]; i++) { + if (i <= d->pref) + d->J[i] = i; + else if (i > d->len[0]-d->suff) + d->J[i] = i+d->len[1] - d->len[0]; else - J[i] = 0; + d->J[i] = 0; } - for(q=clist+p;q->y!=0;q=clist+q->pred) - J[q->x+pref] = q->y+pref; -} - -static void -output(void) -{ - int m, i0, i1, j0, j1; - - m = len[0]; - J[0] = 0; - J[m+1] = len[1]+1; - if (mode != 'e') { - for (i0 = 1; i0 <= m; i0 = i1+1) { - while (i0 <= m && J[i0] == J[i0-1]+1) - i0++; - j0 = J[i0-1]+1; - i1 = i0-1; - while (i1 < m && J[i1+1] == 0) - i1++; - j1 = J[i1+1]-1; - J[i1] = j1; - change(i0, i1, j0, j1); - } - } else { - for (i0 = m; i0 >= 1; i0 = i1-1) { - while (i0 >= 1 && J[i0] == J[i0+1]-1 && J[i0]) - i0--; - j0 = J[i0+1]-1; - i1 = i0+1; - while (i1 > 1 && J[i1-1] == 0) - i1--; - j1 = J[i1-1]+1; - J[i1] = j1; - change(i1 , i0, j1, j0); - } - } - if (m == 0) - change(1, 0, 1, len[1]); - flushchanges(); + for(q=d->clist+p; q->y != 0; q= d->clist + q->pred) + d->J[q->x+d->pref] = q->y+d->pref; } #define BUF 4096 @@ -361,21 +300,20 @@ cmp(Biobuf* b1, Biobuf* b2) } void -diffreg(char *f, char *fo, char *t, char *to) +calcdiff(Diff *d, char *f, char *fo, char *t, char *to) { Biobuf *b0, *b1; int k; - binary = 0; - b0 = prepare(0, f, fo); + b0 = prepare(d, 0, f, fo); if (!b0) return; - b1 = prepare(1, t, to); + b1 = prepare(d, 1, t, to); if (!b1) { Bterm(b0); return; } - if (binary){ + if (d->binary){ // could use b0 and b1 but this is simpler. if (cmp(b0, b1)) print("binary files %s %s differ\n", f, t); @@ -383,38 +321,91 @@ diffreg(char *f, char *fo, char *t, char *to) Bterm(b1); return; } - clen = 0; - prune(); - sort(sfile[0], slen[0]); - sort(sfile[1], slen[1]); - - member = (int *)file[1]; - equiv(sfile[0], slen[0], sfile[1], slen[1], member); - member = erealloc(member, (slen[1]+2)*sizeof(int)); - - class = (int *)file[0]; - unsort(sfile[0], slen[0], class); - class = erealloc(class, (slen[0]+2)*sizeof(int)); - - klist = emalloc((slen[0]+2)*sizeof(int)); - clist = emalloc(sizeof(Cand)); - k = stone(class, slen[0], member, klist); - free(member); - free(class); - - J = emalloc((len[0]+2)*sizeof(int)); - unravel(klist[k]); - free(clist); - free(klist); - - ixold = emalloc((len[0]+2)*sizeof(long)); - ixnew = emalloc((len[1]+2)*sizeof(long)); + d->clen = 0; + prune(d); + sort(d->sfile[0], d->slen[0]); + sort(d->sfile[1], d->slen[1]); + + d->member = (int *)d->file[1]; + equiv(d->sfile[0], d->slen[0], d->sfile[1], d->slen[1], d->member); + d->member = erealloc(d->member, (d->slen[1]+2)*sizeof(int)); + + d->class = (int *)d->file[0]; + unsort(d->sfile[0], d->slen[0], d->class); + d->class = erealloc(d->class, (d->slen[0]+2)*sizeof(int)); + + d->klist = emalloc((d->slen[0]+2)*sizeof(int)); + d->clist = emalloc(sizeof(Cand)); + k = stone(d, d->class, d->slen[0], d->member, d->klist); + free(d->member); + free(d->class); + + d->J = emalloc((d->len[0]+2)*sizeof(int)); + unravel(d, d->klist[k]); + free(d->clist); + free(d->klist); + + d->ixold = emalloc((d->len[0]+2)*sizeof(long)); + d->ixnew = emalloc((d->len[1]+2)*sizeof(long)); Bseek(b0, 0, 0); Bseek(b1, 0, 0); - check(b0, b1); - output(); - free(J); - free(ixold); - free(ixnew); - Bterm(b0); - Bterm(b1); + check(d, b0, b1); +} + +static void +output(Diff *d) +{ + int m, i0, i1, j0, j1; + + m = d->len[0]; + d->J[0] = 0; + d->J[m+1] = d->len[1]+1; + if (mode != 'e') { + for (i0 = 1; i0 <= m; i0 = i1+1) { + while (i0 <= m && d->J[i0] == d->J[i0-1]+1) + i0++; + j0 = d->J[i0-1]+1; + i1 = i0-1; + while (i1 < m && d->J[i1+1] == 0) + i1++; + j1 = d->J[i1+1]-1; + d->J[i1] = j1; + change(d, i0, i1, j0, j1); + } + } else { + for (i0 = m; i0 >= 1; i0 = i1-1) { + while (i0 >= 1 && d->J[i0] == d->J[i0+1]-1 && d->J[i0]) + i0--; + j0 = d->J[i0+1]-1; + i1 = i0+1; + while (i1 > 1 && d->J[i1-1] == 0) + i1--; + j1 = d->J[i1-1]+1; + d->J[i1] = j1; + change(d, i1 , i0, j1, j0); + } + } + if (m == 0) + change(d, 1, 0, 1, d->len[1]); + flushchanges(d); +} + +void +diffreg(char *f, char *fo, char *t, char *to) +{ + Diff d; + + memset(&d, 0, sizeof(d)); + calcdiff(&d, f, fo, t, to); + output(&d); + freediff(&d); +} + +void +freediff(Diff *d) +{ + Bterm(d->input[0]); + Bterm(d->input[1]); + free(d->J); + free(d->ixold); + free(d->ixnew); } diff --git a/sys/src/cmd/diff/main.c b/sys/src/cmd/diff/main.c deleted file mode 100644 index 385537207..000000000 --- a/sys/src/cmd/diff/main.c +++ /dev/null @@ -1,261 +0,0 @@ -#include <u.h> -#include <libc.h> -#include <bio.h> -#include "diff.h" - -#define DIRECTORY(s) ((s)->qid.type&QTDIR) -#define REGULAR_FILE(s) ((s)->type == 'M' && !DIRECTORY(s)) - -Biobuf stdout; -char mode; /* '\0', 'e', 'f', 'h' */ -char bflag; /* ignore multiple and trailing blanks */ -char rflag; /* recurse down directory trees */ -char mflag; /* pseudo flag: doing multiple files, one dir */ -int anychange; - -static char *tmp[] = {"/tmp/diff1XXXXXXXXXXX", "/tmp/diff2XXXXXXXXXXX"}; -static int whichtmp; - -static void -rmtmpfiles(void) -{ - while (whichtmp > 0) { - whichtmp--; - remove(tmp[whichtmp]); - } -} - -void -done(int status) -{ - rmtmpfiles(); - switch(status) - { - case 0: - exits(""); - case 1: - exits("some"); - default: - exits("error"); - } - /*NOTREACHED*/ -} - -void -panic(int status, char *fmt, ...) -{ - va_list arg; - - Bflush(&stdout); - - fprint(2, "%s: ", argv0); - va_start(arg, fmt); - vfprint(2, fmt, arg); - va_end(arg); - if (status) - done(status); - /*NOTREACHED*/ -} - -static int -catch(void *a, char *msg) -{ - USED(a); - panic(2, msg); - return 1; -} - -int -mkpathname(char *pathname, char *path, char *name) -{ - if (strlen(path) + strlen(name) > MAXPATHLEN) { - panic(0, "pathname %s/%s too long\n", path, name); - return 1; - } - sprint(pathname, "%s/%s", path, name); - return 0; -} - -static char * -mktmpfile(int input, Dir **sb) -{ - int fd, i; - char *p; - char buf[8192]; - - atnotify(catch, 1); - p = mktemp(tmp[whichtmp++]); - fd = create(p, OWRITE, 0600); - if (fd < 0) { - panic(mflag ? 0: 2, "cannot create %s: %r\n", p); - return 0; - } - while ((i = read(input, buf, sizeof(buf))) > 0) { - if ((i = write(fd, buf, i)) < 0) - break; - } - *sb = dirfstat(fd); - close(fd); - if (i < 0) { - panic(mflag ? 0: 2, "cannot read/write %s: %r\n", p); - return 0; - } - return p; -} - -static char * -statfile(char *file, Dir **sb) -{ - Dir *dir; - int input; - - dir = dirstat(file); - if(dir == nil) { - if (strcmp(file, "-") || (dir = dirfstat(0)) == nil) { - panic(mflag ? 0: 2, "cannot stat %s: %r\n", file); - return 0; - } - free(dir); - return mktmpfile(0, sb); - } else if (!REGULAR_FILE(dir) && !DIRECTORY(dir)) { - free(dir); - if ((input = open(file, OREAD)) == -1) { - panic(mflag ? 0: 2, "cannot open %s: %r\n", file); - return 0; - } - file = mktmpfile(input, sb); - close(input); - } else - *sb = dir; - return file; -} - -void -diff(char *f, char *t, int level) -{ - char *fp, *tp, *p, fb[MAXPATHLEN+1], tb[MAXPATHLEN+1]; - Dir *fsb, *tsb; - - if ((fp = statfile(f, &fsb)) == 0) - goto Return; - if ((tp = statfile(t, &tsb)) == 0){ - free(fsb); - goto Return; - } - if (DIRECTORY(fsb) && DIRECTORY(tsb)) { - if (rflag || level == 0) - diffdir(fp, tp, level); - else - Bprint(&stdout, "Common subdirectories: %s and %s\n", fp, tp); - } - else if (REGULAR_FILE(fsb) && REGULAR_FILE(tsb)) - diffreg(fp, f, tp, t); - else { - if (REGULAR_FILE(fsb)) { - if ((p = utfrrune(f, '/')) == 0) - p = f; - else - p++; - if (mkpathname(tb, tp, p) == 0) - diffreg(fp, f, tb, t); - } else { - if ((p = utfrrune(t, '/')) == 0) - p = t; - else - p++; - if (mkpathname(fb, fp, p) == 0) - diffreg(fb, f, tp, t); - } - } - free(fsb); - free(tsb); -Return: - rmtmpfiles(); -} - -void -usage(void) -{ - fprint(2, "usage: %s [-abcefmnrw] file1 ... file2\n", argv0); - exits("usage"); -} - -void -main(int argc, char *argv[]) -{ - int i; - Dir *fsb, *tsb; - - Binit(&stdout, 1, OWRITE); - ARGBEGIN{ - case 'e': - case 'f': - case 'n': - case 'c': - case 'a': - case 'u': - mode = ARGC(); - break; - case 'w': - bflag = 2; - break; - - case 'b': - bflag = 1; - break; - - case 'r': - rflag = 1; - break; - - case 'm': - mflag = 1; - break; - - case 'h': - default: - usage(); - }ARGEND; - if (argc < 2) - usage(); - if ((tsb = dirstat(argv[argc-1])) == nil) - panic(2, "can't stat %s\n", argv[argc-1]); - if (argc > 2) { - if (!DIRECTORY(tsb)) - panic(2, "not directory: %s", argv[argc-1]); - mflag = 1; - } else { - if ((fsb = dirstat(argv[0])) == nil) - panic(2, "can't stat %s\n", argv[0]); - if (DIRECTORY(fsb) && DIRECTORY(tsb)) - mflag = 1; - free(fsb); - } - free(tsb); - for (i = 0; i < argc-1; i++) - diff(argv[i], argv[argc-1], 0); - done(anychange); - /*NOTREACHED*/ -} - -static char noroom[] = "out of memory - try diff -h\n"; - -void * -emalloc(unsigned n) -{ - register void *p; - - if ((p = malloc(n)) == 0) - panic(2, noroom); - return p; -} - -void * -erealloc(void *p, unsigned n) -{ - void *rp; - - if ((rp = realloc(p, n)) == 0) - panic(2, noroom); - return rp; -} diff --git a/sys/src/cmd/diff/merge3.c b/sys/src/cmd/diff/merge3.c new file mode 100644 index 000000000..08090e620 --- /dev/null +++ b/sys/src/cmd/diff/merge3.c @@ -0,0 +1,169 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include "diff.h" + +static int +changecmp(void *a, void *b) +{ + return ((Change*)a)->a - ((Change*)b)->a; +} + +static void +addchange(Diff *df, int a, int b, int c, int d) +{ + Change *ch; + + if (a > b && c > d) + return; + if(df->nchanges%1024 == 0) + df->changes = erealloc(df->changes, (df->nchanges+1024)*sizeof(df->changes[0])); + ch = &df->changes[df->nchanges++]; + ch->a = a; + ch->b = b; + ch->c = c; + ch->d = d; +} + +static void +collect(Diff *d) +{ + int m, i0, i1, j0, j1; + + m = d->len[0]; + d->J[0] = 0; + d->J[m+1] = d->len[1]+1; + for (i0 = m; i0 >= 1; i0 = i1-1) { + while (i0 >= 1 && d->J[i0] == d->J[i0+1]-1 && d->J[i0]) + i0--; + j0 = d->J[i0+1]-1; + i1 = i0+1; + while (i1 > 1 && d->J[i1-1] == 0) + i1--; + j1 = d->J[i1-1]+1; + d->J[i1] = j1; + addchange(d, i1 , i0, j1, j0); + } + if (m == 0) + change(d, 1, 0, 1, d->len[1]); + qsort(d->changes, d->nchanges, sizeof(Change), changecmp); +} + +static int +overlaps(Change *l, Change *r) +{ + if(l == nil || r == nil) + return 0; + if(l->a <= r->a) + return l->b >= r->a; + else + return r->b >= l->a; +} + +char* +merge(Diff *l, Diff *r) +{ + int il, ir, x, y, δ; + Change *lc, *rc; + char *status; + vlong ln; + + il = 0; + ir = 0; + ln = 0; + status = nil; + collect(l); + collect(r); + while(il < l->nchanges || ir < r->nchanges){ + lc = nil; + rc = nil; + if(il < l->nchanges) + lc = &l->changes[il]; + if(ir < r->nchanges) + rc = &r->changes[ir]; + if(overlaps(lc, rc)){ + /* + * align the edges of the chunks + */ + if(lc->a < rc->a){ + x = lc->c; + δ = rc->a - lc->a; + rc->a -= δ; + rc->c -= δ; + }else{ + x = rc->c; + δ = lc->a - rc->a; + lc->a -= δ; + lc->c -= δ; + } + if(lc->b > rc->b){ + y = lc->d; + δ = lc->b - rc->b; + rc->b += δ; + rc->d += δ; + }else{ + y = rc->d; + δ = rc->b - lc->b; + lc->b += δ; + lc->d += δ; + } + fetch(l, l->ixold, ln, x-1, l->input[0], ""); + Bprint(&stdout, "<<<<<<<<<< %s\n", l->file2); + fetch(l, l->ixnew, lc->c, lc->d, l->input[1], ""); + Bprint(&stdout, "========== original\n"); + fetch(l, l->ixold, x, y, l->input[0], ""); + Bprint(&stdout, "========== %s\n", r->file2); + fetch(r, r->ixnew, rc->c, rc->d, r->input[1], ""); + Bprint(&stdout, ">>>>>>>>>>\n"); + ln = y+1; + il++; + ir++; + status = "conflict"; + }else if(rc == nil || (lc != nil && lc->a < rc->a)){ + fetch(l, l->ixold, ln, lc->a-1, l->input[0], ""); + fetch(l, l->ixnew, lc->c, lc->d, l->input[1], ""); + ln = lc->b+1; + il++; + }else if(lc == nil || (rc != nil && rc->a < lc->a)){ + fetch(l, l->ixold, ln, rc->a-1, l->input[0], ""); + fetch(r, r->ixnew, rc->c, rc->d, r->input[1], ""); + ln = rc->b+1; + ir++; + }else + abort(); + } + if(ln < l->len[0]) + fetch(l, l->ixold, ln, l->len[0], l->input[0], ""); + return status; +} + +void +usage(void) +{ + fprint(2, "usage: %s theirs base ours\n", argv0); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + Diff l, r; + char *x; + + ARGBEGIN{ + default: + usage(); + }ARGEND; + + if(argc != 3) + usage(); + Binit(&stdout, 1, OWRITE); + memset(&l, 0, sizeof(l)); + memset(&r, 0, sizeof(r)); + calcdiff(&l, argv[1], argv[1], argv[0], argv[0]); + calcdiff(&r, argv[1], argv[1], argv[2], argv[2]); + x = merge(&l, &r); + freediff(&l); + freediff(&r); + exits(x); +} diff --git a/sys/src/cmd/diff/mkfile b/sys/src/cmd/diff/mkfile index 21f333807..521a7fa06 100644 --- a/sys/src/cmd/diff/mkfile +++ b/sys/src/cmd/diff/mkfile @@ -1,13 +1,13 @@ < /$objtype/mkfile -TARG=diff +TARG=diff merge3 OFILES=\ diffdir.$O\ diffio.$O\ diffreg.$O\ - main.$O\ + util.$O HFILES=diff.h BIN=/$objtype/bin -</sys/src/cmd/mkone +</sys/src/cmd/mkmany diff --git a/sys/src/cmd/diff/test/diff-t1.1 b/sys/src/cmd/diff/test/diff-t1.1 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t1.1 diff --git a/sys/src/cmd/diff/test/diff-t1.2 b/sys/src/cmd/diff/test/diff-t1.2 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t1.2 diff --git a/sys/src/cmd/diff/test/diff-t1.expected b/sys/src/cmd/diff/test/diff-t1.expected new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t1.expected diff --git a/sys/src/cmd/diff/test/diff-t10.1 b/sys/src/cmd/diff/test/diff-t10.1 new file mode 100644 index 000000000..f0e624272 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t10.1 @@ -0,0 +1 @@ +a line of text
\ No newline at end of file diff --git a/sys/src/cmd/diff/test/diff-t10.2 b/sys/src/cmd/diff/test/diff-t10.2 new file mode 100644 index 000000000..03f42c690 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t10.2 @@ -0,0 +1 @@ +Another line of text
\ No newline at end of file diff --git a/sys/src/cmd/diff/test/diff-t10.expected b/sys/src/cmd/diff/test/diff-t10.expected new file mode 100644 index 000000000..1a1de7cb7 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t10.expected @@ -0,0 +1,5 @@ +--- diff-t10.1 ++++ diff-t10.2 +@@ -1 +1 @@ +-a line of text ++Another line of text diff --git a/sys/src/cmd/diff/test/diff-t11.1 b/sys/src/cmd/diff/test/diff-t11.1 new file mode 100644 index 000000000..6b95cd93d --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t11.1 @@ -0,0 +1,1003 @@ +.\" $OpenBSD: t11.1,v 1.2 2007/11/27 16:22:12 martynas Exp $ +.\" $NetBSD: ed.1,v 1.13 1995/03/21 09:04:38 cgd Exp $ +.\" +.TH ED 1 "21 May 1993" +.SH NAME +.\" ed, red \- text editor +ed \- text editor +.SH SYNOPSIS +ed [-] [-sx] [-p \fIstring\fR] [\fIfile\fR] +.\" .LP +.\" red [-] [-sx] [-p \fIstring\fR] [\fIfile\fR] +.SH DESCRIPTION +.B ed +is a line-oriented text editor. +It is used to create, display, modify and otherwise manipulate text +files. +.\" .B red +.\" is a restricted +.\" .BR ed : +.\" it can only edit files in the current +.\" directory and cannot execute shell commands. + +If invoked with a +.I file +argument, then a copy of +.I file +is read into the editor's buffer. +Changes are made to this copy and not directly to +.I file +itself. +Upon quitting +.BR ed , +any changes not explicitly saved with a +.I `w' +command are lost. + +Editing is done in two distinct modes: +.I command +and +.IR input . +When first invoked, +.B ed +is in command mode. +In this mode commands are read from the standard input and +executed to manipulate the contents of the editor buffer. +A typical command might look like: +.sp +.RS +,s/\fIold\fR/\fInew\fR/g +.RE +.sp +which replaces all occurrences of the string +.I old +with +.IR new . + +When an input command, such as +.I `a' +(append), +.I `i' +(insert) or +.I `c' +(change), is given, +.B ed +enters input mode. This is the primary means +of adding text to a file. +In this mode, no commands are available; +instead, the standard input is written +directly to the editor buffer. Lines consist of text up to and +including a +.IR newline +character. +Input mode is terminated by +entering a single period (\fI.\fR) on a line. + +All +.B ed +commands operate on whole lines or ranges of lines; e.g., +the +.I `d' +command deletes lines; the +.I `m' +command moves lines, and so on. +It is possible to modify only a portion of a line by means of replacement, +as in the example above. However even here, the +.I `s' +command is applied to whole lines at a time. + +In general, +.B ed +commands consist of zero or more line addresses, followed by a single +character command and possibly additional parameters; i.e., +commands have the structure: +.sp +.RS +.I [address [,address]]command[parameters] +.RE +.sp +The address(es) indicate the line or range of lines to be affected by the +command. If fewer addresses are given than the command accepts, then +default addresses are supplied. + +.SS OPTIONS +.TP 8 +-s +Suppresses diagnostics. This should be used if +.BR ed 's +standard input is from a script. + +.TP 8 +-x +Prompts for an encryption key to be used in subsequent reads and writes +(see the +.I `x' +command). + +.TP 8 +.RI \-p \ string +Specifies a command prompt. This may be toggled on and off with the +.I `P' +command. + +.TP 8 +.I file +Specifies the name of a file to read. If +.I file +is prefixed with a +bang (!), then it is interpreted as a shell command. In this case, +what is read is +the standard output of +.I file +executed via +.IR sh (1). +To read a file whose name begins with a bang, prefix the +name with a backslash (\\). +The default filename is set to +.I file +only if it is not prefixed with a bang. + +.SS LINE ADDRESSING +An address represents the number of a line in the buffer. +.B ed +maintains a +.I current address +which is +typically supplied to commands as the default address when none is specified. +When a file is first read, the current address is set to the last line +of the file. In general, the current address is set to the last line +affected by a command. + +A line address is +constructed from one of the bases in the list below, optionally followed +by a numeric offset. The offset may include any combination +of digits, operators (i.e., +.IR + , +.I - +and +.IR ^ ) +and whitespace. +Addresses are read from left to right, and their values are computed +relative to the current address. + +One exception to the rule that addresses represent line numbers is the +address +.I 0 +(zero). +This means "before the first line," +and is legal wherever it makes sense. + +An address range is two addresses separated either by a comma or +semi-colon. The value of the first address in a range cannot exceed the +value of the second. If only one address is given in a range, then +the second address is set to the given address. If an +.IR n- tuple +of addresses is given where +.I n > 2, +then the corresponding range is determined by the last two addresses in +the +.IR n- tuple. +If only one address is expected, then the last address is used. + +Each address in a comma-delimited range is interpreted relative to the +current address. In a semi-colon-delimited range, the first address is +used to set the current address, and the second address is interpreted +relative to the first. + + +The following address symbols are recognized. + +.TP 8 +\&. +The current line (address) in the buffer. + +.TP 8 +$ +The last line in the buffer. + +.TP 8 +n +The +.IR n th, +line in the buffer +where +.I n +is a number in the range +.I [0,$]. + +.TP 8 +- or ^ +The previous line. +This is equivalent to +.I -1 +and may be repeated with cumulative effect. + +.TP 8 +-\fIn\fR or ^\fIn\fR +The +.IR n th +previous line, where +.I n +is a non-negative number. + +.TP 8 ++ +The +next line. +This is equivalent to +.I +1 +and may be repeated with cumulative effect. + +.TP 8 ++\fIn\fR or whitespace\fIn\fR +The +.IR n th +next line, where +.I n +is a non-negative number. +.I whitespace +followed by a number +.I n +is interpreted as +.IR +n . + +.TP 8 +, \fRor\fB % +The first through last lines in the buffer. This is equivalent to +the address range +.I 1,$. + +.TP 8 +; +The +current through last lines in the buffer. This is equivalent to +the address range +.I .,$. + +.TP 8 +.RI / re/ +The +next line containing the regular expression +.IR re . +The search wraps to the beginning of the buffer and continues down to the +current line, if necessary. +// repeats the last search. + +.TP 8 +.RI ? re? +The +previous line containing the regular expression +.IR re . +The search wraps to the end of the buffer and continues up to the +current line, if necessary. +?? repeats the last search. + +.TP 8 +.RI \' lc +The +line previously marked by a +.I `k' +(mark) command, where +.I lc +is a lower case letter. + +.SS REGULAR EXPRESSIONS +Regular expressions are patterns used in selecting text. +For example, the +.B ed +command +.sp +.RS +g/\fIstring\fR/ +.RE +.sp +prints all lines containing +.IR string . +Regular expressions are also +used by the +.I `s' +command for selecting old text to be replaced with new. + +In addition to a specifying string literals, regular expressions can +represent +classes of strings. Strings thus represented are said to be matched +by the corresponding regular expression. +If it is possible for a regular expression +to match several strings in a line, then the left-most longest match is +the one selected. + +The following symbols are used in constructing regular expressions: + +.TP 8 +c +Any character +.I c +not listed below, including `{', '}', `(', `)', `<' and `>', +matches itself. + +.TP 8 +\fR\e\fIc\fR +Any backslash-escaped character +.IR c , +except for `{', '}', `(', `)', `<' and `>', +matches itself. + +.TP 8 +\fR.\fR +Matches any single character. + +.TP 8 +.I [char-class] +Matches any single character in +.IR char-class . +To include a `]' +in +.IR char-class , +it must be the first character. +A range of characters may be specified by separating the end characters +of the range with a `-', e.g., `a-z' specifies the lower case characters. +The following literal expressions can also be used in +.I char-class +to specify sets of characters: +.sp +\ \ [:alnum:]\ \ [:cntrl:]\ \ [:lower:]\ \ [:space:] +.PD 0 +\ \ [:alpha:]\ \ [:digit:]\ \ [:print:]\ \ [:upper:] +.PD 0 +\ \ [:blank:]\ \ [:graph:]\ \ [:punct:]\ \ [:xdigit:] +.sp +If `-' appears as the first or last +character of +.IR char-class , +then it matches itself. +All other characters in +.I char-class +match themselves. +.sp +Patterns in +.I char-class +of the form: +.sp +\ \ [.\fIcol-elm\fR.] or, +.PD 0 +\ \ [=\fIcol-elm\fR=] +.sp +where +.I col-elm +is a +.I collating element +are interpreted according to +.IR locale (5) +(not currently supported). +See +.IR regex (3) +for an explanation of these constructs. + +.TP 8 +[^\fIchar-class\fR] +Matches any single character, other than newline, not in +.IR char-class . +.IR char-class +is defined +as above. + +.TP 8 +^ +If `^' is the first character of a regular expression, then it +anchors the regular expression to the beginning of a line. +Otherwise, it matches itself. + +.TP 8 +$ +If `$' is the last character of a regular expression, it +anchors the regular expression to the end of a line. +Otherwise, it matches itself. + +.TP 8 +\fR\e<\fR +Anchors the single character regular expression or subexpression +immediately following it to the beginning of a word. +(This may not be available) + +.TP 8 +\fR\e>\fR +Anchors the single character regular expression or subexpression +immediately following it to the end of a word. +(This may not be available) + +.TP 8 +\fR\e(\fIre\fR\e)\fR +Defines a subexpression +.IR re . +Subexpressions may be nested. +A subsequent backreference of the form \fI`\en'\fR, where +.I n +is a number in the range [1,9], expands to the text matched by the +.IR n th +subexpression. +For example, the regular expression `\e(.*\e)\e1' matches any string +consisting of identical adjacent substrings. +Subexpressions are ordered relative to +their left delimiter. + +.TP 8 +* +Matches the single character regular expression or subexpression +immediately preceding it zero or more times. If '*' is the first +character of a regular expression or subexpression, then it matches +itself. The `*' operator sometimes yields unexpected results. +For example, the regular expression `b*' matches the beginning of +the string `abbb' (as opposed to the substring `bbb'), since a null match +is the only left-most match. + +.TP 8 +\fR\e{\fIn,m\fR\e}\fR or \fR\e{\fIn,\fR\e}\fR or \fR\e{\fIn\fR\e}\fR +Matches the single character regular expression or subexpression +immediately preceding it at least +.I n +and at most +.I m +times. +If +.I m +is omitted, then it matches at least +.I n +times. +If the comma is also omitted, then it matches exactly +.I n +times. + +.LP +Additional regular expression operators may be defined depending on the +particular +.IR regex (3) +implementation. + +.SS COMMANDS +All +.B ed +commands are single characters, though some require additonal parameters. +If a command's parameters extend over several lines, then +each line except for the last +must be terminated with a backslash (\\). + +In general, at most one command is allowed per line. +However, most commands accept a print suffix, which is any of +.I `p' +(print), +.I `l' +(list) , +or +.I `n' +(enumerate), +to print the last line affected by the command. + +An interrupt (typically ^C) has the effect of aborting the current command +and returning the editor to command mode. + +.B ed +recognizes the following commands. The commands are shown together with +the default address or address range supplied if none is +specified (in parenthesis). + +.TP 8 +(.)a +Appends text to the buffer after the addressed line. +Text is entered in input mode. +The current address is set to last line entered. + +.TP 8 +(.,.)c +Changes lines in the buffer. The addressed lines are deleted +from the buffer, and text is appended in their place. +Text is entered in input mode. +The current address is set to last line entered. + +.TP 8 +(.,.)d +Deletes the addressed lines from the buffer. +If there is a line after the deleted range, then the current address is set +to this line. Otherwise the current address is set to the line +before the deleted range. + +.TP 8 +.RI e \ file +Edits +.IR file , +and sets the default filename. +If +.I file +is not specified, then the default filename is used. +Any lines in the buffer are deleted before +the new file is read. +The current address is set to the last line read. + +.TP 8 +.RI e \ !command +Edits the standard output of +.IR `!command' , +(see +.RI ! command +below). +The default filename is unchanged. +Any lines in the buffer are deleted before the output of +.I command +is read. +The current address is set to the last line read. + +.TP 8 +.RI E \ file +Edits +.I file +unconditionally. +This is similar to the +.I e +command, +except that unwritten changes are discarded without warning. +The current address is set to the last line read. + +.TP 8 +.RI f \ file +Sets the default filename to +.IR file . +If +.I file +is not specified, then the default unescaped filename is printed. + +.TP 8 +.RI (1,$)g /re/command-list +Applies +.I command-list +to each of the addressed lines matching a regular expression +.IR re . +The current address is set to the +line currently matched before +.I command-list +is executed. +At the end of the +.I `g' +command, the current address is set to the last line affected by +.IR command-list . + +Each command in +.I command-list +must be on a separate line, +and every line except for the last must be terminated by a backslash +(\\). +Any commands are allowed, except for +.IR `g' , +.IR `G' , +.IR `v' , +and +.IR `V' . +A newline alone in +.I command-list +is equivalent to a +.I `p' +command. + +.TP 8 +.RI (1,$)G /re/ +Interactively edits the addressed lines matching a regular expression +.IR re. +For each matching line, +the line is printed, +the current address is set, +and the user is prompted to enter a +.IR command-list . +At the end of the +.I `G' +command, the current address +is set to the last line affected by (the last) +.IR command-list . + +The format of +.I command-list +is the same as that of the +.I `g' +command. A newline alone acts as a null command list. +A single `&' repeats the last non-null command list. + +.TP 8 +H +Toggles the printing of error explanations. +By default, explanations are not printed. +It is recommended that ed scripts begin with this command to +aid in debugging. + +.TP 8 +h +Prints an explanation of the last error. + +.TP 8 +(.)i +Inserts text in the buffer before the current line. +Text is entered in input mode. +The current address is set to the last line entered. + +.TP 8 +(.,.+1)j +Joins the addressed lines. The addressed lines are +deleted from the buffer and replaced by a single +line containing their joined text. +The current address is set to the resultant line. + +.TP 8 +.RI (.)k lc +Marks a line with a lower case letter +.IR lc . +The line can then be addressed as +.I 'lc +(i.e., a single quote followed by +.I lc +) in subsequent commands. The mark is not cleared until the line is +deleted or otherwise modified. + +.TP 8 +(.,.)l +Prints the addressed lines unambiguously. +If a single line fills for than one screen (as might be the case +when viewing a binary file, for instance), a `--More--' +prompt is printed on the last line. +.B ed +waits until the RETURN key is pressed +before displaying the next screen. +The current address is set to the last line +printed. + +.TP 8 +(.,.)m(.) +Moves lines in the buffer. The addressed lines are moved to after the +right-hand destination address, which may be the address +.IR 0 +(zero). +The current address is set to the +last line moved. + +.TP 8 +(.,.)n +Prints the addressed lines along with +their line numbers. The current address is set to the last line +printed. + +.TP 8 +(.,.)p +Prints the addressed lines. The current address is set to the last line +printed. + +.TP 8 +P +Toggles the command prompt on and off. +Unless a prompt was specified by with command-line option +\fI-p string\fR, the command prompt is by default turned off. + +.TP 8 +q +Quits ed. + +.TP 8 +Q +Quits ed unconditionally. +This is similar to the +.I q +command, +except that unwritten changes are discarded without warning. + +.TP 8 +.RI ($)r \ file +Reads +.I file +to after the addressed line. If +.I file +is not specified, then the default +filename is used. If there was no default filename prior to the command, +then the default filename is set to +.IR file . +Otherwise, the default filename is unchanged. +The current address is set to the last line read. + +.TP 8 +.RI ($)r \ !command +Reads +to after the addressed line +the standard output of +.IR `!command' , +(see the +.RI ! command +below). +The default filename is unchanged. +The current address is set to the last line read. + +.HP +.RI (.,.)s /re/replacement/ +.PD 0 +.HP +.RI (.,.)s /re/replacement/\fRg\fR +.HP +.RI (.,.)s /re/replacement/n +.br +Replaces text in the addressed lines +matching a regular expression +.I re +with +.IR replacement . +By default, only the first match in each line is replaced. +If the +.I `g' +(global) suffix is given, then every match to be replaced. +The +.I `n' +suffix, where +.I n +is a postive number, causes only the +.IR n th +match to be replaced. +It is an error if no substitutions are performed on any of the addressed +lines. +The current address is set the last line affected. + +.I re +and +.I replacement +may be delimited by any character other than space and newline +(see the +.I `s' +command below). +If one or two of the last delimiters is omitted, then the last line +affected is printed as though the print suffix +.I `p' +were specified. + + +An unescaped `&' in +.I replacement +is replaced by the currently matched text. +The character sequence +\fI`\em'\fR, +where +.I m +is a number in the range [1,9], is replaced by the +.IR m th +backreference expression of the matched text. +If +.I replacement +consists of a single `%', then +.I replacement +from the last substitution is used. +Newlines may be embedded in +.I replacement +if they are escaped with a backslash (\\). + +.TP 8 +(.,.)s +Repeats the last substitution. +This form of the +.I `s' +command accepts a count suffix +.IR `n' , +or any combination of the characters +.IR `r' , +.IR `g' , +and +.IR `p' . +If a count suffix +.I `n' +is given, then only the +.IR n th +match is replaced. +The +.I `r' +suffix causes +the regular expression of the last search to be used instead of the +that of the last substitution. +The +.I `g' +suffix toggles the global suffix of the last substitution. +The +.I `p' +suffix toggles the print suffix of the last substitution +The current address is set to the last line affected. + +.TP 8 +(.,.)t(.) +Copies (i.e., transfers) the addressed lines to after the right-hand +destination address, which may be the address +.IR 0 +(zero). +The current address is set to the last line +copied. + +.TP 8 +u +Undoes the last command and restores the current address +to what it was before the command. +The global commands +.IR `g' , +.IR `G' , +.IR `v' , +and +.IR `V' . +are treated as a single command by undo. +.I `u' +is its own inverse. + +.TP 8 +.RI (1,$)v /pat/command-list +Applies +.I command-list +to each of the addressed lines not matching a regular expression +.IR re . +This is similar to the +.I `g' +command. + +.TP 8 +.RI (1,$)V /re/ +Interactively edits the addressed lines not matching a regular expression +.IR re. +This is similar to the +.I `G' +command. + +.TP 8 +.RI (1,$)w \ file +Writes the addressed lines to +.IR file . +Any previous contents of +.I file +is lost without warning. +If there is no default filename, then the default filename is set to +.IR file, +otherwise it is unchanged. If no filename is specified, then the default +filename is used. +The current address is unchanged. + +.TP 8 +.RI (1,$)wq \ file +Writes the addressed lines to +.IR file , +and then executes a +.I `q' +command. + +.TP 8 +.RI (1,$)w \ !command +Writes the addressed lines to the standard input of +.IR `!command' , +(see the +.RI ! command +below). +The default filename and current address are unchanged. + +.TP 8 +.RI (1,$)W \ file +Appends the addressed lines to the end of +.IR file . +This is similar to the +.I `w' +command, expect that the previous contents of file is not clobbered. +The current address is unchanged. + +.TP 8 +x +Prompts for an encryption key which is used in subsequent reads and +writes. If a newline alone is entered as the key, then encryption is +turned off. Otherwise, echoing is disabled while a key is read. +Encryption/decryption is done using the bdes(1) algorithm. + +.TP 8 +.RI (.+1)z n +Scrolls +.I n +lines at a time starting at addressed line. If +.I n +is not specified, then the current window size is used. +The current address is set to the last line printed. + +.TP 8 +.RI ! command +Executes +.I command +via +.IR sh (1). +If the first character of +.I command +is `!', then it is replaced by text of the +previous +.IR `!command' . +.B ed +does not process +.I command +for backslash (\\) escapes. +However, an unescaped +.I `%' +is replaced by the default filename. +When the shell returns from execution, a `!' +is printed to the standard output. +The current line is unchanged. + +.TP 8 +($)= +Prints the line number of the addressed line. + +.TP 8 +(.+1)newline +Prints the addressed line, and sets the current address to +that line. + +.SH FILES +.TP 20 +/tmp/ed.* +Buffer file +.PD 0 +.TP 20 +ed.hup +The file to which +.B ed +attempts to write the buffer if the terminal hangs up. + +.SH SEE ALSO + +.IR vi (1), +.IR sed (1), +.IR regex (3), +.IR bdes (1), +.IR sh (1). + +USD:12-13 + +B. W. Kernighan and P. J. Plauger, +.I Software Tools in Pascal , +Addison-Wesley, 1981. + +.SH LIMITATIONS +.B ed +processes +.I file +arguments for backslash escapes, i.e., in a filename, +any characters preceded by a backslash (\\) are +interpreted literally. + +If a text (non-binary) file is not terminated by a newline character, +then +.B ed +appends one on reading/writing it. In the case of a binary file, +.B ed +does not append a newline on reading/writing. + +per line overhead: 4 ints + +.SH DIAGNOSTICS +When an error occurs, +.B ed +prints a `?' and either returns to command mode +or exits if its input is from a script. +An explanation of the last error can be +printed with the +.I `h' +(help) command. + +Since the +.I `g' +(global) command masks any errors from failed searches and substitutions, +it can be used to perform conditional operations in scripts; e.g., +.sp +.RS +g/\fIold\fR/s//\fInew\fR/ +.RE +.sp +replaces any occurrences of +.I old +with +.IR new . +If the +.I `u' +(undo) command occurs in a global command list, then +the command list is executed only once. + +If diagnostics are not disabled, attempting to quit +.B ed +or edit another file before writing a modified buffer +results in an error. +If the command is entered a second time, it succeeds, +but any changes to the buffer are lost. diff --git a/sys/src/cmd/diff/test/diff-t11.2 b/sys/src/cmd/diff/test/diff-t11.2 new file mode 100644 index 000000000..b55792073 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t11.2 @@ -0,0 +1,908 @@ +.\" $OpenBSD: t11.2,v 1.1 2003/07/21 20:16:21 otto Exp $ +.\" +.Dd May 2, 1993 +.Dt ED 1 +.Os +.Sh NAME +.Nm ed +.Nd text editor +.Sh SYNOPSIS +.Nm ed +.Op Fl +.Op Fl sx +.Op Fl p Ar string +.Op Ar file +.Sh DESCRIPTION +.Nm +is a line-oriented text editor. +It is used to create, display, modify, and otherwise manipulate text files. +If invoked with a +.Ar file +argument, then a copy of +.Ar file +is read into the editor's buffer. +Changes are made to this copy and not directly to +.Ar file +itself. +Upon quitting +.Nm ed , +any changes not explicitly saved with a +.Em w +command are lost. +.Pp +Editing is done in two distinct modes: +.Em command +and +.Em input . +When first invoked, +.Nm +is in command mode. +In this mode, commands are read from the standard input and +executed to manipulate the contents of the editor buffer. +.Pp +A typical command might look like: +.Bd -literal -offset indent +,s/old/new/g +.Ed +.Pp +which replaces all occurrences of the string +.Pa old +with +.Pa new . +.Pp +When an input command, such as +.Em a +(append), +.Em i +(insert), +or +.Em c +(change) is given, +.Nm +enters input mode. +This is the primary means of adding text to a file. +In this mode, no commands are available; +instead, the standard input is written directory to the editor buffer. +Lines consist of text up to and including a newline character. +Input mode is terminated by entering a single period +.Pq Ql \&. +on a line. +.Pp +All +.Nm +commands operate on whole lines or ranges of lines; e.g., +the +.Em d +command deletes lines; the +.Em m +command moves lines, and so on. +It is possible to modify only a portion of a line by means of replacement, +as in the example above. +However, even here, the +.Em s +command is applied to whole lines at a time. +.Pp +In general, +.Nm +commands consist of zero or more line addresses, followed by a single +character command and possibly additional parameters; i.e., +commands have the structure: +.Bd -literal -offset indent +[address [,address]]command[parameters] +.Ed +.Pp +The address(es) indicate the line or range of lines to be affected by the +command. +If fewer addresses are given than the command accepts, then +default addresses are supplied. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl +Same as the +.Fl s +option (deprecated). +.It Fl s +Suppress diagnostics. +This should be used if +.Nm +standard input is from a script. +.Fl s +flag. +.It Fl x +Prompt for an encryption key to be used in subsequent reads and writes +(see the +.Em x +command). +.It Fl p Ar string +Specifies a command prompt. +This may be toggled on and off with the +.Em P +command. +.It Ar file +Specifies the name of a file to read. +If +.Ar file +is prefixed with a +bang +.Pq Ql \&! , +then it is interpreted as a shell command. +In this case, what is read is the standard output of +.Ar file +executed via +.Xr sh 1 . +To read a file whose name begins with a bang, prefix the +name with a backslash +.Pq Ql \e . +The default filename is set to +.Ar file +only if it is not prefixed with a bang. +.El +.Ss LINE ADDRESSING +An address represents the number of a line in the buffer. +.Nm +maintains a +.Em current address +which is typically supplied to commands as the default address +when none is specified. +When a file is first read, the current address is set to the last line +of the file. +In general, the current address is set to the last line affected by a command. +.Pp +A line address is +constructed from one of the bases in the list below, optionally followed +by a numeric offset. +The offset may include any combination of digits, operators (i.e., +.Em + , +.Em - , +and +.Em ^ ) , +and whitespace. +Addresses are read from left to right, and their values are computed +relative to the current address. +.Pp +One exception to the rule that addresses represent line numbers is the +address +.Em 0 +(zero). +This means +.Dq before the first line , +and is legal wherever it makes sense. +.Pp +An address range is two addresses separated either by a comma or semi-colon. +The value of the first address in a range cannot exceed the +value of the second. +If only one address is given in a range, +then the second address is set to the given address. +If an +.Em n Ns No -tuple +of addresses is given where +.Em n > 2 , +then the corresponding range is determined by the last two addresses in the +.Em n Ns No -tuple. +If only one address is expected, then the last address is used. +.Pp +Each address in a comma-delimited range is interpreted relative to the +current address. +In a semi-colon-delimited range, the first address is +used to set the current address, and the second address is interpreted +relative to the first. +.Pp +The following address symbols are recognized: +.Bl -tag -width Ds +.It Em \&. +The current line (address) in the buffer. +.It Em $ +The last line in the buffer. +.It Em n +The +.Em n Ns No th +line in the buffer where +.Em n +is a number in the range +.Em [0,$] . +.It Em - No or Em ^ +The previous line. +This is equivalent to +.Em -1 +and may be repeated with cumulative effect. +.It Em -n No or Em ^n +The +.Em n Ns No th +previous line, where +.Em n +is a non-negative number. +.It Em + +The next line. +This is equivalent to +.Em +1 +and may be repeated with cumulative effect. +.It Em +n +The +.Em n Ns No th +next line, where +.Em n +is a non-negative number. +.It Em \&, No or Em % +The first through last lines in the buffer. +This is equivalent to the address range +.Em 1,$ . +.It Em \&; +The current through last lines in the buffer. +This is equivalent to the address range +.Em .,$ . +.It Em / Ns No re Ns Em / +The next line containing the regular expression +.Em re . +The search wraps to the beginning of the buffer and continues down to the +current line, if necessary. +.Em // +repeats the last search. +.It Em ? Ns No re Ns Em ? +The previous line containing the regular expression +.Em re . +The search wraps to the end of the buffer and continues up to the +current line, if necessary. +.Em ?? +repeats the last search. +.It Em \&\' Ns No lc +The line previously marked by a +.Em k +(mark) command, where +.Em lc +is a lower case letter. +.El +.Ss REGULAR EXPRESSIONS +Regular expressions are patterns used in selecting text. +For example, the +.Nm +command +.Bd -literal -offset indent +g/string/ +.Ed +.Pp +prints all lines containing +.Em string . +Regular expressions are also used by the +.Em s +command for selecting old text to be replaced with new. +.Pp +In addition to a specifying string literals, regular expressions can +represent classes of strings. +Strings thus represented are said to be matched by the +corresponding regular expression. +If it is possible for a regular expression to match several strings in +a line, then the leftmost longest match is the one selected. +.Pp +The following symbols are used in constructing regular expressions: +.Bl -tag -width Dsasdfsd +.It Em c +Any character +.Em c +not listed below, including +.Em { Ns No , +.Em } Ns No , +.Em \&( Ns No , +.Em \&) Ns No , +.Em < Ns No , +and +.Em > +matches itself. +.It Em \ec +Any backslash-escaped character +.Em c Ns No , +except for +.Em { Ns No , +.Em } Ns No , +.Em \&( Ns No , +.Em \&) Ns No , +.Em < Ns No , and +.Em > +matches itself. +.It Em \&. +Matches any single character. +.It Em [char-class] +Matches any single character in +.Em char-class . +To include a +.Ql \&] +in +.Em char-class Ns No , +it must be the first character. +A range of characters may be specified by separating the end characters +of the range with a +.Ql - ; +e.g., +.Em a-z +specifies the lower case characters. +The following literal expressions can also be used in +.Em char-class +to specify sets of characters: +.Pp +.Em \ \ [:alnum:]\ \ [:cntrl:]\ \ [:lower:]\ \ [:space:] +.Em \ \ [:alpha:]\ \ [:digit:]\ \ [:print:]\ \ [:upper:] +.Em \ \ [:blank:]\ \ [:graph:]\ \ [:punct:]\ \ [:xdigit:] +.Pp +If +.Ql - +appears as the first or last character of +.Em char-class Ns No , +then it matches itself. +All other characters in +.Em char-class +match themselves. +.Pp +Patterns in +.Em char-class +of the form +.Em [.col-elm.] No or Em [=col-elm=] +where +.Em col-elm +is a collating element are interpreted according to +.Xr locale 5 +(not currently supported). +See +.Xr regex 3 +for an explanation of these constructs. +.It Em [^char-class] +Matches any single character, other than newline, not in +.Em char-class Ns No . +.Em char-class +is defined as above. +.It Em ^ +If +.Em ^ +is the first character of a regular expression, then it +anchors the regular expression to the beginning of a line. +Otherwise, it matches itself. +.It Em $ +If +.Em $ +is the last character of a regular expression, +it anchors the regular expression to the end of a line. +Otherwise, it matches itself. +.It Em \e< +Anchors the single character regular expression or subexpression +immediately following it to the beginning of a word. +(This may not be available.) +.It Em \e> +Anchors the single character regular expression or subexpression +immediately following it to the end of a word. +(This may not be available.) +.It Em \e( Ns No re Ns Em \e) +Defines a subexpression +.Em re . +Subexpressions may be nested. +A subsequent backreference of the form +.Em \en Ns No , +where +.Em n +is a number in the range [1,9], expands to the text matched by the +.Em n Ns No th +subexpression. +For example, the regular expression +.Em \e(.*\e)\e1 +matches any string consisting of identical adjacent substrings. +Subexpressions are ordered relative to their left delimiter. +.It Em * +Matches the single character regular expression or subexpression +immediately preceding it zero or more times. +If +.Em * +is the first character of a regular expression or subexpression, +then it matches itself. +The +.Em * +operator sometimes yields unexpected results. +For example, the regular expression +.Em b* +matches the beginning of the string +.Em abbb +(as opposed to the substring +.Em bbb Ns No ), +since a null match is the only leftmost match. +.Sm off +.It Xo Em \e{ No n,m +.Em \e}\ \e{ No n, Em \e}\ +.Em \e{ No n Em \e} +.Xc +.Sm on +Matches the single character regular expression or subexpression +immediately preceding it at least +.Em n +and at most +.Em m +times. +If +.Em m +is omitted, then it matches at least +.Em n +times. +If the comma is also omitted, then it matches exactly +.Em n +times. +.El +.Pp +Additional regular expression operators may be defined depending on the +particular +.Xr regex 3 +implementation. +.Ss COMMANDS +All +.Nm +commands are single characters, though some require additional parameters. +If a command's parameters extend over several lines, then +each line except for the last must be terminated with a backslash +.Pq Ql \e . +.Pp +In general, at most one command is allowed per line. +However, most commands accept a print suffix, which is any of +.Em p No (print), +.Em l No (list), +or +.Em n No (enumerate), +to print the last line affected by the command. +.Pp +An interrupt (typically ^C) has the effect of aborting the current command +and returning the editor to command mode. +.Pp +.Nm +recognizes the following commands. +The commands are shown together with +the default address or address range supplied if none is +specified (in parentheses), and other possible arguments on the right. +.Bl -tag -width Dxxs +.It (.) Ns Em a +Appends text to the buffer after the addressed line. +Text is entered in input mode. +The current address is set to last line entered. +.It (.,.) Ns Em c +Changes lines in the buffer. +The addressed lines are deleted from the buffer, +and text is appended in their place. +Text is entered in input mode. +The current address is set to last line entered. +.It (.,.) Ns Em d +Deletes the addressed lines from the buffer. +If there is a line after the deleted range, then the current address is set +to this line. +Otherwise the current address is set to the line before the deleted range. +.It Em e No file +Edits +.Em file Ns No , +and sets the default filename. +If +.Em file +is not specified, then the default filename is used. +Any lines in the buffer are deleted before the new file is read. +The current address is set to the last line read. +.It Em e No !command +Edits the standard output of +.Em !command Ns No , +(see +.Em ! No command +below). +The default filename is unchanged. +Any lines in the buffer are deleted before the output of +.Em command +is read. +The current address is set to the last line read. +.It Em E No file +Edits +.Em file +unconditionally. +This is similar to the +.Em e +command, except that unwritten changes are discarded without warning. +The current address is set to the last line read. +.It Em f No file +Sets the default filename to +.Em file Ns No . +If +.Em file +is not specified, then the default unescaped filename is printed. +.It (1,$) Ns Em g Ns No /re/command-list +Applies +.Em command-list +to each of the addressed lines matching a regular expression +.Em re Ns No . +The current address is set to the line currently matched before +.Em command-list +is executed. +At the end of the +.Em g +command, the current address is set to the last line affected by +.Em command-list Ns No . +.Pp +Each command in +.Em command-list +must be on a separate line, +and every line except for the last must be terminated by +.Em \e No (backslash). +Any commands are allowed, except for +.Em g Ns No , +.Em G Ns No , +.Em v Ns No , +and +.Em V Ns No . +A newline alone in +.Em command-list +is equivalent to a +.Em p +command. +.It (1,$) Ns Em G Ns No /re/ +Interactively edits the addressed lines matching a regular expression +.Em re Ns No . +For each matching line, the line is printed, the current address is set, +and the user is prompted to enter a +.Em command-list Ns No . +At the end of the +.Em g +command, the current address is set to the last line affected by (the last) +.Em command-list Ns No . +.Pp +The format of +.Em command-list +is the same as that of the +.Em g +command. +A newline alone acts as a null command list. +A single +.Em & +repeats the last non-null command list. +.It Em H +Toggles the printing of error explanations. +By default, explanations are not printed. +It is recommended that +.Nm +scripts begin with this command to aid in debugging. +.It Em h +Prints an explanation of the last error. +.It (.) Ns Em i +Inserts text in the buffer before the current line. +Text is entered in input mode. +The current address is set to the last line entered. +.It (.,.+1) Ns Em j +Joins the addressed lines. +The addressed lines are deleted from the buffer and replaced by a single +line containing their joined text. +The current address is set to the resultant line. +.It (.) Ns Em klc +Marks a line with a lower case letter +.Em lc Ns No \&. +The line can then be addressed as +.Em \&'lc +(i.e., a single quote followed by +.Em lc Ns No ) +in subsequent commands. +The mark is not cleared until the line is deleted or otherwise modified. +.It (.,.) Ns Em l +Prints the addressed lines unambiguously. +If a single line fills more than one screen (as might be the case +when viewing a binary file, for instance), a +.Dq --More-- +prompt is printed on the last line. +.Nm +waits until the RETURN key is pressed before displaying the next screen. +The current address is set to the last line printed. +.It (.,.) Ns Em m Ns No (.) +Moves lines in the buffer. +The addressed lines are moved to after the +right-hand destination address, which may be the address +.Em 0 +(zero). +The current address is set to the last line moved. +.It (.,.) Ns Em n +Prints the addressed lines along with their line numbers. +The current address is set to the last line printed. +.It (.,.) Ns Em p +Prints the addressed lines. +The current address is set to the last line printed. +.It Em P +Toggles the command prompt on and off. +Unless a prompt was specified by with command-line option +.Fl p Ar string Ns No , +the command prompt is by default turned off. +.It Em q +Quits +.Nm ed . +.It Em Q +Quits +.Nm +unconditionally. +This is similar to the +.Em q +command, except that unwritten changes are discarded without warning. +.It ($) Ns Em r No file +Reads +.Em file +to after the addressed line. +If +.Em file +is not specified, then the default filename is used. +If there was no default filename prior to the command, +then the default filename is set to +.Em file Ns No . +Otherwise, the default filename is unchanged. +The current address is set to the last line read. +.It ($) Ns Em r No !command +Reads to after the addressed line the standard output of +.Em !command Ns No , +(see the +.Em ! +command below). +The default filename is unchanged. +The current address is set to the last line read. +.Sm off +.It Xo (.,.) Em s No /re/replacement/ , \ (.,.) +.Em s No /re/replacement/ Em g , No \ (.,.) +.Em s No /re/replacement/ Em n +.Xc +.Sm on +Replaces text in the addressed lines matching a regular expression +.Em re +with +.Em replacement Ns No . +By default, only the first match in each line is replaced. +If the +.Em g +(global) suffix is given, then every match to be replaced. +The +.Em n +suffix, where +.Em n +is a positive number, causes only the +.Em n Ns No th +match to be replaced. +It is an error if no substitutions are performed on any of the addressed +lines. +The current address is set the last line affected. +.Pp +.Em re +and +.Em replacement +may be delimited by any character other than space and newline +(see the +.Em s +command below). +If one or two of the last delimiters is omitted, then the last line +affected is printed as though the print suffix +.Em p +were specified. +.Pp +An unescaped +.Ql \e +in +.Em replacement +is replaced by the currently matched text. +The character sequence +.Em \em Ns No , +where +.Em m +is a number in the range [1,9], is replaced by the +.Em m Ns No th +backreference expression of the matched text. +If +.Em replacement +consists of a single +.Ql % , +then +.Em replacement +from the last substitution is used. +Newlines may be embedded in +.Em replacement +if they are escaped with a backslash +.Pq Ql \e . +.It (.,.) Ns Em s +Repeats the last substitution. +This form of the +.Em s +command accepts a count suffix +.Em n Ns No , +or any combination of the characters +.Em r Ns No , +.Em g Ns No , +and +.Em p Ns No . +If a count suffix +.Em n +is given, then only the +.Em n Ns No th +match is replaced. +The +.Em r +suffix causes +the regular expression of the last search to be used instead of the +that of the last substitution. +The +.Em g +suffix toggles the global suffix of the last substitution. +The +.Em p +suffix toggles the print suffix of the last substitution +The current address is set to the last line affected. +.It (.,.) Ns Em t Ns No (.) +Copies (i.e., transfers) the addressed lines to after the right-hand +destination address, which may be the address +.Em 0 +(zero). +The current address is set to the last line copied. +.It Em u +Undoes the last command and restores the current address +to what it was before the command. +The global commands +.Em g Ns No , +.Em G Ns No , +.Em v Ns No , +and +.Em V Ns No . +are treated as a single command by undo. +.Em u +is its own inverse. +.It (1,$) Ns Em v Ns No /re/command-list +Applies +.Em command-list +to each of the addressed lines not matching a regular expression +.Em re Ns No . +This is similar to the +.Em g +command. +.It (1,$) Ns Em V Ns No /re/ +Interactively edits the addressed lines not matching a regular expression +.Em re Ns No . +This is similar to the +.Em G +command. +.It (1,$) Ns Em w No file +Writes the addressed lines to +.Em file Ns No . +Any previous contents of +.Em file +is lost without warning. +If there is no default filename, then the default filename is set to +.Em file Ns No , +otherwise it is unchanged. +If no filename is specified, then the default filename is used. +The current address is unchanged. +.It (1,$) Ns Em wq No file +Writes the addressed lines to +.Em file Ns No , +and then executes a +.Em q +command. +.It (1,$) Ns Em w No !command +Writes the addressed lines to the standard input of +.Em !command Ns No , +(see the +.Em ! +command below). +The default filename and current address are unchanged. +.It (1,$) Ns Em W No file +Appends the addressed lines to the end of +.Em file Ns No . +This is similar to the +.Em w +command, expect that the previous contents of file is not clobbered. +The current address is unchanged. +.It Em x +Prompts for an encryption key which is used in subsequent reads and writes. +If a newline alone is entered as the key, then encryption is turned off. +Otherwise, echoing is disabled while a key is read. +Encryption/decryption is done using the +.Xr bdes 1 +algorithm. +.It (.+1) Ns Em z Ns No n +Scrolls +.Em n +lines at a time starting at addressed line. +If +.Em n +is not specified, then the current window size is used. +The current address is set to the last line printed. +.It ($) Ns Em = +Prints the line number of the addressed line. +.It (.+1) Ns Em newline +Prints the addressed line, and sets the current address to that line. +.It Em ! Ns No command +Executes +.Em command +via +.Xr sh 1 . +If the first character of +.Em command +is +.Em ! Ns No , +then it is replaced by text of the previous +.Em !command Ns No . +.Nm +does not process +.Em command +for +.Em \e +(backslash) escapes. +However, an unescaped +.Em % +is replaced by the default filename. +When the shell returns from execution, a +.Em ! +is printed to the standard output. +The current line is unchanged. +.El +.Sh LIMITATIONS +.Nm +processes +.Em file +arguments for backslash escapes, i.e., in a filename, +any characters preceded by a backslash +.Pq Ql \e +are interpreted literally. +.Pp +If a text (non-binary) file is not terminated by a newline character, +then +.Nm +appends one on reading/writing it. +In the case of a binary file, +.Nm +does not append a newline on reading/writing. +.Sh DIAGNOSTICS +When an error occurs, +.Nm +prints a +.Dq ? +and either returns to command mode or exits if its input is from a script. +An explanation of the last error can be printed with the +.Em h +(help) command. +.Pp +Since the +.Em g +(global) command masks any errors from failed searches and substitutions, +it can be used to perform conditional operations in scripts; e.g., +.Bd -literal -offset indent +g/old/s//new/ +.Ed +.Pp +replaces any occurrences of +.Em old +with +.Em new Ns No . +.Pp +If the +.Em u +(undo) command occurs in a global command list, then +the command list is executed only once. +.Pp +If diagnostics are not disabled, attempting to quit +.Nm +or edit another file before writing a modified buffer results in an error. +If the command is entered a second time, it succeeds, +but any changes to the buffer are lost. +.Sh FILES +.Bl -tag -width /tmp/ed.* -compact +.It Pa /tmp/ed.* +buffer file +.It Pa ed.hup +where +.Nm +attempts to write the buffer if the terminal hangs up +.El +.Sh SEE ALSO +.Xr bdes 1 , +.Xr sed 1 , +.Xr sh 1 , +.Xr vi 1 , +.Xr regex 3 +.Pp +USD:12-13 +.Rs +.%A B. W. Kernighan +.%A P. J. Plauger +.%B Software Tools in Pascal +.%O Addison-Wesley +.%D 1981 +.Re +.Sh HISTORY +An +.Nm +command appeared in +.At v1 . diff --git a/sys/src/cmd/diff/test/diff-t11.expected b/sys/src/cmd/diff/test/diff-t11.expected new file mode 100644 index 000000000..9456249f1 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t11.expected @@ -0,0 +1,1612 @@ +--- diff-t11.1 ++++ diff-t11.2 +@@ -1,1003 +1,908 @@ +-.\" $OpenBSD: t11.1,v 1.2 2007/11/27 16:22:12 martynas Exp $ +-.\" $NetBSD: ed.1,v 1.13 1995/03/21 09:04:38 cgd Exp $ ++.\" $OpenBSD: t11.2,v 1.1 2003/07/21 20:16:21 otto Exp $ + .\" +-.TH ED 1 "21 May 1993" +-.SH NAME +-.\" ed, red \- text editor +-ed \- text editor +-.SH SYNOPSIS +-ed [-] [-sx] [-p \fIstring\fR] [\fIfile\fR] +-.\" .LP +-.\" red [-] [-sx] [-p \fIstring\fR] [\fIfile\fR] +-.SH DESCRIPTION +-.B ed ++.Dd May 2, 1993 ++.Dt ED 1 ++.Os ++.Sh NAME ++.Nm ed ++.Nd text editor ++.Sh SYNOPSIS ++.Nm ed ++.Op Fl ++.Op Fl sx ++.Op Fl p Ar string ++.Op Ar file ++.Sh DESCRIPTION ++.Nm + is a line-oriented text editor. +-It is used to create, display, modify and otherwise manipulate text +-files. +-.\" .B red +-.\" is a restricted +-.\" .BR ed : +-.\" it can only edit files in the current +-.\" directory and cannot execute shell commands. +- ++It is used to create, display, modify, and otherwise manipulate text files. + If invoked with a +-.I file ++.Ar file + argument, then a copy of +-.I file ++.Ar file + is read into the editor's buffer. + Changes are made to this copy and not directly to +-.I file ++.Ar file + itself. + Upon quitting +-.BR ed , +-any changes not explicitly saved with a +-.I `w' ++.Nm ed , ++any changes not explicitly saved with a ++.Em w + command are lost. +- ++.Pp + Editing is done in two distinct modes: +-.I command ++.Em command + and +-.IR input . ++.Em input . + When first invoked, +-.B ed ++.Nm + is in command mode. +-In this mode commands are read from the standard input and ++In this mode, commands are read from the standard input and + executed to manipulate the contents of the editor buffer. ++.Pp + A typical command might look like: +-.sp +-.RS +-,s/\fIold\fR/\fInew\fR/g +-.RE +-.sp ++.Bd -literal -offset indent ++,s/old/new/g ++.Ed ++.Pp + which replaces all occurrences of the string +-.I old ++.Pa old + with +-.IR new . +- ++.Pa new . ++.Pp + When an input command, such as +-.I `a' ++.Em a + (append), +-.I `i' +-(insert) or +-.I `c' +-(change), is given, +-.B ed +-enters input mode. This is the primary means +-of adding text to a file. ++.Em i ++(insert), ++or ++.Em c ++(change) is given, ++.Nm ++enters input mode. ++This is the primary means of adding text to a file. + In this mode, no commands are available; +-instead, the standard input is written +-directly to the editor buffer. Lines consist of text up to and +-including a +-.IR newline +-character. +-Input mode is terminated by +-entering a single period (\fI.\fR) on a line. +- ++instead, the standard input is written directory to the editor buffer. ++Lines consist of text up to and including a newline character. ++Input mode is terminated by entering a single period ++.Pq Ql \&. ++on a line. ++.Pp + All +-.B ed ++.Nm + commands operate on whole lines or ranges of lines; e.g., + the +-.I `d' ++.Em d + command deletes lines; the +-.I `m' ++.Em m + command moves lines, and so on. + It is possible to modify only a portion of a line by means of replacement, +-as in the example above. However even here, the +-.I `s' ++as in the example above. ++However, even here, the ++.Em s + command is applied to whole lines at a time. +- ++.Pp + In general, +-.B ed ++.Nm + commands consist of zero or more line addresses, followed by a single + character command and possibly additional parameters; i.e., + commands have the structure: +-.sp +-.RS +-.I [address [,address]]command[parameters] +-.RE +-.sp ++.Bd -literal -offset indent ++[address [,address]]command[parameters] ++.Ed ++.Pp + The address(es) indicate the line or range of lines to be affected by the +-command. If fewer addresses are given than the command accepts, then ++command. ++If fewer addresses are given than the command accepts, then + default addresses are supplied. +- +-.SS OPTIONS +-.TP 8 +--s +-Suppresses diagnostics. This should be used if +-.BR ed 's ++.Pp ++The options are as follows: ++.Bl -tag -width Ds ++.It Fl ++Same as the ++.Fl s ++option (deprecated). ++.It Fl s ++Suppress diagnostics. ++This should be used if ++.Nm + standard input is from a script. +- +-.TP 8 +--x +-Prompts for an encryption key to be used in subsequent reads and writes ++.Fl s ++flag. ++.It Fl x ++Prompt for an encryption key to be used in subsequent reads and writes + (see the +-.I `x' ++.Em x + command). +- +-.TP 8 +-.RI \-p \ string +-Specifies a command prompt. This may be toggled on and off with the +-.I `P' ++.It Fl p Ar string ++Specifies a command prompt. ++This may be toggled on and off with the ++.Em P + command. +- +-.TP 8 +-.I file +-Specifies the name of a file to read. If +-.I file ++.It Ar file ++Specifies the name of a file to read. ++If ++.Ar file + is prefixed with a +-bang (!), then it is interpreted as a shell command. In this case, +-what is read is +-the standard output of +-.I file ++bang ++.Pq Ql \&! , ++then it is interpreted as a shell command. ++In this case, what is read is the standard output of ++.Ar file + executed via +-.IR sh (1). ++.Xr sh 1 . + To read a file whose name begins with a bang, prefix the +-name with a backslash (\\). ++name with a backslash ++.Pq Ql \e . + The default filename is set to +-.I file ++.Ar file + only if it is not prefixed with a bang. +- +-.SS LINE ADDRESSING ++.El ++.Ss LINE ADDRESSING + An address represents the number of a line in the buffer. +-.B ed ++.Nm + maintains a +-.I current address +-which is +-typically supplied to commands as the default address when none is specified. +-When a file is first read, the current address is set to the last line +-of the file. In general, the current address is set to the last line +-affected by a command. +- ++.Em current address ++which is typically supplied to commands as the default address ++when none is specified. ++When a file is first read, the current address is set to the last line ++of the file. ++In general, the current address is set to the last line affected by a command. ++.Pp + A line address is + constructed from one of the bases in the list below, optionally followed +-by a numeric offset. The offset may include any combination +-of digits, operators (i.e., +-.IR + , +-.I - ++by a numeric offset. ++The offset may include any combination of digits, operators (i.e., ++.Em + , ++.Em - , + and +-.IR ^ ) ++.Em ^ ) , + and whitespace. + Addresses are read from left to right, and their values are computed + relative to the current address. +- ++.Pp + One exception to the rule that addresses represent line numbers is the + address +-.I 0 ++.Em 0 + (zero). +-This means "before the first line," ++This means ++.Dq before the first line , + and is legal wherever it makes sense. +- +-An address range is two addresses separated either by a comma or +-semi-colon. The value of the first address in a range cannot exceed the +-value of the second. If only one address is given in a range, then +-the second address is set to the given address. If an +-.IR n- tuple ++.Pp ++An address range is two addresses separated either by a comma or semi-colon. ++The value of the first address in a range cannot exceed the ++value of the second. ++If only one address is given in a range, ++then the second address is set to the given address. ++If an ++.Em n Ns No -tuple + of addresses is given where +-.I n > 2, +-then the corresponding range is determined by the last two addresses in +-the +-.IR n- tuple. ++.Em n > 2 , ++then the corresponding range is determined by the last two addresses in the ++.Em n Ns No -tuple. + If only one address is expected, then the last address is used. +- ++.Pp + Each address in a comma-delimited range is interpreted relative to the +-current address. In a semi-colon-delimited range, the first address is ++current address. ++In a semi-colon-delimited range, the first address is + used to set the current address, and the second address is interpreted + relative to the first. +- +- +-The following address symbols are recognized. +- +-.TP 8 +-\&. ++.Pp ++The following address symbols are recognized: ++.Bl -tag -width Ds ++.It Em \&. + The current line (address) in the buffer. +- +-.TP 8 +-$ ++.It Em $ + The last line in the buffer. +- +-.TP 8 +-n ++.It Em n + The +-.IR n th, +-line in the buffer +-where +-.I n ++.Em n Ns No th ++line in the buffer where ++.Em n + is a number in the range +-.I [0,$]. +- +-.TP 8 +-- or ^ ++.Em [0,$] . ++.It Em - No or Em ^ + The previous line. + This is equivalent to +-.I -1 ++.Em -1 + and may be repeated with cumulative effect. +- +-.TP 8 +--\fIn\fR or ^\fIn\fR ++.It Em -n No or Em ^n + The +-.IR n th ++.Em n Ns No th + previous line, where +-.I n ++.Em n + is a non-negative number. +- +-.TP 8 +-+ +-The +-next line. ++.It Em + ++The next line. + This is equivalent to +-.I +1 ++.Em +1 + and may be repeated with cumulative effect. +- +-.TP 8 +-+\fIn\fR or whitespace\fIn\fR ++.It Em +n + The +-.IR n th ++.Em n Ns No th + next line, where +-.I n ++.Em n + is a non-negative number. +-.I whitespace +-followed by a number +-.I n +-is interpreted as +-.IR +n . +- +-.TP 8 +-, \fRor\fB % +-The first through last lines in the buffer. This is equivalent to +-the address range +-.I 1,$. +- +-.TP 8 +-; +-The +-current through last lines in the buffer. This is equivalent to +-the address range +-.I .,$. +- +-.TP 8 +-.RI / re/ +-The +-next line containing the regular expression +-.IR re . ++.It Em \&, No or Em % ++The first through last lines in the buffer. ++This is equivalent to the address range ++.Em 1,$ . ++.It Em \&; ++The current through last lines in the buffer. ++This is equivalent to the address range ++.Em .,$ . ++.It Em / Ns No re Ns Em / ++The next line containing the regular expression ++.Em re . + The search wraps to the beginning of the buffer and continues down to the + current line, if necessary. +-// repeats the last search. +- +-.TP 8 +-.RI ? re? +-The +-previous line containing the regular expression +-.IR re . ++.Em // ++repeats the last search. ++.It Em ? Ns No re Ns Em ? ++The previous line containing the regular expression ++.Em re . + The search wraps to the end of the buffer and continues up to the + current line, if necessary. +-?? repeats the last search. +- +-.TP 8 +-.RI \' lc +-The +-line previously marked by a +-.I `k' ++.Em ?? ++repeats the last search. ++.It Em \&\' Ns No lc ++The line previously marked by a ++.Em k + (mark) command, where +-.I lc ++.Em lc + is a lower case letter. +- +-.SS REGULAR EXPRESSIONS ++.El ++.Ss REGULAR EXPRESSIONS + Regular expressions are patterns used in selecting text. + For example, the +-.B ed ++.Nm + command +-.sp +-.RS +-g/\fIstring\fR/ +-.RE +-.sp ++.Bd -literal -offset indent ++g/string/ ++.Ed ++.Pp + prints all lines containing +-.IR string . +-Regular expressions are also +-used by the +-.I `s' ++.Em string . ++Regular expressions are also used by the ++.Em s + command for selecting old text to be replaced with new. +- ++.Pp + In addition to a specifying string literals, regular expressions can +-represent +-classes of strings. Strings thus represented are said to be matched +-by the corresponding regular expression. +-If it is possible for a regular expression +-to match several strings in a line, then the left-most longest match is +-the one selected. +- ++represent classes of strings. ++Strings thus represented are said to be matched by the ++corresponding regular expression. ++If it is possible for a regular expression to match several strings in ++a line, then the leftmost longest match is the one selected. ++.Pp + The following symbols are used in constructing regular expressions: +- +-.TP 8 +-c ++.Bl -tag -width Dsasdfsd ++.It Em c + Any character +-.I c +-not listed below, including `{', '}', `(', `)', `<' and `>', ++.Em c ++not listed below, including ++.Em { Ns No , ++.Em } Ns No , ++.Em \&( Ns No , ++.Em \&) Ns No , ++.Em < Ns No , ++and ++.Em > + matches itself. +- +-.TP 8 +-\fR\e\fIc\fR ++.It Em \ec + Any backslash-escaped character +-.IR c , +-except for `{', '}', `(', `)', `<' and `>', ++.Em c Ns No , ++except for ++.Em { Ns No , ++.Em } Ns No , ++.Em \&( Ns No , ++.Em \&) Ns No , ++.Em < Ns No , and ++.Em > + matches itself. +- +-.TP 8 +-\fR.\fR ++.It Em \&. + Matches any single character. +- +-.TP 8 +-.I [char-class] ++.It Em [char-class] + Matches any single character in +-.IR char-class . +-To include a `]' ++.Em char-class . ++To include a ++.Ql \&] + in +-.IR char-class , ++.Em char-class Ns No , + it must be the first character. + A range of characters may be specified by separating the end characters +-of the range with a `-', e.g., `a-z' specifies the lower case characters. ++of the range with a ++.Ql - ; ++e.g., ++.Em a-z ++specifies the lower case characters. + The following literal expressions can also be used in +-.I char-class ++.Em char-class + to specify sets of characters: +-.sp +-\ \ [:alnum:]\ \ [:cntrl:]\ \ [:lower:]\ \ [:space:] +-.PD 0 +-\ \ [:alpha:]\ \ [:digit:]\ \ [:print:]\ \ [:upper:] +-.PD 0 +-\ \ [:blank:]\ \ [:graph:]\ \ [:punct:]\ \ [:xdigit:] +-.sp +-If `-' appears as the first or last +-character of +-.IR char-class , ++.Pp ++.Em \ \ [:alnum:]\ \ [:cntrl:]\ \ [:lower:]\ \ [:space:] ++.Em \ \ [:alpha:]\ \ [:digit:]\ \ [:print:]\ \ [:upper:] ++.Em \ \ [:blank:]\ \ [:graph:]\ \ [:punct:]\ \ [:xdigit:] ++.Pp ++If ++.Ql - ++appears as the first or last character of ++.Em char-class Ns No , + then it matches itself. + All other characters in +-.I char-class ++.Em char-class + match themselves. +-.sp ++.Pp + Patterns in +-.I char-class +-of the form: +-.sp +-\ \ [.\fIcol-elm\fR.] or, +-.PD 0 +-\ \ [=\fIcol-elm\fR=] +-.sp ++.Em char-class ++of the form ++.Em [.col-elm.] No or Em [=col-elm=] + where +-.I col-elm +-is a +-.I collating element +-are interpreted according to +-.IR locale (5) ++.Em col-elm ++is a collating element are interpreted according to ++.Xr locale 5 + (not currently supported). + See +-.IR regex (3) ++.Xr regex 3 + for an explanation of these constructs. +- +-.TP 8 +-[^\fIchar-class\fR] ++.It Em [^char-class] + Matches any single character, other than newline, not in +-.IR char-class . +-.IR char-class +-is defined +-as above. +- +-.TP 8 +-^ +-If `^' is the first character of a regular expression, then it ++.Em char-class Ns No . ++.Em char-class ++is defined as above. ++.It Em ^ ++If ++.Em ^ ++is the first character of a regular expression, then it + anchors the regular expression to the beginning of a line. + Otherwise, it matches itself. +- +-.TP 8 +-$ +-If `$' is the last character of a regular expression, it +-anchors the regular expression to the end of a line. ++.It Em $ ++If ++.Em $ ++is the last character of a regular expression, ++it anchors the regular expression to the end of a line. + Otherwise, it matches itself. +- +-.TP 8 +-\fR\e<\fR ++.It Em \e< + Anchors the single character regular expression or subexpression + immediately following it to the beginning of a word. +-(This may not be available) +- +-.TP 8 +-\fR\e>\fR ++(This may not be available.) ++.It Em \e> + Anchors the single character regular expression or subexpression + immediately following it to the end of a word. +-(This may not be available) +- +-.TP 8 +-\fR\e(\fIre\fR\e)\fR ++(This may not be available.) ++.It Em \e( Ns No re Ns Em \e) + Defines a subexpression +-.IR re . ++.Em re . + Subexpressions may be nested. +-A subsequent backreference of the form \fI`\en'\fR, where +-.I n ++A subsequent backreference of the form ++.Em \en Ns No , ++where ++.Em n + is a number in the range [1,9], expands to the text matched by the +-.IR n th ++.Em n Ns No th + subexpression. +-For example, the regular expression `\e(.*\e)\e1' matches any string +-consisting of identical adjacent substrings. +-Subexpressions are ordered relative to +-their left delimiter. +- +-.TP 8 +-* ++For example, the regular expression ++.Em \e(.*\e)\e1 ++matches any string consisting of identical adjacent substrings. ++Subexpressions are ordered relative to their left delimiter. ++.It Em * + Matches the single character regular expression or subexpression +-immediately preceding it zero or more times. If '*' is the first +-character of a regular expression or subexpression, then it matches +-itself. The `*' operator sometimes yields unexpected results. +-For example, the regular expression `b*' matches the beginning of +-the string `abbb' (as opposed to the substring `bbb'), since a null match +-is the only left-most match. +- +-.TP 8 +-\fR\e{\fIn,m\fR\e}\fR or \fR\e{\fIn,\fR\e}\fR or \fR\e{\fIn\fR\e}\fR ++immediately preceding it zero or more times. ++If ++.Em * ++is the first character of a regular expression or subexpression, ++then it matches itself. ++The ++.Em * ++operator sometimes yields unexpected results. ++For example, the regular expression ++.Em b* ++matches the beginning of the string ++.Em abbb ++(as opposed to the substring ++.Em bbb Ns No ), ++since a null match is the only leftmost match. ++.Sm off ++.It Xo Em \e{ No n,m ++.Em \e}\ \e{ No n, Em \e}\ ++.Em \e{ No n Em \e} ++.Xc ++.Sm on + Matches the single character regular expression or subexpression + immediately preceding it at least +-.I n ++.Em n + and at most +-.I m ++.Em m + times. + If +-.I m ++.Em m + is omitted, then it matches at least +-.I n ++.Em n + times. + If the comma is also omitted, then it matches exactly +-.I n ++.Em n + times. +- +-.LP ++.El ++.Pp + Additional regular expression operators may be defined depending on the + particular +-.IR regex (3) ++.Xr regex 3 + implementation. +- +-.SS COMMANDS ++.Ss COMMANDS + All +-.B ed +-commands are single characters, though some require additonal parameters. ++.Nm ++commands are single characters, though some require additional parameters. + If a command's parameters extend over several lines, then +-each line except for the last +-must be terminated with a backslash (\\). +- ++each line except for the last must be terminated with a backslash ++.Pq Ql \e . ++.Pp + In general, at most one command is allowed per line. + However, most commands accept a print suffix, which is any of +-.I `p' +-(print), +-.I `l' +-(list) , ++.Em p No (print), ++.Em l No (list), + or +-.I `n' +-(enumerate), ++.Em n No (enumerate), + to print the last line affected by the command. +- ++.Pp + An interrupt (typically ^C) has the effect of aborting the current command + and returning the editor to command mode. +- +-.B ed +-recognizes the following commands. The commands are shown together with ++.Pp ++.Nm ++recognizes the following commands. ++The commands are shown together with + the default address or address range supplied if none is +-specified (in parenthesis). +- +-.TP 8 +-(.)a ++specified (in parentheses), and other possible arguments on the right. ++.Bl -tag -width Dxxs ++.It (.) Ns Em a + Appends text to the buffer after the addressed line. + Text is entered in input mode. + The current address is set to last line entered. +- +-.TP 8 +-(.,.)c +-Changes lines in the buffer. The addressed lines are deleted +-from the buffer, and text is appended in their place. ++.It (.,.) Ns Em c ++Changes lines in the buffer. ++The addressed lines are deleted from the buffer, ++and text is appended in their place. + Text is entered in input mode. + The current address is set to last line entered. +- +-.TP 8 +-(.,.)d ++.It (.,.) Ns Em d + Deletes the addressed lines from the buffer. + If there is a line after the deleted range, then the current address is set +-to this line. Otherwise the current address is set to the line +-before the deleted range. +- +-.TP 8 +-.RI e \ file ++to this line. ++Otherwise the current address is set to the line before the deleted range. ++.It Em e No file + Edits +-.IR file , ++.Em file Ns No , + and sets the default filename. + If +-.I file +-is not specified, then the default filename is used. +-Any lines in the buffer are deleted before +-the new file is read. ++.Em file ++is not specified, then the default filename is used. ++Any lines in the buffer are deleted before the new file is read. + The current address is set to the last line read. +- +-.TP 8 +-.RI e \ !command ++.It Em e No !command + Edits the standard output of +-.IR `!command' , ++.Em !command Ns No , + (see +-.RI ! command ++.Em ! No command + below). + The default filename is unchanged. + Any lines in the buffer are deleted before the output of +-.I command ++.Em command + is read. + The current address is set to the last line read. +- +-.TP 8 +-.RI E \ file ++.It Em E No file + Edits +-.I file ++.Em file + unconditionally. + This is similar to the +-.I e +-command, +-except that unwritten changes are discarded without warning. ++.Em e ++command, except that unwritten changes are discarded without warning. + The current address is set to the last line read. +- +-.TP 8 +-.RI f \ file ++.It Em f No file + Sets the default filename to +-.IR file . ++.Em file Ns No . + If +-.I file ++.Em file + is not specified, then the default unescaped filename is printed. +- +-.TP 8 +-.RI (1,$)g /re/command-list ++.It (1,$) Ns Em g Ns No /re/command-list + Applies +-.I command-list ++.Em command-list + to each of the addressed lines matching a regular expression +-.IR re . +-The current address is set to the +-line currently matched before +-.I command-list ++.Em re Ns No . ++The current address is set to the line currently matched before ++.Em command-list + is executed. + At the end of the +-.I `g' ++.Em g + command, the current address is set to the last line affected by +-.IR command-list . +- ++.Em command-list Ns No . ++.Pp + Each command in +-.I command-list ++.Em command-list + must be on a separate line, +-and every line except for the last must be terminated by a backslash +-(\\). ++and every line except for the last must be terminated by ++.Em \e No (backslash). + Any commands are allowed, except for +-.IR `g' , +-.IR `G' , +-.IR `v' , ++.Em g Ns No , ++.Em G Ns No , ++.Em v Ns No , + and +-.IR `V' . ++.Em V Ns No . + A newline alone in +-.I command-list +-is equivalent to a +-.I `p' ++.Em command-list ++is equivalent to a ++.Em p + command. +- +-.TP 8 +-.RI (1,$)G /re/ ++.It (1,$) Ns Em G Ns No /re/ + Interactively edits the addressed lines matching a regular expression +-.IR re. +-For each matching line, +-the line is printed, +-the current address is set, +-and the user is prompted to enter a +-.IR command-list . ++.Em re Ns No . ++For each matching line, the line is printed, the current address is set, ++and the user is prompted to enter a ++.Em command-list Ns No . + At the end of the +-.I `G' +-command, the current address +-is set to the last line affected by (the last) +-.IR command-list . +- ++.Em g ++command, the current address is set to the last line affected by (the last) ++.Em command-list Ns No . ++.Pp + The format of +-.I command-list ++.Em command-list + is the same as that of the +-.I `g' +-command. A newline alone acts as a null command list. +-A single `&' repeats the last non-null command list. +- +-.TP 8 +-H ++.Em g ++command. ++A newline alone acts as a null command list. ++A single ++.Em & ++repeats the last non-null command list. ++.It Em H + Toggles the printing of error explanations. + By default, explanations are not printed. +-It is recommended that ed scripts begin with this command to +-aid in debugging. +- +-.TP 8 +-h ++It is recommended that ++.Nm ++scripts begin with this command to aid in debugging. ++.It Em h + Prints an explanation of the last error. +- +-.TP 8 +-(.)i ++.It (.) Ns Em i + Inserts text in the buffer before the current line. + Text is entered in input mode. + The current address is set to the last line entered. +- +-.TP 8 +-(.,.+1)j +-Joins the addressed lines. The addressed lines are +-deleted from the buffer and replaced by a single ++.It (.,.+1) Ns Em j ++Joins the addressed lines. ++The addressed lines are deleted from the buffer and replaced by a single + line containing their joined text. + The current address is set to the resultant line. +- +-.TP 8 +-.RI (.)k lc ++.It (.) Ns Em klc + Marks a line with a lower case letter +-.IR lc . +-The line can then be addressed as +-.I 'lc ++.Em lc Ns No \&. ++The line can then be addressed as ++.Em \&'lc + (i.e., a single quote followed by +-.I lc +-) in subsequent commands. The mark is not cleared until the line is +-deleted or otherwise modified. +- +-.TP 8 +-(.,.)l ++.Em lc Ns No ) ++in subsequent commands. ++The mark is not cleared until the line is deleted or otherwise modified. ++.It (.,.) Ns Em l + Prints the addressed lines unambiguously. +-If a single line fills for than one screen (as might be the case +-when viewing a binary file, for instance), a `--More--' +-prompt is printed on the last line. +-.B ed +-waits until the RETURN key is pressed +-before displaying the next screen. +-The current address is set to the last line +-printed. +- +-.TP 8 +-(.,.)m(.) +-Moves lines in the buffer. The addressed lines are moved to after the ++If a single line fills more than one screen (as might be the case ++when viewing a binary file, for instance), a ++.Dq --More-- ++prompt is printed on the last line. ++.Nm ++waits until the RETURN key is pressed before displaying the next screen. ++The current address is set to the last line printed. ++.It (.,.) Ns Em m Ns No (.) ++Moves lines in the buffer. ++The addressed lines are moved to after the + right-hand destination address, which may be the address +-.IR 0 ++.Em 0 + (zero). +-The current address is set to the +-last line moved. +- +-.TP 8 +-(.,.)n +-Prints the addressed lines along with +-their line numbers. The current address is set to the last line +-printed. +- +-.TP 8 +-(.,.)p +-Prints the addressed lines. The current address is set to the last line +-printed. +- +-.TP 8 +-P ++The current address is set to the last line moved. ++.It (.,.) Ns Em n ++Prints the addressed lines along with their line numbers. ++The current address is set to the last line printed. ++.It (.,.) Ns Em p ++Prints the addressed lines. ++The current address is set to the last line printed. ++.It Em P + Toggles the command prompt on and off. + Unless a prompt was specified by with command-line option +-\fI-p string\fR, the command prompt is by default turned off. +- +-.TP 8 +-q +-Quits ed. +- +-.TP 8 +-Q +-Quits ed unconditionally. ++.Fl p Ar string Ns No , ++the command prompt is by default turned off. ++.It Em q ++Quits ++.Nm ed . ++.It Em Q ++Quits ++.Nm ++unconditionally. + This is similar to the +-.I q +-command, +-except that unwritten changes are discarded without warning. +- +-.TP 8 +-.RI ($)r \ file ++.Em q ++command, except that unwritten changes are discarded without warning. ++.It ($) Ns Em r No file + Reads +-.I file +-to after the addressed line. If +-.I file +-is not specified, then the default +-filename is used. If there was no default filename prior to the command, ++.Em file ++to after the addressed line. ++If ++.Em file ++is not specified, then the default filename is used. ++If there was no default filename prior to the command, + then the default filename is set to +-.IR file . ++.Em file Ns No . + Otherwise, the default filename is unchanged. + The current address is set to the last line read. +- +-.TP 8 +-.RI ($)r \ !command +-Reads +-to after the addressed line +-the standard output of +-.IR `!command' , ++.It ($) Ns Em r No !command ++Reads to after the addressed line the standard output of ++.Em !command Ns No , + (see the +-.RI ! command +-below). ++.Em ! ++command below). + The default filename is unchanged. + The current address is set to the last line read. +- +-.HP +-.RI (.,.)s /re/replacement/ +-.PD 0 +-.HP +-.RI (.,.)s /re/replacement/\fRg\fR +-.HP +-.RI (.,.)s /re/replacement/n +-.br +-Replaces text in the addressed lines +-matching a regular expression +-.I re ++.Sm off ++.It Xo (.,.) Em s No /re/replacement/ , \ (.,.) ++.Em s No /re/replacement/ Em g , No \ (.,.) ++.Em s No /re/replacement/ Em n ++.Xc ++.Sm on ++Replaces text in the addressed lines matching a regular expression ++.Em re + with +-.IR replacement . ++.Em replacement Ns No . + By default, only the first match in each line is replaced. + If the +-.I `g' ++.Em g + (global) suffix is given, then every match to be replaced. + The +-.I `n' ++.Em n + suffix, where +-.I n +-is a postive number, causes only the +-.IR n th ++.Em n ++is a positive number, causes only the ++.Em n Ns No th + match to be replaced. + It is an error if no substitutions are performed on any of the addressed + lines. + The current address is set the last line affected. +- +-.I re ++.Pp ++.Em re + and +-.I replacement ++.Em replacement + may be delimited by any character other than space and newline + (see the +-.I `s' ++.Em s + command below). + If one or two of the last delimiters is omitted, then the last line + affected is printed as though the print suffix +-.I `p' ++.Em p + were specified. +- +- +-An unescaped `&' in +-.I replacement ++.Pp ++An unescaped ++.Ql \e ++in ++.Em replacement + is replaced by the currently matched text. + The character sequence +-\fI`\em'\fR, ++.Em \em Ns No , + where +-.I m ++.Em m + is a number in the range [1,9], is replaced by the +-.IR m th ++.Em m Ns No th + backreference expression of the matched text. + If +-.I replacement +-consists of a single `%', then +-.I replacement ++.Em replacement ++consists of a single ++.Ql % , ++then ++.Em replacement + from the last substitution is used. + Newlines may be embedded in +-.I replacement +-if they are escaped with a backslash (\\). +- +-.TP 8 +-(.,.)s ++.Em replacement ++if they are escaped with a backslash ++.Pq Ql \e . ++.It (.,.) Ns Em s + Repeats the last substitution. + This form of the +-.I `s' ++.Em s + command accepts a count suffix +-.IR `n' , ++.Em n Ns No , + or any combination of the characters +-.IR `r' , +-.IR `g' , ++.Em r Ns No , ++.Em g Ns No , + and +-.IR `p' . ++.Em p Ns No . + If a count suffix +-.I `n' ++.Em n + is given, then only the +-.IR n th ++.Em n Ns No th + match is replaced. + The +-.I `r' ++.Em r + suffix causes + the regular expression of the last search to be used instead of the + that of the last substitution. + The +-.I `g' ++.Em g + suffix toggles the global suffix of the last substitution. + The +-.I `p' ++.Em p + suffix toggles the print suffix of the last substitution + The current address is set to the last line affected. +- +-.TP 8 +-(.,.)t(.) ++.It (.,.) Ns Em t Ns No (.) + Copies (i.e., transfers) the addressed lines to after the right-hand + destination address, which may be the address +-.IR 0 ++.Em 0 + (zero). +-The current address is set to the last line +-copied. +- +-.TP 8 +-u ++The current address is set to the last line copied. ++.It Em u + Undoes the last command and restores the current address + to what it was before the command. + The global commands +-.IR `g' , +-.IR `G' , +-.IR `v' , ++.Em g Ns No , ++.Em G Ns No , ++.Em v Ns No , + and +-.IR `V' . ++.Em V Ns No . + are treated as a single command by undo. +-.I `u' ++.Em u + is its own inverse. +- +-.TP 8 +-.RI (1,$)v /pat/command-list ++.It (1,$) Ns Em v Ns No /re/command-list + Applies +-.I command-list ++.Em command-list + to each of the addressed lines not matching a regular expression +-.IR re . ++.Em re Ns No . + This is similar to the +-.I `g' ++.Em g + command. +- +-.TP 8 +-.RI (1,$)V /re/ ++.It (1,$) Ns Em V Ns No /re/ + Interactively edits the addressed lines not matching a regular expression +-.IR re. ++.Em re Ns No . + This is similar to the +-.I `G' ++.Em G + command. +- +-.TP 8 +-.RI (1,$)w \ file ++.It (1,$) Ns Em w No file + Writes the addressed lines to +-.IR file . ++.Em file Ns No . + Any previous contents of +-.I file ++.Em file + is lost without warning. + If there is no default filename, then the default filename is set to +-.IR file, +-otherwise it is unchanged. If no filename is specified, then the default +-filename is used. ++.Em file Ns No , ++otherwise it is unchanged. ++If no filename is specified, then the default filename is used. + The current address is unchanged. +- +-.TP 8 +-.RI (1,$)wq \ file ++.It (1,$) Ns Em wq No file + Writes the addressed lines to +-.IR file , ++.Em file Ns No , + and then executes a +-.I `q' ++.Em q + command. +- +-.TP 8 +-.RI (1,$)w \ !command ++.It (1,$) Ns Em w No !command + Writes the addressed lines to the standard input of +-.IR `!command' , ++.Em !command Ns No , + (see the +-.RI ! command +-below). ++.Em ! ++command below). + The default filename and current address are unchanged. +- +-.TP 8 +-.RI (1,$)W \ file ++.It (1,$) Ns Em W No file + Appends the addressed lines to the end of +-.IR file . ++.Em file Ns No . + This is similar to the +-.I `w' ++.Em w + command, expect that the previous contents of file is not clobbered. + The current address is unchanged. +- +-.TP 8 +-x +-Prompts for an encryption key which is used in subsequent reads and +-writes. If a newline alone is entered as the key, then encryption is +-turned off. Otherwise, echoing is disabled while a key is read. +-Encryption/decryption is done using the bdes(1) algorithm. +- +-.TP 8 +-.RI (.+1)z n ++.It Em x ++Prompts for an encryption key which is used in subsequent reads and writes. ++If a newline alone is entered as the key, then encryption is turned off. ++Otherwise, echoing is disabled while a key is read. ++Encryption/decryption is done using the ++.Xr bdes 1 ++algorithm. ++.It (.+1) Ns Em z Ns No n + Scrolls +-.I n +-lines at a time starting at addressed line. If +-.I n ++.Em n ++lines at a time starting at addressed line. ++If ++.Em n + is not specified, then the current window size is used. + The current address is set to the last line printed. +- +-.TP 8 +-.RI ! command ++.It ($) Ns Em = ++Prints the line number of the addressed line. ++.It (.+1) Ns Em newline ++Prints the addressed line, and sets the current address to that line. ++.It Em ! Ns No command + Executes +-.I command ++.Em command + via +-.IR sh (1). ++.Xr sh 1 . + If the first character of +-.I command +-is `!', then it is replaced by text of the +-previous +-.IR `!command' . +-.B ed ++.Em command ++is ++.Em ! Ns No , ++then it is replaced by text of the previous ++.Em !command Ns No . ++.Nm + does not process +-.I command +-for backslash (\\) escapes. ++.Em command ++for ++.Em \e ++(backslash) escapes. + However, an unescaped +-.I `%' ++.Em % + is replaced by the default filename. +-When the shell returns from execution, a `!' ++When the shell returns from execution, a ++.Em ! + is printed to the standard output. + The current line is unchanged. +- +-.TP 8 +-($)= +-Prints the line number of the addressed line. +- +-.TP 8 +-(.+1)newline +-Prints the addressed line, and sets the current address to +-that line. +- +-.SH FILES +-.TP 20 +-/tmp/ed.* +-Buffer file +-.PD 0 +-.TP 20 +-ed.hup +-The file to which +-.B ed +-attempts to write the buffer if the terminal hangs up. +- +-.SH SEE ALSO +- +-.IR vi (1), +-.IR sed (1), +-.IR regex (3), +-.IR bdes (1), +-.IR sh (1). +- +-USD:12-13 +- +-B. W. Kernighan and P. J. Plauger, +-.I Software Tools in Pascal , +-Addison-Wesley, 1981. +- +-.SH LIMITATIONS +-.B ed ++.El ++.Sh LIMITATIONS ++.Nm + processes +-.I file +-arguments for backslash escapes, i.e., in a filename, +-any characters preceded by a backslash (\\) are +-interpreted literally. +- ++.Em file ++arguments for backslash escapes, i.e., in a filename, ++any characters preceded by a backslash ++.Pq Ql \e ++are interpreted literally. ++.Pp + If a text (non-binary) file is not terminated by a newline character, + then +-.B ed +-appends one on reading/writing it. In the case of a binary file, +-.B ed ++.Nm ++appends one on reading/writing it. ++In the case of a binary file, ++.Nm + does not append a newline on reading/writing. +- +-per line overhead: 4 ints +- +-.SH DIAGNOSTICS ++.Sh DIAGNOSTICS + When an error occurs, +-.B ed +-prints a `?' and either returns to command mode +-or exits if its input is from a script. +-An explanation of the last error can be +-printed with the +-.I `h' ++.Nm ++prints a ++.Dq ? ++and either returns to command mode or exits if its input is from a script. ++An explanation of the last error can be printed with the ++.Em h + (help) command. +- +-Since the +-.I `g' +-(global) command masks any errors from failed searches and substitutions, ++.Pp ++Since the ++.Em g ++(global) command masks any errors from failed searches and substitutions, + it can be used to perform conditional operations in scripts; e.g., +-.sp +-.RS +-g/\fIold\fR/s//\fInew\fR/ +-.RE +-.sp ++.Bd -literal -offset indent ++g/old/s//new/ ++.Ed ++.Pp + replaces any occurrences of +-.I old ++.Em old + with +-.IR new . ++.Em new Ns No . ++.Pp + If the +-.I `u' ++.Em u + (undo) command occurs in a global command list, then + the command list is executed only once. +- ++.Pp + If diagnostics are not disabled, attempting to quit +-.B ed +-or edit another file before writing a modified buffer +-results in an error. ++.Nm ++or edit another file before writing a modified buffer results in an error. + If the command is entered a second time, it succeeds, + but any changes to the buffer are lost. ++.Sh FILES ++.Bl -tag -width /tmp/ed.* -compact ++.It Pa /tmp/ed.* ++buffer file ++.It Pa ed.hup ++where ++.Nm ++attempts to write the buffer if the terminal hangs up ++.El ++.Sh SEE ALSO ++.Xr bdes 1 , ++.Xr sed 1 , ++.Xr sh 1 , ++.Xr vi 1 , ++.Xr regex 3 ++.Pp ++USD:12-13 ++.Rs ++.%A B. W. Kernighan ++.%A P. J. Plauger ++.%B Software Tools in Pascal ++.%O Addison-Wesley ++.%D 1981 ++.Re ++.Sh HISTORY ++An ++.Nm ++command appeared in ++.At v1 . diff --git a/sys/src/cmd/diff/test/diff-t12.1 b/sys/src/cmd/diff/test/diff-t12.1 new file mode 100644 index 000000000..009bd692d --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t12.1 @@ -0,0 +1,10 @@ +# Test HMAC: + +PROG= hmactest +SRCS= hash.c hmactest.c +.PATH: ${.CURDIR}/../../ +NOMAN= +CFLAGS+= -I${.CURDIR}/../../ -Wall +DEBUG= -g + +.include <bsd.prog.mk>
\ No newline at end of file diff --git a/sys/src/cmd/diff/test/diff-t12.2 b/sys/src/cmd/diff/test/diff-t12.2 new file mode 100644 index 000000000..93a7a2f80 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t12.2 @@ -0,0 +1,12 @@ +# $OpenBSD: t12.2,v 1.1 2003/07/21 20:16:21 otto Exp $ + +# Test HMAC: + +PROG= hmactest +SRCS= hash.c hmactest.c +.PATH: ${.CURDIR}/../../ +NOMAN= +CFLAGS+= -I${.CURDIR}/../../ -Wall +DEBUG= -g + +.include <bsd.prog.mk>
\ No newline at end of file diff --git a/sys/src/cmd/diff/test/diff-t12.expected b/sys/src/cmd/diff/test/diff-t12.expected new file mode 100644 index 000000000..bc49aabf5 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t12.expected @@ -0,0 +1,8 @@ +--- diff-t12.1 ++++ diff-t12.2 +@@ -1,3 +1,5 @@ ++# $OpenBSD: t12.2,v 1.1 2003/07/21 20:16:21 otto Exp $ ++ + # Test HMAC: + + PROG= hmactest diff --git a/sys/src/cmd/diff/test/diff-t13.1 b/sys/src/cmd/diff/test/diff-t13.1 new file mode 100644 index 000000000..bedd72eee --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t13.1 @@ -0,0 +1,11 @@ +A line of text +. +Another line of text +.. +A third line +... +A fourth line +. +We keep counting +. +. diff --git a/sys/src/cmd/diff/test/diff-t13.2 b/sys/src/cmd/diff/test/diff-t13.2 new file mode 100644 index 000000000..3e00acf6e --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t13.2 @@ -0,0 +1,9 @@ +A line of text +Another line of text +.. +. +A third line +... +.. +We keep counting +. diff --git a/sys/src/cmd/diff/test/diff-t13.expected b/sys/src/cmd/diff/test/diff-t13.expected new file mode 100644 index 000000000..57bc1b6fd --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t13.expected @@ -0,0 +1,16 @@ +--- diff-t13.1 ++++ diff-t13.2 +@@ -1,11 +1,9 @@ + A line of text +-. + Another line of text + .. ++. + A third line + ... +-A fourth line +-. ++.. + We keep counting +-. + . diff --git a/sys/src/cmd/diff/test/diff-t14.1 b/sys/src/cmd/diff/test/diff-t14.1 new file mode 100644 index 000000000..7a754f414 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t14.1 @@ -0,0 +1,2 @@ +1 +2
\ No newline at end of file diff --git a/sys/src/cmd/diff/test/diff-t14.2 b/sys/src/cmd/diff/test/diff-t14.2 new file mode 100644 index 000000000..01e79c32a --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t14.2 @@ -0,0 +1,3 @@ +1 +2 +3 diff --git a/sys/src/cmd/diff/test/diff-t14.expected b/sys/src/cmd/diff/test/diff-t14.expected new file mode 100644 index 000000000..4f946034a --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t14.expected @@ -0,0 +1,7 @@ +--- diff-t14.1 ++++ diff-t14.2 +@@ -1,2 +1,3 @@ + 1 +-2 ++2 ++3 diff --git a/sys/src/cmd/diff/test/diff-t15.1 b/sys/src/cmd/diff/test/diff-t15.1 new file mode 100644 index 000000000..5f5fbe759 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t15.1 @@ -0,0 +1,3 @@ +1 +2 +3
\ No newline at end of file diff --git a/sys/src/cmd/diff/test/diff-t15.2 b/sys/src/cmd/diff/test/diff-t15.2 new file mode 100644 index 000000000..1191247b6 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t15.2 @@ -0,0 +1,2 @@ +1 +2 diff --git a/sys/src/cmd/diff/test/diff-t15.expected b/sys/src/cmd/diff/test/diff-t15.expected new file mode 100644 index 000000000..9743db4c2 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t15.expected @@ -0,0 +1,6 @@ +--- diff-t15.1 ++++ diff-t15.2 +@@ -1,3 +1,2 @@ + 1 + 2 +-3 diff --git a/sys/src/cmd/diff/test/diff-t2.1 b/sys/src/cmd/diff/test/diff-t2.1 new file mode 100644 index 000000000..e01fce04d --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t2.1 @@ -0,0 +1,25 @@ +Below is an example license to be used for new code in OpenBSD, +modeled after the ISC license. + +It is important to specify the year of the copyright. Additional years +should be separated by a comma, e.g. + Copyright (c) 2003, 2004 + +If you add extra text to the body of the license, be careful not to +add further restrictions. + +/* + * Copyright (c) CCYY YOUR NAME HERE <user@your.dom.ain> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/sys/src/cmd/diff/test/diff-t2.2 b/sys/src/cmd/diff/test/diff-t2.2 new file mode 100644 index 000000000..40a0f253d --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t2.2 @@ -0,0 +1,25 @@ +Below is an example license to be used for new code in OpenBSD, +modeled after the ISC license. + +It is important to specify the year of the copyright. Additional years +should be separated by a comma, e.g. + Copyright (c) 2003, 2004, 2005 + +If you add extra text to the body of the license, be careful not to +add further restrictions. + +/* + * Copyright (c) CCYY YOUR NAME HERE <user@your.dom.ain> + * + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +An extra line diff --git a/sys/src/cmd/diff/test/diff-t2.expected b/sys/src/cmd/diff/test/diff-t2.expected new file mode 100644 index 000000000..2eb7330d0 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t2.expected @@ -0,0 +1,24 @@ +--- diff-t2.1 ++++ diff-t2.2 +@@ -3,7 +3,7 @@ + + It is important to specify the year of the copyright. Additional years + should be separated by a comma, e.g. +- Copyright (c) 2003, 2004 ++ Copyright (c) 2003, 2004, 2005 + + If you add extra text to the body of the license, be careful not to + add further restrictions. +@@ -11,7 +11,6 @@ + /* + * Copyright (c) CCYY YOUR NAME HERE <user@your.dom.ain> + * +- * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * +@@ -23,3 +22,4 @@ + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ ++An extra line diff --git a/sys/src/cmd/diff/test/diff-t3.1 b/sys/src/cmd/diff/test/diff-t3.1 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t3.1 diff --git a/sys/src/cmd/diff/test/diff-t3.2 b/sys/src/cmd/diff/test/diff-t3.2 new file mode 100644 index 000000000..e01fce04d --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t3.2 @@ -0,0 +1,25 @@ +Below is an example license to be used for new code in OpenBSD, +modeled after the ISC license. + +It is important to specify the year of the copyright. Additional years +should be separated by a comma, e.g. + Copyright (c) 2003, 2004 + +If you add extra text to the body of the license, be careful not to +add further restrictions. + +/* + * Copyright (c) CCYY YOUR NAME HERE <user@your.dom.ain> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/sys/src/cmd/diff/test/diff-t3.expected b/sys/src/cmd/diff/test/diff-t3.expected new file mode 100644 index 000000000..1e58c5c88 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t3.expected @@ -0,0 +1,28 @@ +--- diff-t3.1 ++++ diff-t3.2 +@@ -1,0 +1,25 @@ ++Below is an example license to be used for new code in OpenBSD, ++modeled after the ISC license. ++ ++It is important to specify the year of the copyright. Additional years ++should be separated by a comma, e.g. ++ Copyright (c) 2003, 2004 ++ ++If you add extra text to the body of the license, be careful not to ++add further restrictions. ++ ++/* ++ * Copyright (c) CCYY YOUR NAME HERE <user@your.dom.ain> ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ diff --git a/sys/src/cmd/diff/test/diff-t4.1 b/sys/src/cmd/diff/test/diff-t4.1 new file mode 100644 index 000000000..e01fce04d --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t4.1 @@ -0,0 +1,25 @@ +Below is an example license to be used for new code in OpenBSD, +modeled after the ISC license. + +It is important to specify the year of the copyright. Additional years +should be separated by a comma, e.g. + Copyright (c) 2003, 2004 + +If you add extra text to the body of the license, be careful not to +add further restrictions. + +/* + * Copyright (c) CCYY YOUR NAME HERE <user@your.dom.ain> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/sys/src/cmd/diff/test/diff-t4.2 b/sys/src/cmd/diff/test/diff-t4.2 new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t4.2 diff --git a/sys/src/cmd/diff/test/diff-t4.expected b/sys/src/cmd/diff/test/diff-t4.expected new file mode 100644 index 000000000..95c30dde0 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t4.expected @@ -0,0 +1,28 @@ +--- diff-t4.1 ++++ diff-t4.2 +@@ -1,25 +1,0 @@ +-Below is an example license to be used for new code in OpenBSD, +-modeled after the ISC license. +- +-It is important to specify the year of the copyright. Additional years +-should be separated by a comma, e.g. +- Copyright (c) 2003, 2004 +- +-If you add extra text to the body of the license, be careful not to +-add further restrictions. +- +-/* +- * Copyright (c) CCYY YOUR NAME HERE <user@your.dom.ain> +- * +- * Permission to use, copy, modify, and distribute this software for any +- * purpose with or without fee is hereby granted, provided that the above +- * copyright notice and this permission notice appear in all copies. +- * +- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +- */ diff --git a/sys/src/cmd/diff/test/diff-t5.1 b/sys/src/cmd/diff/test/diff-t5.1 new file mode 100644 index 000000000..44312fc97 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t5.1 @@ -0,0 +1,10 @@ +OpenBSD 3.3-current (GENERIC) #47: Mon Jun 30 11:19:56 CEST 2003 + +Welcome to OpenBSD: The proactively secure Unix-like operating system. + +Please use the sendbug(1) utility to report bugs in the system. +Before reporting a bug, please try to reproduce it with the latest +version of the code. With bug reports, please try to ensure that +enough information to reproduce the problem is enclosed, and if a +known fix for it exists, include that as well. + diff --git a/sys/src/cmd/diff/test/diff-t5.2 b/sys/src/cmd/diff/test/diff-t5.2 new file mode 100644 index 000000000..44312fc97 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t5.2 @@ -0,0 +1,10 @@ +OpenBSD 3.3-current (GENERIC) #47: Mon Jun 30 11:19:56 CEST 2003 + +Welcome to OpenBSD: The proactively secure Unix-like operating system. + +Please use the sendbug(1) utility to report bugs in the system. +Before reporting a bug, please try to reproduce it with the latest +version of the code. With bug reports, please try to ensure that +enough information to reproduce the problem is enclosed, and if a +known fix for it exists, include that as well. + diff --git a/sys/src/cmd/diff/test/diff-t5.expected b/sys/src/cmd/diff/test/diff-t5.expected new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t5.expected diff --git a/sys/src/cmd/diff/test/diff-t6.1 b/sys/src/cmd/diff/test/diff-t6.1 new file mode 100644 index 000000000..0e009186b --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t6.1 @@ -0,0 +1,9 @@ +OpenBSD 3.3-current (GENERIC) #47: Mon Jun 30 11:19:56 CEST 2003 + +Welcome to OpenBSD: The proactively secure Unix-like operating system. + +Please use the sendbug(1) utility to report bugs in the system. +Before reporting a bug, please try to reproduce it with the latest +version of the code. With bug reports, please try to ensure that +enough information to reproduce the problem is enclosed, and if a +known fix for it exists, include that as well.
\ No newline at end of file diff --git a/sys/src/cmd/diff/test/diff-t6.2 b/sys/src/cmd/diff/test/diff-t6.2 new file mode 100644 index 000000000..54d08bd62 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t6.2 @@ -0,0 +1,9 @@ +OpenBSD 3.3-current (GENERIC) #47: Mon Jun 30 11:19:56 CEST 2003 + +Welcome to OpenBSD: The proactively secure Unix-like operating system. + +Please use the sendbug(1) utility to report bugs in the system. +Before reporting a bug, please try to reproduce it with the latest +version of the code. With bug reports, please try to ensure that +enough information to reproduce the problem is enclosed, and if a +known fix for it exists, include that as well. diff --git a/sys/src/cmd/diff/test/diff-t6.expected b/sys/src/cmd/diff/test/diff-t6.expected new file mode 100644 index 000000000..b3b25ad14 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t6.expected @@ -0,0 +1,8 @@ +--- diff-t6.1 ++++ diff-t6.2 +@@ -6,4 +6,4 @@ + Before reporting a bug, please try to reproduce it with the latest + version of the code. With bug reports, please try to ensure that + enough information to reproduce the problem is enclosed, and if a +-known fix for it exists, include that as well. ++known fix for it exists, include that as well. diff --git a/sys/src/cmd/diff/test/diff-t7.1 b/sys/src/cmd/diff/test/diff-t7.1 new file mode 100644 index 000000000..54d08bd62 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t7.1 @@ -0,0 +1,9 @@ +OpenBSD 3.3-current (GENERIC) #47: Mon Jun 30 11:19:56 CEST 2003 + +Welcome to OpenBSD: The proactively secure Unix-like operating system. + +Please use the sendbug(1) utility to report bugs in the system. +Before reporting a bug, please try to reproduce it with the latest +version of the code. With bug reports, please try to ensure that +enough information to reproduce the problem is enclosed, and if a +known fix for it exists, include that as well. diff --git a/sys/src/cmd/diff/test/diff-t7.2 b/sys/src/cmd/diff/test/diff-t7.2 new file mode 100644 index 000000000..0e009186b --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t7.2 @@ -0,0 +1,9 @@ +OpenBSD 3.3-current (GENERIC) #47: Mon Jun 30 11:19:56 CEST 2003 + +Welcome to OpenBSD: The proactively secure Unix-like operating system. + +Please use the sendbug(1) utility to report bugs in the system. +Before reporting a bug, please try to reproduce it with the latest +version of the code. With bug reports, please try to ensure that +enough information to reproduce the problem is enclosed, and if a +known fix for it exists, include that as well.
\ No newline at end of file diff --git a/sys/src/cmd/diff/test/diff-t7.expected b/sys/src/cmd/diff/test/diff-t7.expected new file mode 100644 index 000000000..fb331566a --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t7.expected @@ -0,0 +1,8 @@ +--- diff-t7.1 ++++ diff-t7.2 +@@ -6,4 +6,4 @@ + Before reporting a bug, please try to reproduce it with the latest + version of the code. With bug reports, please try to ensure that + enough information to reproduce the problem is enclosed, and if a +-known fix for it exists, include that as well. ++known fix for it exists, include that as well. diff --git a/sys/src/cmd/diff/test/diff-t8.1 b/sys/src/cmd/diff/test/diff-t8.1 new file mode 100644 index 000000000..d7e53ca4d --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t8.1 @@ -0,0 +1,392 @@ +/* $NetBSD: kern_malloc.c,v 1.11 1995/05/01 22:39:11 cgd Exp $ */ + +/* + * Copyright (c) 1987, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)kern_malloc.c 8.3 (Berkeley) 1/4/94 + */ + +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/map.h> +#include <sys/kernel.h> +#include <sys/malloc.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> + +struct kmembuckets bucket[MINBUCKET + 16]; +struct kmemstats kmemstats[M_LAST]; +struct kmemusage *kmemusage; +char *kmembase, *kmemlimit; +char *memname[] = INITKMEMNAMES; + +#ifdef DIAGNOSTIC +/* + * This structure provides a set of masks to catch unaligned frees. + */ +long addrmask[] = { 0, + 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, + 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, + 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, +}; + +/* + * The WEIRD_ADDR is used as known text to copy into free objects so + * that modifications after frees can be detected. + */ +#define WEIRD_ADDR 0xdeadbeef +#define MAX_COPY 32 + +/* + * Normally the freelist structure is used only to hold the list pointer + * for free objects. However, when running with diagnostics, the first + * 8 bytes of the structure is unused except for diagnostic information, + * and the free list pointer is at offst 8 in the structure. Since the + * first 8 bytes is the portion of the structure most often modified, this + * helps to detect memory reuse problems and avoid free list corruption. + */ +struct freelist { + int32_t spare0; + int16_t type; + int16_t spare1; + caddr_t next; +}; +#else /* !DIAGNOSTIC */ +struct freelist { + caddr_t next; +}; +#endif /* DIAGNOSTIC */ + +/* + * Allocate a block of memory + */ +void * +malloc(size, type, flags) + unsigned long size; + int type, flags; +{ + register struct kmembuckets *kbp; + register struct kmemusage *kup; + register struct freelist *freep; + long indx, npg, allocsize; + int s; + caddr_t va, cp, savedlist; +#ifdef DIAGNOSTIC + int32_t *end, *lp; + int copysize; + char *savedtype; +#endif +#ifdef KMEMSTATS + register struct kmemstats *ksp = &kmemstats[type]; + + if (((unsigned long)type) > M_LAST) + panic("malloc - bogus type"); +#endif + indx = BUCKETINDX(size); + kbp = &bucket[indx]; + s = splimp(); +#ifdef KMEMSTATS + while (ksp->ks_memuse >= ksp->ks_limit) { + if (flags & M_NOWAIT) { + splx(s); + return ((void *) NULL); + } + if (ksp->ks_limblocks < 65535) + ksp->ks_limblocks++; + tsleep((caddr_t)ksp, PSWP+2, memname[type], 0); + } + ksp->ks_size |= 1 << indx; +#endif +#ifdef DIAGNOSTIC + copysize = 1 << indx < MAX_COPY ? 1 << indx : MAX_COPY; +#endif + if (kbp->kb_next == NULL) { + kbp->kb_last = NULL; + if (size > MAXALLOCSAVE) + allocsize = roundup(size, CLBYTES); + else + allocsize = 1 << indx; + npg = clrnd(btoc(allocsize)); + va = (caddr_t) kmem_malloc(kmem_map, (vm_size_t)ctob(npg), + !(flags & M_NOWAIT)); + if (va == NULL) { + splx(s); + return ((void *) NULL); + } +#ifdef KMEMSTATS + kbp->kb_total += kbp->kb_elmpercl; +#endif + kup = btokup(va); + kup->ku_indx = indx; + if (allocsize > MAXALLOCSAVE) { + if (npg > 65535) + panic("malloc: allocation too large"); + kup->ku_pagecnt = npg; +#ifdef KMEMSTATS + ksp->ks_memuse += allocsize; +#endif + goto out; + } +#ifdef KMEMSTATS + kup->ku_freecnt = kbp->kb_elmpercl; + kbp->kb_totalfree += kbp->kb_elmpercl; +#endif + /* + * Just in case we blocked while allocating memory, + * and someone else also allocated memory for this + * bucket, don't assume the list is still empty. + */ + savedlist = kbp->kb_next; + kbp->kb_next = cp = va + (npg * NBPG) - allocsize; + for (;;) { + freep = (struct freelist *)cp; +#ifdef DIAGNOSTIC + /* + * Copy in known text to detect modification + * after freeing. + */ + end = (int32_t *)&cp[copysize]; + for (lp = (int32_t *)cp; lp < end; lp++) + *lp = WEIRD_ADDR; + freep->type = M_FREE; +#endif /* DIAGNOSTIC */ + if (cp <= va) + break; + cp -= allocsize; + freep->next = cp; + } + freep->next = savedlist; + if (kbp->kb_last == NULL) + kbp->kb_last = (caddr_t)freep; + } + va = kbp->kb_next; + kbp->kb_next = ((struct freelist *)va)->next; +#ifdef DIAGNOSTIC + freep = (struct freelist *)va; + savedtype = (unsigned)freep->type < M_LAST ? + memname[freep->type] : "???"; + if (kbp->kb_next && + !kernacc(kbp->kb_next, sizeof(struct freelist), 0)) { + printf("%s %d of object %p size %d %s %s (invalid addr %p)\n", + "Data modified on freelist: word", + (int32_t *)&kbp->kb_next - (int32_t *)kbp, va, size, + "previous type", savedtype, kbp->kb_next); + kbp->kb_next = NULL; + } + + /* Fill the fields that we've used with WEIRD_ADDR */ +#if BYTE_ORDER == BIG_ENDIAN + freep->type = WEIRD_ADDR >> 16; +#endif +#if BYTE_ORDER == LITTLE_ENDIAN + freep->type = (short)WEIRD_ADDR; +#endif + end = (int32_t *)&freep->next + + (sizeof(freep->next) / sizeof(int32_t)); + for (lp = (int32_t *)&freep->next; lp < end; lp++) + *lp = WEIRD_ADDR; + + /* and check that the data hasn't been modified. */ + end = (int32_t *)&va[copysize]; + for (lp = (int32_t *)va; lp < end; lp++) { + if (*lp == WEIRD_ADDR) + continue; + printf("%s %d of object %p size %d %s %s (%p != %p)\n", + "Data modified on freelist: word", lp - (int32_t *)va, + va, size, "previous type", savedtype, *lp, WEIRD_ADDR); + break; + } + + freep->spare0 = 0; +#endif /* DIAGNOSTIC */ +#ifdef KMEMSTATS + kup = btokup(va); + if (kup->ku_indx != indx) + panic("malloc: wrong bucket"); + if (kup->ku_freecnt == 0) + panic("malloc: lost data"); + kup->ku_freecnt--; + kbp->kb_totalfree--; + ksp->ks_memuse += 1 << indx; +out: + kbp->kb_calls++; + ksp->ks_inuse++; + ksp->ks_calls++; + if (ksp->ks_memuse > ksp->ks_maxused) + ksp->ks_maxused = ksp->ks_memuse; +#else +out: +#endif + splx(s); + return ((void *) va); +} + +/* + * Free a block of memory allocated by malloc. + */ +void +free(addr, type) + void *addr; + int type; +{ + register struct kmembuckets *kbp; + register struct kmemusage *kup; + register struct freelist *freep; + long size; + int s; +#ifdef DIAGNOSTIC + caddr_t cp; + int32_t *end, *lp; + long alloc, copysize; +#endif +#ifdef KMEMSTATS + register struct kmemstats *ksp = &kmemstats[type]; +#endif + + kup = btokup(addr); + size = 1 << kup->ku_indx; + kbp = &bucket[kup->ku_indx]; + s = splimp(); +#ifdef DIAGNOSTIC + /* + * Check for returns of data that do not point to the + * beginning of the allocation. + */ + if (size > NBPG * CLSIZE) + alloc = addrmask[BUCKETINDX(NBPG * CLSIZE)]; + else + alloc = addrmask[kup->ku_indx]; + if (((u_long)addr & alloc) != 0) + panic("free: unaligned addr 0x%x, size %d, type %s, mask %d\n", + addr, size, memname[type], alloc); +#endif /* DIAGNOSTIC */ + if (size > MAXALLOCSAVE) { + kmem_free(kmem_map, (vm_offset_t)addr, ctob(kup->ku_pagecnt)); +#ifdef KMEMSTATS + size = kup->ku_pagecnt << PGSHIFT; + ksp->ks_memuse -= size; + kup->ku_indx = 0; + kup->ku_pagecnt = 0; + if (ksp->ks_memuse + size >= ksp->ks_limit && + ksp->ks_memuse < ksp->ks_limit) + wakeup((caddr_t)ksp); + ksp->ks_inuse--; + kbp->kb_total -= 1; +#endif + splx(s); + return; + } + freep = (struct freelist *)addr; +#ifdef DIAGNOSTIC + /* + * Check for multiple frees. Use a quick check to see if + * it looks free before laboriously searching the freelist. + */ + if (freep->spare0 == WEIRD_ADDR) { + for (cp = kbp->kb_next; cp; cp = *(caddr_t *)cp) { + if (addr != cp) + continue; + printf("multiply freed item %p\n", addr); + panic("free: duplicated free"); + } + } + /* + * Copy in known text to detect modification after freeing + * and to make it look free. Also, save the type being freed + * so we can list likely culprit if modification is detected + * when the object is reallocated. + */ + copysize = size < MAX_COPY ? size : MAX_COPY; + end = (int32_t *)&((caddr_t)addr)[copysize]; + for (lp = (int32_t *)addr; lp < end; lp++) + *lp = WEIRD_ADDR; + freep->type = type; +#endif /* DIAGNOSTIC */ +#ifdef KMEMSTATS + kup->ku_freecnt++; + if (kup->ku_freecnt >= kbp->kb_elmpercl) + if (kup->ku_freecnt > kbp->kb_elmpercl) + panic("free: multiple frees"); + else if (kbp->kb_totalfree > kbp->kb_highwat) + kbp->kb_couldfree++; + kbp->kb_totalfree++; + ksp->ks_memuse -= size; + if (ksp->ks_memuse + size >= ksp->ks_limit && + ksp->ks_memuse < ksp->ks_limit) + wakeup((caddr_t)ksp); + ksp->ks_inuse--; +#endif + if (kbp->kb_next == NULL) + kbp->kb_next = addr; + else + ((struct freelist *)kbp->kb_last)->next = addr; + freep->next = NULL; + kbp->kb_last = addr; + splx(s); +} + +/* + * Initialize the kernel memory allocator + */ +kmeminit() +{ + register long indx; + int npg; + +#if ((MAXALLOCSAVE & (MAXALLOCSAVE - 1)) != 0) + ERROR!_kmeminit:_MAXALLOCSAVE_not_power_of_2 +#endif +#if (MAXALLOCSAVE > MINALLOCSIZE * 32768) + ERROR!_kmeminit:_MAXALLOCSAVE_too_big +#endif +#if (MAXALLOCSAVE < CLBYTES) + ERROR!_kmeminit:_MAXALLOCSAVE_too_small +#endif + + if (sizeof(struct freelist) > (1 << MINBUCKET)) + panic("minbucket too small/struct freelist too big"); + + npg = VM_KMEM_SIZE/ NBPG; + kmemusage = (struct kmemusage *) kmem_alloc(kernel_map, + (vm_size_t)(npg * sizeof(struct kmemusage))); + kmem_map = kmem_suballoc(kernel_map, (vm_offset_t *)&kmembase, + (vm_offset_t *)&kmemlimit, (vm_size_t)(npg * NBPG), FALSE); +#ifdef KMEMSTATS + for (indx = 0; indx < MINBUCKET + 16; indx++) { + if (1 << indx >= CLBYTES) + bucket[indx].kb_elmpercl = 1; + else + bucket[indx].kb_elmpercl = CLBYTES / (1 << indx); + bucket[indx].kb_highwat = 5 * bucket[indx].kb_elmpercl; + } + for (indx = 0; indx < M_LAST; indx++) + kmemstats[indx].ks_limit = npg * NBPG * 6 / 10; +#endif +} diff --git a/sys/src/cmd/diff/test/diff-t8.2 b/sys/src/cmd/diff/test/diff-t8.2 new file mode 100644 index 000000000..f84ad5c34 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t8.2 @@ -0,0 +1,616 @@ +/* $OpenBSD: t8.2,v 1.1 2003/07/17 21:04:04 otto Exp $ */ +/* $NetBSD: kern_malloc.c,v 1.15.4.2 1996/06/13 17:10:56 cgd Exp $ */ + +/* + * Copyright (c) 1987, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)kern_malloc.c 8.3 (Berkeley) 1/4/94 + */ + +#include <sys/param.h> +#include <sys/proc.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#include <sys/sysctl.h> + +#include <uvm/uvm_extern.h> + +static struct vm_map_intrsafe kmem_map_store; +struct vm_map *kmem_map = NULL; + +#ifdef NKMEMCLUSTERS +#error NKMEMCLUSTERS is obsolete; remove it from your kernel config file and use NKMEMPAGES instead or let the kernel auto-size +#endif + +/* + * Default number of pages in kmem_map. We attempt to calculate this + * at run-time, but allow it to be either patched or set in the kernel + * config file. + */ +#ifndef NKMEMPAGES +#define NKMEMPAGES 0 +#endif +int nkmempages = NKMEMPAGES; + +/* + * Defaults for lower- and upper-bounds for the kmem_map page count. + * Can be overridden by kernel config options. + */ +#ifndef NKMEMPAGES_MIN +#define NKMEMPAGES_MIN NKMEMPAGES_MIN_DEFAULT +#endif + +#ifndef NKMEMPAGES_MAX +#define NKMEMPAGES_MAX NKMEMPAGES_MAX_DEFAULT +#endif + +struct kmembuckets bucket[MINBUCKET + 16]; +struct kmemstats kmemstats[M_LAST]; +struct kmemusage *kmemusage; +char *kmembase, *kmemlimit; +char buckstring[16 * sizeof("123456,")]; +int buckstring_init = 0; +#if defined(KMEMSTATS) || defined(DIAGNOSTIC) || defined(FFS_SOFTUPDATES) +char *memname[] = INITKMEMNAMES; +char *memall = NULL; +extern struct lock sysctl_kmemlock; +#endif + +#ifdef DIAGNOSTIC +/* + * This structure provides a set of masks to catch unaligned frees. + */ +const long addrmask[] = { 0, + 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, + 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, + 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, +}; + +/* + * The WEIRD_ADDR is used as known text to copy into free objects so + * that modifications after frees can be detected. + */ +#define WEIRD_ADDR ((unsigned) 0xdeadbeef) +#define MAX_COPY 32 + +/* + * Normally the freelist structure is used only to hold the list pointer + * for free objects. However, when running with diagnostics, the first + * 8 bytes of the structure is unused except for diagnostic information, + * and the free list pointer is at offset 8 in the structure. Since the + * first 8 bytes is the portion of the structure most often modified, this + * helps to detect memory reuse problems and avoid free list corruption. + */ +struct freelist { + int32_t spare0; + int16_t type; + int16_t spare1; + caddr_t next; +}; +#else /* !DIAGNOSTIC */ +struct freelist { + caddr_t next; +}; +#endif /* DIAGNOSTIC */ + +/* + * Allocate a block of memory + */ +void * +malloc(size, type, flags) + unsigned long size; + int type, flags; +{ + register struct kmembuckets *kbp; + register struct kmemusage *kup; + register struct freelist *freep; + long indx, npg, allocsize; + int s; + caddr_t va, cp, savedlist; +#ifdef DIAGNOSTIC + int32_t *end, *lp; + int copysize; + char *savedtype; +#endif +#ifdef KMEMSTATS + register struct kmemstats *ksp = &kmemstats[type]; + + if (((unsigned long)type) >= M_LAST) + panic("malloc - bogus type"); +#endif + +#ifdef MALLOC_DEBUG + if (debug_malloc(size, type, flags, (void **)&va)) + return ((void *) va); +#endif + + indx = BUCKETINDX(size); + kbp = &bucket[indx]; + s = splvm(); +#ifdef KMEMSTATS + while (ksp->ks_memuse >= ksp->ks_limit) { + if (flags & M_NOWAIT) { + splx(s); + return ((void *) NULL); + } + if (ksp->ks_limblocks < 65535) + ksp->ks_limblocks++; + tsleep((caddr_t)ksp, PSWP+2, memname[type], 0); + } + ksp->ks_size |= 1 << indx; +#endif +#ifdef DIAGNOSTIC + copysize = 1 << indx < MAX_COPY ? 1 << indx : MAX_COPY; +#endif + if (kbp->kb_next == NULL) { + kbp->kb_last = NULL; + if (size > MAXALLOCSAVE) + allocsize = round_page(size); + else + allocsize = 1 << indx; + npg = btoc(allocsize); + va = (caddr_t) uvm_km_kmemalloc(kmem_map, uvmexp.kmem_object, + (vsize_t)ctob(npg), + (flags & M_NOWAIT) ? UVM_KMF_NOWAIT : 0); + if (va == NULL) { + /* + * Kmem_malloc() can return NULL, even if it can + * wait, if there is no map space available, because + * it can't fix that problem. Neither can we, + * right now. (We should release pages which + * are completely free and which are in buckets + * with too many free elements.) + */ + if ((flags & M_NOWAIT) == 0) + panic("malloc: out of space in kmem_map"); + splx(s); + return ((void *) NULL); + } +#ifdef KMEMSTATS + kbp->kb_total += kbp->kb_elmpercl; +#endif + kup = btokup(va); + kup->ku_indx = indx; + if (allocsize > MAXALLOCSAVE) { + if (npg > 65535) + panic("malloc: allocation too large"); + kup->ku_pagecnt = npg; +#ifdef KMEMSTATS + ksp->ks_memuse += allocsize; +#endif + goto out; + } +#ifdef KMEMSTATS + kup->ku_freecnt = kbp->kb_elmpercl; + kbp->kb_totalfree += kbp->kb_elmpercl; +#endif + /* + * Just in case we blocked while allocating memory, + * and someone else also allocated memory for this + * bucket, don't assume the list is still empty. + */ + savedlist = kbp->kb_next; + kbp->kb_next = cp = va + (npg * PAGE_SIZE) - allocsize; + for (;;) { + freep = (struct freelist *)cp; +#ifdef DIAGNOSTIC + /* + * Copy in known text to detect modification + * after freeing. + */ + end = (int32_t *)&cp[copysize]; + for (lp = (int32_t *)cp; lp < end; lp++) + *lp = WEIRD_ADDR; + freep->type = M_FREE; +#endif /* DIAGNOSTIC */ + if (cp <= va) + break; + cp -= allocsize; + freep->next = cp; + } + freep->next = savedlist; + if (kbp->kb_last == NULL) + kbp->kb_last = (caddr_t)freep; + } + va = kbp->kb_next; + kbp->kb_next = ((struct freelist *)va)->next; +#ifdef DIAGNOSTIC + freep = (struct freelist *)va; + savedtype = (unsigned)freep->type < M_LAST ? + memname[freep->type] : "???"; + if (kbp->kb_next) { + int rv; + vaddr_t addr = (vaddr_t)kbp->kb_next; + + vm_map_lock(kmem_map); + rv = uvm_map_checkprot(kmem_map, addr, + addr + sizeof(struct freelist), VM_PROT_WRITE); + vm_map_unlock(kmem_map); + + if (!rv) { + printf("%s %d of object %p size 0x%lx %s %s (invalid addr %p)\n", + "Data modified on freelist: word", + (int32_t *)&kbp->kb_next - (int32_t *)kbp, va, size, + "previous type", savedtype, kbp->kb_next); + kbp->kb_next = NULL; + } + } + + /* Fill the fields that we've used with WEIRD_ADDR */ +#if BYTE_ORDER == BIG_ENDIAN + freep->type = WEIRD_ADDR >> 16; +#endif +#if BYTE_ORDER == LITTLE_ENDIAN + freep->type = (short)WEIRD_ADDR; +#endif + end = (int32_t *)&freep->next + + (sizeof(freep->next) / sizeof(int32_t)); + for (lp = (int32_t *)&freep->next; lp < end; lp++) + *lp = WEIRD_ADDR; + + /* and check that the data hasn't been modified. */ + end = (int32_t *)&va[copysize]; + for (lp = (int32_t *)va; lp < end; lp++) { + if (*lp == WEIRD_ADDR) + continue; + printf("%s %d of object %p size 0x%lx %s %s (0x%x != 0x%x)\n", + "Data modified on freelist: word", lp - (int32_t *)va, + va, size, "previous type", savedtype, *lp, WEIRD_ADDR); + break; + } + + freep->spare0 = 0; +#endif /* DIAGNOSTIC */ +#ifdef KMEMSTATS + kup = btokup(va); + if (kup->ku_indx != indx) + panic("malloc: wrong bucket"); + if (kup->ku_freecnt == 0) + panic("malloc: lost data"); + kup->ku_freecnt--; + kbp->kb_totalfree--; + ksp->ks_memuse += 1 << indx; +out: + kbp->kb_calls++; + ksp->ks_inuse++; + ksp->ks_calls++; + if (ksp->ks_memuse > ksp->ks_maxused) + ksp->ks_maxused = ksp->ks_memuse; +#else +out: +#endif + splx(s); + return ((void *) va); +} + +/* + * Free a block of memory allocated by malloc. + */ +void +free(addr, type) + void *addr; + int type; +{ + register struct kmembuckets *kbp; + register struct kmemusage *kup; + register struct freelist *freep; + long size; + int s; +#ifdef DIAGNOSTIC + caddr_t cp; + int32_t *end, *lp; + long alloc, copysize; +#endif +#ifdef KMEMSTATS + register struct kmemstats *ksp = &kmemstats[type]; +#endif + +#ifdef MALLOC_DEBUG + if (debug_free(addr, type)) + return; +#endif + +#ifdef DIAGNOSTIC + if (addr < (void *)kmembase || addr >= (void *)kmemlimit) + panic("free: non-malloced addr %p type %s", addr, + memname[type]); +#endif + + kup = btokup(addr); + size = 1 << kup->ku_indx; + kbp = &bucket[kup->ku_indx]; + s = splvm(); +#ifdef DIAGNOSTIC + /* + * Check for returns of data that do not point to the + * beginning of the allocation. + */ + if (size > PAGE_SIZE) + alloc = addrmask[BUCKETINDX(PAGE_SIZE)]; + else + alloc = addrmask[kup->ku_indx]; + if (((u_long)addr & alloc) != 0) + panic("free: unaligned addr %p, size %ld, type %s, mask %ld", + addr, size, memname[type], alloc); +#endif /* DIAGNOSTIC */ + if (size > MAXALLOCSAVE) { + uvm_km_free(kmem_map, (vaddr_t)addr, ctob(kup->ku_pagecnt)); +#ifdef KMEMSTATS + size = kup->ku_pagecnt << PGSHIFT; + ksp->ks_memuse -= size; + kup->ku_indx = 0; + kup->ku_pagecnt = 0; + if (ksp->ks_memuse + size >= ksp->ks_limit && + ksp->ks_memuse < ksp->ks_limit) + wakeup((caddr_t)ksp); + ksp->ks_inuse--; + kbp->kb_total -= 1; +#endif + splx(s); + return; + } + freep = (struct freelist *)addr; +#ifdef DIAGNOSTIC + /* + * Check for multiple frees. Use a quick check to see if + * it looks free before laboriously searching the freelist. + */ + if (freep->spare0 == WEIRD_ADDR) { + for (cp = kbp->kb_next; cp; + cp = ((struct freelist *)cp)->next) { + if (addr != cp) + continue; + printf("multiply freed item %p\n", addr); + panic("free: duplicated free"); + } + } + /* + * Copy in known text to detect modification after freeing + * and to make it look free. Also, save the type being freed + * so we can list likely culprit if modification is detected + * when the object is reallocated. + */ + copysize = size < MAX_COPY ? size : MAX_COPY; + end = (int32_t *)&((caddr_t)addr)[copysize]; + for (lp = (int32_t *)addr; lp < end; lp++) + *lp = WEIRD_ADDR; + freep->type = type; +#endif /* DIAGNOSTIC */ +#ifdef KMEMSTATS + kup->ku_freecnt++; + if (kup->ku_freecnt >= kbp->kb_elmpercl) { + if (kup->ku_freecnt > kbp->kb_elmpercl) + panic("free: multiple frees"); + else if (kbp->kb_totalfree > kbp->kb_highwat) + kbp->kb_couldfree++; + } + kbp->kb_totalfree++; + ksp->ks_memuse -= size; + if (ksp->ks_memuse + size >= ksp->ks_limit && + ksp->ks_memuse < ksp->ks_limit) + wakeup((caddr_t)ksp); + ksp->ks_inuse--; +#endif + if (kbp->kb_next == NULL) + kbp->kb_next = addr; + else + ((struct freelist *)kbp->kb_last)->next = addr; + freep->next = NULL; + kbp->kb_last = addr; + splx(s); +} + +/* + * Compute the number of pages that kmem_map will map, that is, + * the size of the kernel malloc arena. + */ +void +kmeminit_nkmempages() +{ + int npages; + + if (nkmempages != 0) { + /* + * It's already been set (by us being here before, or + * by patching or kernel config options), bail out now. + */ + return; + } + + /* + * We use the following (simple) formula: + * + * - Starting point is physical memory / 4. + * + * - Clamp it down to NKMEMPAGES_MAX. + * + * - Round it up to NKMEMPAGES_MIN. + */ + npages = physmem / 4; + + if (npages > NKMEMPAGES_MAX) + npages = NKMEMPAGES_MAX; + + if (npages < NKMEMPAGES_MIN) + npages = NKMEMPAGES_MIN; + + nkmempages = npages; +} + +/* + * Initialize the kernel memory allocator + */ +void +kmeminit() +{ + vaddr_t base, limit; +#ifdef KMEMSTATS + long indx; +#endif + +#ifdef DIAGNOSTIC + if (sizeof(struct freelist) > (1 << MINBUCKET)) + panic("kmeminit: minbucket too small/struct freelist too big"); +#endif + + /* + * Compute the number of kmem_map pages, if we have not + * done so already. + */ + kmeminit_nkmempages(); + base = vm_map_min(kernel_map); + kmem_map = uvm_km_suballoc(kernel_map, &base, &limit, + (vsize_t)(nkmempages * PAGE_SIZE), VM_MAP_INTRSAFE, FALSE, + &kmem_map_store.vmi_map); + kmembase = (char *)base; + kmemlimit = (char *)limit; + kmemusage = (struct kmemusage *) uvm_km_zalloc(kernel_map, + (vsize_t)(nkmempages * sizeof(struct kmemusage))); +#ifdef KMEMSTATS + for (indx = 0; indx < MINBUCKET + 16; indx++) { + if (1 << indx >= PAGE_SIZE) + bucket[indx].kb_elmpercl = 1; + else + bucket[indx].kb_elmpercl = PAGE_SIZE / (1 << indx); + bucket[indx].kb_highwat = 5 * bucket[indx].kb_elmpercl; + } + for (indx = 0; indx < M_LAST; indx++) + kmemstats[indx].ks_limit = nkmempages * PAGE_SIZE * 6 / 10; +#endif +#ifdef MALLOC_DEBUG + debug_malloc_init(); +#endif +} + +/* + * Return kernel malloc statistics information. + */ +int +sysctl_malloc(name, namelen, oldp, oldlenp, newp, newlen, p) + int *name; + u_int namelen; + void *oldp; + size_t *oldlenp; + void *newp; + size_t newlen; + struct proc *p; +{ + struct kmembuckets kb; + int i, siz; + + if (namelen != 2 && name[0] != KERN_MALLOC_BUCKETS && + name[0] != KERN_MALLOC_KMEMNAMES) + return (ENOTDIR); /* overloaded */ + + switch (name[0]) { + case KERN_MALLOC_BUCKETS: + /* Initialize the first time */ + if (buckstring_init == 0) { + buckstring_init = 1; + bzero(buckstring, sizeof(buckstring)); + for (siz = 0, i = MINBUCKET; i < MINBUCKET + 16; i++) { + snprintf(buckstring + siz, + sizeof buckstring - siz, + "%d,", (u_int)(1<<i)); + siz += strlen(buckstring + siz); + } + /* Remove trailing comma */ + if (siz) + buckstring[siz - 1] = '\0'; + } + return (sysctl_rdstring(oldp, oldlenp, newp, buckstring)); + + case KERN_MALLOC_BUCKET: + bcopy(&bucket[BUCKETINDX(name[1])], &kb, sizeof(kb)); + kb.kb_next = kb.kb_last = 0; + return (sysctl_rdstruct(oldp, oldlenp, newp, &kb, sizeof(kb))); + case KERN_MALLOC_KMEMSTATS: +#ifdef KMEMSTATS + if ((name[1] < 0) || (name[1] >= M_LAST)) + return (EINVAL); + return (sysctl_rdstruct(oldp, oldlenp, newp, + &kmemstats[name[1]], sizeof(struct kmemstats))); +#else + return (EOPNOTSUPP); +#endif + case KERN_MALLOC_KMEMNAMES: +#if defined(KMEMSTATS) || defined(DIAGNOSTIC) || defined(FFS_SOFTUPDATES) + if (memall == NULL) { + int totlen; + + i = lockmgr(&sysctl_kmemlock, LK_EXCLUSIVE, NULL, p); + if (i) + return (i); + + /* Figure out how large a buffer we need */ + for (totlen = 0, i = 0; i < M_LAST; i++) { + if (memname[i]) + totlen += strlen(memname[i]); + totlen++; + } + memall = malloc(totlen + M_LAST, M_SYSCTL, M_WAITOK); + bzero(memall, totlen + M_LAST); + for (siz = 0, i = 0; i < M_LAST; i++) { + snprintf(memall + siz, + totlen + M_LAST - siz, + "%s,", memname[i] ? memname[i] : ""); + siz += strlen(memall + siz); + } + /* Remove trailing comma */ + if (siz) + memall[siz - 1] = '\0'; + + /* Now, convert all spaces to underscores */ + for (i = 0; i < totlen; i++) + if (memall[i] == ' ') + memall[i] = '_'; + lockmgr(&sysctl_kmemlock, LK_RELEASE, NULL, p); + } + return (sysctl_rdstring(oldp, oldlenp, newp, memall)); +#else + return (EOPNOTSUPP); +#endif + default: + return (EOPNOTSUPP); + } + /* NOTREACHED */ +} + +/* + * Round up a size to how much malloc would actually allocate. + */ +size_t +malloc_roundup(size_t sz) +{ + if (sz > MAXALLOCSAVE) + return round_page(sz); + + return (1 << BUCKETINDX(sz)); +} diff --git a/sys/src/cmd/diff/test/diff-t8.expected b/sys/src/cmd/diff/test/diff-t8.expected new file mode 100644 index 000000000..ffd949bf6 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t8.expected @@ -0,0 +1,460 @@ +--- diff-t8.1 ++++ diff-t8.2 +@@ -1,4 +1,5 @@ +-/* $NetBSD: kern_malloc.c,v 1.11 1995/05/01 22:39:11 cgd Exp $ */ ++/* $OpenBSD: t8.2,v 1.1 2003/07/17 21:04:04 otto Exp $ */ ++/* $NetBSD: kern_malloc.c,v 1.15.4.2 1996/06/13 17:10:56 cgd Exp $ */ + + /* + * Copyright (c) 1987, 1991, 1993 +@@ -33,24 +34,59 @@ + + #include <sys/param.h> + #include <sys/proc.h> +-#include <sys/map.h> + #include <sys/kernel.h> + #include <sys/malloc.h> ++#include <sys/systm.h> ++#include <sys/sysctl.h> + +-#include <vm/vm.h> +-#include <vm/vm_kern.h> ++#include <uvm/uvm_extern.h> ++ ++static struct vm_map_intrsafe kmem_map_store; ++struct vm_map *kmem_map = NULL; ++ ++#ifdef NKMEMCLUSTERS ++#error NKMEMCLUSTERS is obsolete; remove it from your kernel config file and use NKMEMPAGES instead or let the kernel auto-size ++#endif ++ ++/* ++ * Default number of pages in kmem_map. We attempt to calculate this ++ * at run-time, but allow it to be either patched or set in the kernel ++ * config file. ++ */ ++#ifndef NKMEMPAGES ++#define NKMEMPAGES 0 ++#endif ++int nkmempages = NKMEMPAGES; ++ ++/* ++ * Defaults for lower- and upper-bounds for the kmem_map page count. ++ * Can be overridden by kernel config options. ++ */ ++#ifndef NKMEMPAGES_MIN ++#define NKMEMPAGES_MIN NKMEMPAGES_MIN_DEFAULT ++#endif ++ ++#ifndef NKMEMPAGES_MAX ++#define NKMEMPAGES_MAX NKMEMPAGES_MAX_DEFAULT ++#endif + + struct kmembuckets bucket[MINBUCKET + 16]; + struct kmemstats kmemstats[M_LAST]; + struct kmemusage *kmemusage; + char *kmembase, *kmemlimit; ++char buckstring[16 * sizeof("123456,")]; ++int buckstring_init = 0; ++#if defined(KMEMSTATS) || defined(DIAGNOSTIC) || defined(FFS_SOFTUPDATES) + char *memname[] = INITKMEMNAMES; ++char *memall = NULL; ++extern struct lock sysctl_kmemlock; ++#endif + + #ifdef DIAGNOSTIC + /* + * This structure provides a set of masks to catch unaligned frees. + */ +-long addrmask[] = { 0, ++const long addrmask[] = { 0, + 0x00000001, 0x00000003, 0x00000007, 0x0000000f, + 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, + 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, +@@ -61,14 +97,14 @@ + * The WEIRD_ADDR is used as known text to copy into free objects so + * that modifications after frees can be detected. + */ +-#define WEIRD_ADDR 0xdeadbeef ++#define WEIRD_ADDR ((unsigned) 0xdeadbeef) + #define MAX_COPY 32 + + /* + * Normally the freelist structure is used only to hold the list pointer + * for free objects. However, when running with diagnostics, the first + * 8 bytes of the structure is unused except for diagnostic information, +- * and the free list pointer is at offst 8 in the structure. Since the ++ * and the free list pointer is at offset 8 in the structure. Since the + * first 8 bytes is the portion of the structure most often modified, this + * helps to detect memory reuse problems and avoid free list corruption. + */ +@@ -106,12 +142,18 @@ + #ifdef KMEMSTATS + register struct kmemstats *ksp = &kmemstats[type]; + +- if (((unsigned long)type) > M_LAST) ++ if (((unsigned long)type) >= M_LAST) + panic("malloc - bogus type"); + #endif ++ ++#ifdef MALLOC_DEBUG ++ if (debug_malloc(size, type, flags, (void **)&va)) ++ return ((void *) va); ++#endif ++ + indx = BUCKETINDX(size); + kbp = &bucket[indx]; +- s = splimp(); ++ s = splvm(); + #ifdef KMEMSTATS + while (ksp->ks_memuse >= ksp->ks_limit) { + if (flags & M_NOWAIT) { +@@ -130,13 +172,24 @@ + if (kbp->kb_next == NULL) { + kbp->kb_last = NULL; + if (size > MAXALLOCSAVE) +- allocsize = roundup(size, CLBYTES); ++ allocsize = round_page(size); + else + allocsize = 1 << indx; +- npg = clrnd(btoc(allocsize)); +- va = (caddr_t) kmem_malloc(kmem_map, (vm_size_t)ctob(npg), +- !(flags & M_NOWAIT)); ++ npg = btoc(allocsize); ++ va = (caddr_t) uvm_km_kmemalloc(kmem_map, uvmexp.kmem_object, ++ (vsize_t)ctob(npg), ++ (flags & M_NOWAIT) ? UVM_KMF_NOWAIT : 0); + if (va == NULL) { ++ /* ++ * Kmem_malloc() can return NULL, even if it can ++ * wait, if there is no map space available, because ++ * it can't fix that problem. Neither can we, ++ * right now. (We should release pages which ++ * are completely free and which are in buckets ++ * with too many free elements.) ++ */ ++ if ((flags & M_NOWAIT) == 0) ++ panic("malloc: out of space in kmem_map"); + splx(s); + return ((void *) NULL); + } +@@ -164,7 +217,7 @@ + * bucket, don't assume the list is still empty. + */ + savedlist = kbp->kb_next; +- kbp->kb_next = cp = va + (npg * NBPG) - allocsize; ++ kbp->kb_next = cp = va + (npg * PAGE_SIZE) - allocsize; + for (;;) { + freep = (struct freelist *)cp; + #ifdef DIAGNOSTIC +@@ -192,13 +245,22 @@ + freep = (struct freelist *)va; + savedtype = (unsigned)freep->type < M_LAST ? + memname[freep->type] : "???"; +- if (kbp->kb_next && +- !kernacc(kbp->kb_next, sizeof(struct freelist), 0)) { +- printf("%s %d of object %p size %d %s %s (invalid addr %p)\n", ++ if (kbp->kb_next) { ++ int rv; ++ vaddr_t addr = (vaddr_t)kbp->kb_next; ++ ++ vm_map_lock(kmem_map); ++ rv = uvm_map_checkprot(kmem_map, addr, ++ addr + sizeof(struct freelist), VM_PROT_WRITE); ++ vm_map_unlock(kmem_map); ++ ++ if (!rv) { ++ printf("%s %d of object %p size 0x%lx %s %s (invalid addr %p)\n", + "Data modified on freelist: word", + (int32_t *)&kbp->kb_next - (int32_t *)kbp, va, size, + "previous type", savedtype, kbp->kb_next); + kbp->kb_next = NULL; ++ } + } + + /* Fill the fields that we've used with WEIRD_ADDR */ +@@ -218,7 +280,7 @@ + for (lp = (int32_t *)va; lp < end; lp++) { + if (*lp == WEIRD_ADDR) + continue; +- printf("%s %d of object %p size %d %s %s (%p != %p)\n", ++ printf("%s %d of object %p size 0x%lx %s %s (0x%x != 0x%x)\n", + "Data modified on freelist: word", lp - (int32_t *)va, + va, size, "previous type", savedtype, *lp, WEIRD_ADDR); + break; +@@ -270,25 +332,36 @@ + register struct kmemstats *ksp = &kmemstats[type]; + #endif + ++#ifdef MALLOC_DEBUG ++ if (debug_free(addr, type)) ++ return; ++#endif ++ ++#ifdef DIAGNOSTIC ++ if (addr < (void *)kmembase || addr >= (void *)kmemlimit) ++ panic("free: non-malloced addr %p type %s", addr, ++ memname[type]); ++#endif ++ + kup = btokup(addr); + size = 1 << kup->ku_indx; + kbp = &bucket[kup->ku_indx]; +- s = splimp(); ++ s = splvm(); + #ifdef DIAGNOSTIC + /* + * Check for returns of data that do not point to the + * beginning of the allocation. + */ +- if (size > NBPG * CLSIZE) +- alloc = addrmask[BUCKETINDX(NBPG * CLSIZE)]; ++ if (size > PAGE_SIZE) ++ alloc = addrmask[BUCKETINDX(PAGE_SIZE)]; + else + alloc = addrmask[kup->ku_indx]; + if (((u_long)addr & alloc) != 0) +- panic("free: unaligned addr 0x%x, size %d, type %s, mask %d\n", ++ panic("free: unaligned addr %p, size %ld, type %s, mask %ld", + addr, size, memname[type], alloc); + #endif /* DIAGNOSTIC */ + if (size > MAXALLOCSAVE) { +- kmem_free(kmem_map, (vm_offset_t)addr, ctob(kup->ku_pagecnt)); ++ uvm_km_free(kmem_map, (vaddr_t)addr, ctob(kup->ku_pagecnt)); + #ifdef KMEMSTATS + size = kup->ku_pagecnt << PGSHIFT; + ksp->ks_memuse -= size; +@@ -310,7 +383,8 @@ + * it looks free before laboriously searching the freelist. + */ + if (freep->spare0 == WEIRD_ADDR) { +- for (cp = kbp->kb_next; cp; cp = *(caddr_t *)cp) { ++ for (cp = kbp->kb_next; cp; ++ cp = ((struct freelist *)cp)->next) { + if (addr != cp) + continue; + printf("multiply freed item %p\n", addr); +@@ -331,11 +405,12 @@ + #endif /* DIAGNOSTIC */ + #ifdef KMEMSTATS + kup->ku_freecnt++; +- if (kup->ku_freecnt >= kbp->kb_elmpercl) ++ if (kup->ku_freecnt >= kbp->kb_elmpercl) { + if (kup->ku_freecnt > kbp->kb_elmpercl) + panic("free: multiple frees"); + else if (kbp->kb_totalfree > kbp->kb_highwat) + kbp->kb_couldfree++; ++ } + kbp->kb_totalfree++; + ksp->ks_memuse -= size; + if (ksp->ks_memuse + size >= ksp->ks_limit && +@@ -353,40 +428,189 @@ + } + + /* ++ * Compute the number of pages that kmem_map will map, that is, ++ * the size of the kernel malloc arena. ++ */ ++void ++kmeminit_nkmempages() ++{ ++ int npages; ++ ++ if (nkmempages != 0) { ++ /* ++ * It's already been set (by us being here before, or ++ * by patching or kernel config options), bail out now. ++ */ ++ return; ++ } ++ ++ /* ++ * We use the following (simple) formula: ++ * ++ * - Starting point is physical memory / 4. ++ * ++ * - Clamp it down to NKMEMPAGES_MAX. ++ * ++ * - Round it up to NKMEMPAGES_MIN. ++ */ ++ npages = physmem / 4; ++ ++ if (npages > NKMEMPAGES_MAX) ++ npages = NKMEMPAGES_MAX; ++ ++ if (npages < NKMEMPAGES_MIN) ++ npages = NKMEMPAGES_MIN; ++ ++ nkmempages = npages; ++} ++ ++/* + * Initialize the kernel memory allocator + */ ++void + kmeminit() + { +- register long indx; +- int npg; +- +-#if ((MAXALLOCSAVE & (MAXALLOCSAVE - 1)) != 0) +- ERROR!_kmeminit:_MAXALLOCSAVE_not_power_of_2 +-#endif +-#if (MAXALLOCSAVE > MINALLOCSIZE * 32768) +- ERROR!_kmeminit:_MAXALLOCSAVE_too_big +-#endif +-#if (MAXALLOCSAVE < CLBYTES) +- ERROR!_kmeminit:_MAXALLOCSAVE_too_small ++ vaddr_t base, limit; ++#ifdef KMEMSTATS ++ long indx; + #endif + ++#ifdef DIAGNOSTIC + if (sizeof(struct freelist) > (1 << MINBUCKET)) +- panic("minbucket too small/struct freelist too big"); ++ panic("kmeminit: minbucket too small/struct freelist too big"); ++#endif + +- npg = VM_KMEM_SIZE/ NBPG; +- kmemusage = (struct kmemusage *) kmem_alloc(kernel_map, +- (vm_size_t)(npg * sizeof(struct kmemusage))); +- kmem_map = kmem_suballoc(kernel_map, (vm_offset_t *)&kmembase, +- (vm_offset_t *)&kmemlimit, (vm_size_t)(npg * NBPG), FALSE); ++ /* ++ * Compute the number of kmem_map pages, if we have not ++ * done so already. ++ */ ++ kmeminit_nkmempages(); ++ base = vm_map_min(kernel_map); ++ kmem_map = uvm_km_suballoc(kernel_map, &base, &limit, ++ (vsize_t)(nkmempages * PAGE_SIZE), VM_MAP_INTRSAFE, FALSE, ++ &kmem_map_store.vmi_map); ++ kmembase = (char *)base; ++ kmemlimit = (char *)limit; ++ kmemusage = (struct kmemusage *) uvm_km_zalloc(kernel_map, ++ (vsize_t)(nkmempages * sizeof(struct kmemusage))); + #ifdef KMEMSTATS + for (indx = 0; indx < MINBUCKET + 16; indx++) { +- if (1 << indx >= CLBYTES) ++ if (1 << indx >= PAGE_SIZE) + bucket[indx].kb_elmpercl = 1; + else +- bucket[indx].kb_elmpercl = CLBYTES / (1 << indx); ++ bucket[indx].kb_elmpercl = PAGE_SIZE / (1 << indx); + bucket[indx].kb_highwat = 5 * bucket[indx].kb_elmpercl; + } + for (indx = 0; indx < M_LAST; indx++) +- kmemstats[indx].ks_limit = npg * NBPG * 6 / 10; ++ kmemstats[indx].ks_limit = nkmempages * PAGE_SIZE * 6 / 10; ++#endif ++#ifdef MALLOC_DEBUG ++ debug_malloc_init(); ++#endif ++} ++ ++/* ++ * Return kernel malloc statistics information. ++ */ ++int ++sysctl_malloc(name, namelen, oldp, oldlenp, newp, newlen, p) ++ int *name; ++ u_int namelen; ++ void *oldp; ++ size_t *oldlenp; ++ void *newp; ++ size_t newlen; ++ struct proc *p; ++{ ++ struct kmembuckets kb; ++ int i, siz; ++ ++ if (namelen != 2 && name[0] != KERN_MALLOC_BUCKETS && ++ name[0] != KERN_MALLOC_KMEMNAMES) ++ return (ENOTDIR); /* overloaded */ ++ ++ switch (name[0]) { ++ case KERN_MALLOC_BUCKETS: ++ /* Initialize the first time */ ++ if (buckstring_init == 0) { ++ buckstring_init = 1; ++ bzero(buckstring, sizeof(buckstring)); ++ for (siz = 0, i = MINBUCKET; i < MINBUCKET + 16; i++) { ++ snprintf(buckstring + siz, ++ sizeof buckstring - siz, ++ "%d,", (u_int)(1<<i)); ++ siz += strlen(buckstring + siz); ++ } ++ /* Remove trailing comma */ ++ if (siz) ++ buckstring[siz - 1] = '\0'; ++ } ++ return (sysctl_rdstring(oldp, oldlenp, newp, buckstring)); ++ ++ case KERN_MALLOC_BUCKET: ++ bcopy(&bucket[BUCKETINDX(name[1])], &kb, sizeof(kb)); ++ kb.kb_next = kb.kb_last = 0; ++ return (sysctl_rdstruct(oldp, oldlenp, newp, &kb, sizeof(kb))); ++ case KERN_MALLOC_KMEMSTATS: ++#ifdef KMEMSTATS ++ if ((name[1] < 0) || (name[1] >= M_LAST)) ++ return (EINVAL); ++ return (sysctl_rdstruct(oldp, oldlenp, newp, ++ &kmemstats[name[1]], sizeof(struct kmemstats))); ++#else ++ return (EOPNOTSUPP); + #endif ++ case KERN_MALLOC_KMEMNAMES: ++#if defined(KMEMSTATS) || defined(DIAGNOSTIC) || defined(FFS_SOFTUPDATES) ++ if (memall == NULL) { ++ int totlen; ++ ++ i = lockmgr(&sysctl_kmemlock, LK_EXCLUSIVE, NULL, p); ++ if (i) ++ return (i); ++ ++ /* Figure out how large a buffer we need */ ++ for (totlen = 0, i = 0; i < M_LAST; i++) { ++ if (memname[i]) ++ totlen += strlen(memname[i]); ++ totlen++; ++ } ++ memall = malloc(totlen + M_LAST, M_SYSCTL, M_WAITOK); ++ bzero(memall, totlen + M_LAST); ++ for (siz = 0, i = 0; i < M_LAST; i++) { ++ snprintf(memall + siz, ++ totlen + M_LAST - siz, ++ "%s,", memname[i] ? memname[i] : ""); ++ siz += strlen(memall + siz); ++ } ++ /* Remove trailing comma */ ++ if (siz) ++ memall[siz - 1] = '\0'; ++ ++ /* Now, convert all spaces to underscores */ ++ for (i = 0; i < totlen; i++) ++ if (memall[i] == ' ') ++ memall[i] = '_'; ++ lockmgr(&sysctl_kmemlock, LK_RELEASE, NULL, p); ++ } ++ return (sysctl_rdstring(oldp, oldlenp, newp, memall)); ++#else ++ return (EOPNOTSUPP); ++#endif ++ default: ++ return (EOPNOTSUPP); ++ } ++ /* NOTREACHED */ ++} ++ ++/* ++ * Round up a size to how much malloc would actually allocate. ++ */ ++size_t ++malloc_roundup(size_t sz) ++{ ++ if (sz > MAXALLOCSAVE) ++ return round_page(sz); ++ ++ return (1 << BUCKETINDX(sz)); + } diff --git a/sys/src/cmd/diff/test/diff-t9.1 b/sys/src/cmd/diff/test/diff-t9.1 new file mode 100644 index 000000000..5f4d84322 --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t9.1 @@ -0,0 +1,2045 @@ +/* $NetBSD: vfs_syscalls.c,v 1.57 1995/10/07 06:28:51 mycroft Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)vfs_syscalls.c 8.28 (Berkeley) 12/10/94 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/filedesc.h> +#include <sys/kernel.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/proc.h> +#include <sys/uio.h> +#include <sys/malloc.h> +#include <sys/dirent.h> + +#include <sys/syscallargs.h> + +#include <vm/vm.h> +#include <sys/sysctl.h> + +static int change_dir __P((struct nameidata *ndp, struct proc *p)); + +/* + * Virtual File System System Calls + */ + +/* + * Mount a file system. + */ +/* ARGSUSED */ +sys_mount(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_mount_args /* { + syscallarg(char *) type; + syscallarg(char *) path; + syscallarg(int) flags; + syscallarg(caddr_t) data; + } */ *uap = v; + register struct vnode *vp; + register struct mount *mp; + int error, flag; + u_long fsindex; + char fstypename[MFSNAMELEN]; + struct vattr va; + struct nameidata nd; + + /* + * Get vnode to be covered + */ + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (SCARG(uap, flags) & MNT_UPDATE) { + if ((vp->v_flag & VROOT) == 0) { + vput(vp); + return (EINVAL); + } + mp = vp->v_mount; + flag = mp->mnt_flag; + /* + * We only allow the filesystem to be reloaded if it + * is currently mounted read-only. + */ + if ((SCARG(uap, flags) & MNT_RELOAD) && + ((mp->mnt_flag & MNT_RDONLY) == 0)) { + vput(vp); + return (EOPNOTSUPP); /* Needs translation */ + } + mp->mnt_flag |= + SCARG(uap, flags) & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE); + /* + * Only root, or the user that did the original mount is + * permitted to update it. + */ + if (mp->mnt_stat.f_owner != p->p_ucred->cr_uid && + (error = suser(p->p_ucred, &p->p_acflag))) { + vput(vp); + return (error); + } + /* + * Do not allow NFS export by non-root users. Silently + * enforce MNT_NOSUID and MNT_NODEV for non-root users. + */ + if (p->p_ucred->cr_uid != 0) { + if (SCARG(uap, flags) & MNT_EXPORTED) { + vput(vp); + return (EPERM); + } + SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; + } + VOP_UNLOCK(vp); + goto update; + } + /* + * If the user is not root, ensure that they own the directory + * onto which we are attempting to mount. + */ + if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) || + (va.va_uid != p->p_ucred->cr_uid && + (error = suser(p->p_ucred, &p->p_acflag)))) { + vput(vp); + return (error); + } + /* + * Do not allow NFS export by non-root users. Silently + * enforce MNT_NOSUID and MNT_NODEV for non-root users. + */ + if (p->p_ucred->cr_uid != 0) { + if (SCARG(uap, flags) & MNT_EXPORTED) { + vput(vp); + return (EPERM); + } + SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; + } + if (error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) + return (error); + if (vp->v_type != VDIR) { + vput(vp); + return (ENOTDIR); + } + if (error = copyinstr(SCARG(uap, type), fstypename, MFSNAMELEN, + (size_t *)0)) { +#if defined(COMPAT_09) || defined(COMPAT_43) + /* + * Historically filesystem types were identified by number. + * If we get an integer for the filesystem type instead of a + * string, we check to see if it matches one of the historic + * filesystem types. + */ + fsindex = (u_long)SCARG(uap, type); + if (fsindex >= nvfssw || vfssw[fsindex] == NULL) { + vput(vp); + return (ENODEV); + } + strncpy(fstypename, vfssw[fsindex]->vfs_name, MFSNAMELEN); +#else + vput(vp); + return (error); +#endif + } + for (fsindex = 0; fsindex < nvfssw; fsindex++) + if (vfssw[fsindex] != NULL && + !strncmp(vfssw[fsindex]->vfs_name, fstypename, MFSNAMELEN)) + break; + if (fsindex >= nvfssw) { + vput(vp); + return (ENODEV); + } + if (vp->v_mountedhere != NULL) { + vput(vp); + return (EBUSY); + } + + /* + * Allocate and initialize the file system. + */ + mp = (struct mount *)malloc((u_long)sizeof(struct mount), + M_MOUNT, M_WAITOK); + bzero((char *)mp, (u_long)sizeof(struct mount)); + mp->mnt_op = vfssw[fsindex]; + if (error = vfs_lock(mp)) { + free((caddr_t)mp, M_MOUNT); + vput(vp); + return (error); + } + /* Do this early in case we block later. */ + vfssw[fsindex]->vfs_refcount++; + vp->v_mountedhere = mp; + mp->mnt_vnodecovered = vp; + mp->mnt_stat.f_owner = p->p_ucred->cr_uid; +update: + /* + * Set the mount level flags. + */ + if (SCARG(uap, flags) & MNT_RDONLY) + mp->mnt_flag |= MNT_RDONLY; + else if (mp->mnt_flag & MNT_RDONLY) + mp->mnt_flag |= MNT_WANTRDWR; + mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | + MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); + mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC | + MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); + /* + * Mount the filesystem. + */ + error = VFS_MOUNT(mp, SCARG(uap, path), SCARG(uap, data), &nd, p); + if (mp->mnt_flag & MNT_UPDATE) { + vrele(vp); + if (mp->mnt_flag & MNT_WANTRDWR) + mp->mnt_flag &= ~MNT_RDONLY; + mp->mnt_flag &=~ + (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_WANTRDWR); + if (error) + mp->mnt_flag = flag; + return (error); + } + /* + * Put the new filesystem on the mount list after root. + */ + cache_purge(vp); + if (!error) { + TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); + checkdirs(vp); + VOP_UNLOCK(vp); + vfs_unlock(mp); + (void) VFS_STATFS(mp, &mp->mnt_stat, p); + error = VFS_START(mp, 0, p); + } else { + mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; + vfssw[fsindex]->vfs_refcount--; + vfs_unlock(mp); + free((caddr_t)mp, M_MOUNT); + vput(vp); + } + return (error); +} + +/* + * Scan all active processes to see if any of them have a current + * or root directory onto which the new filesystem has just been + * mounted. If so, replace them with the new mount point. + */ +checkdirs(olddp) + struct vnode *olddp; +{ + struct filedesc *fdp; + struct vnode *newdp; + struct proc *p; + + if (olddp->v_usecount == 1) + return; + if (VFS_ROOT(olddp->v_mountedhere, &newdp)) + panic("mount: lost mount"); + for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) { + fdp = p->p_fd; + if (fdp->fd_cdir == olddp) { + vrele(fdp->fd_cdir); + VREF(newdp); + fdp->fd_cdir = newdp; + } + if (fdp->fd_rdir == olddp) { + vrele(fdp->fd_rdir); + VREF(newdp); + fdp->fd_rdir = newdp; + } + } + if (rootvnode == olddp) { + vrele(rootvnode); + VREF(newdp); + rootvnode = newdp; + } + vput(newdp); +} + +/* + * Unmount a file system. + * + * Note: unmount takes a path to the vnode mounted on as argument, + * not special file (as before). + */ +/* ARGSUSED */ +sys_unmount(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_unmount_args /* { + syscallarg(char *) path; + syscallarg(int) flags; + } */ *uap = v; + register struct vnode *vp; + struct mount *mp; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + mp = vp->v_mount; + + /* + * Only root, or the user that did the original mount is + * permitted to unmount this filesystem. + */ + if ((mp->mnt_stat.f_owner != p->p_ucred->cr_uid) && + (error = suser(p->p_ucred, &p->p_acflag))) { + vput(vp); + return (error); + } + + /* + * Don't allow unmounting the root file system. + */ + if (mp->mnt_flag & MNT_ROOTFS) { + vput(vp); + return (EINVAL); + } + + /* + * Must be the root of the filesystem + */ + if ((vp->v_flag & VROOT) == 0) { + vput(vp); + return (EINVAL); + } + vput(vp); + return (dounmount(mp, SCARG(uap, flags), p)); +} + +/* + * Do the actual file system unmount. + */ +dounmount(mp, flags, p) + register struct mount *mp; + int flags; + struct proc *p; +{ + struct vnode *coveredvp; + int error; + + coveredvp = mp->mnt_vnodecovered; + if (vfs_busy(mp)) + return (EBUSY); + mp->mnt_flag |= MNT_UNMOUNT; + if (error = vfs_lock(mp)) + return (error); + + mp->mnt_flag &=~ MNT_ASYNC; + vnode_pager_umount(mp); /* release cached vnodes */ + cache_purgevfs(mp); /* remove cache entries for this file sys */ + if ((error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0 || + (flags & MNT_FORCE)) + error = VFS_UNMOUNT(mp, flags, p); + mp->mnt_flag &= ~MNT_UNMOUNT; + vfs_unbusy(mp); + if (error) { + vfs_unlock(mp); + } else { + TAILQ_REMOVE(&mountlist, mp, mnt_list); + if (coveredvp != NULLVP) { + vrele(coveredvp); + coveredvp->v_mountedhere = (struct mount *)0; + } + mp->mnt_op->vfs_refcount--; + vfs_unlock(mp); + if (mp->mnt_vnodelist.lh_first != NULL) + panic("unmount: dangling vnode"); + free((caddr_t)mp, M_MOUNT); + } + return (error); +} + +/* + * Sync each mounted filesystem. + */ +#ifdef DEBUG +int syncprt = 0; +struct ctldebug debug0 = { "syncprt", &syncprt }; +#endif + +/* ARGSUSED */ +sys_sync(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct mount *mp, *nmp; + int asyncflag; + + for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) { + /* + * Get the next pointer in case we hang on vfs_busy + * while we are being unmounted. + */ + nmp = mp->mnt_list.cqe_next; + /* + * The lock check below is to avoid races with mount + * and unmount. + */ + if ((mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_MPBUSY)) == 0 && + !vfs_busy(mp)) { + asyncflag = mp->mnt_flag & MNT_ASYNC; + mp->mnt_flag &= ~MNT_ASYNC; + VFS_SYNC(mp, MNT_NOWAIT, p->p_ucred, p); + if (asyncflag) + mp->mnt_flag |= MNT_ASYNC; + /* + * Get the next pointer again, as the next filesystem + * might have been unmounted while we were sync'ing. + */ + nmp = mp->mnt_list.cqe_next; + vfs_unbusy(mp); + } + } +#ifdef DEBUG + if (syncprt) + vfs_bufstats(); +#endif /* DEBUG */ + return (0); +} + +/* + * Change filesystem quotas. + */ +/* ARGSUSED */ +sys_quotactl(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_quotactl_args /* { + syscallarg(char *) path; + syscallarg(int) cmd; + syscallarg(int) uid; + syscallarg(caddr_t) arg; + } */ *uap = v; + register struct mount *mp; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + mp = nd.ni_vp->v_mount; + vrele(nd.ni_vp); + return (VFS_QUOTACTL(mp, SCARG(uap, cmd), SCARG(uap, uid), + SCARG(uap, arg), p)); +} + +/* + * Get filesystem statistics. + */ +/* ARGSUSED */ +sys_statfs(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_statfs_args /* { + syscallarg(char *) path; + syscallarg(struct statfs *) buf; + } */ *uap = v; + register struct mount *mp; + register struct statfs *sp; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + mp = nd.ni_vp->v_mount; + sp = &mp->mnt_stat; + vrele(nd.ni_vp); + if (error = VFS_STATFS(mp, sp, p)) + return (error); + sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; + return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); +} + +/* + * Get filesystem statistics. + */ +/* ARGSUSED */ +sys_fstatfs(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_fstatfs_args /* { + syscallarg(int) fd; + syscallarg(struct statfs *) buf; + } */ *uap = v; + struct file *fp; + struct mount *mp; + register struct statfs *sp; + int error; + + if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) + return (error); + mp = ((struct vnode *)fp->f_data)->v_mount; + sp = &mp->mnt_stat; + if (error = VFS_STATFS(mp, sp, p)) + return (error); + sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; + return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); +} + +/* + * Get statistics on all filesystems. + */ +sys_getfsstat(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_getfsstat_args /* { + syscallarg(struct statfs *) buf; + syscallarg(long) bufsize; + syscallarg(int) flags; + } */ *uap = v; + register struct mount *mp, *nmp; + register struct statfs *sp; + caddr_t sfsp; + long count, maxcount, error; + + maxcount = SCARG(uap, bufsize) / sizeof(struct statfs); + sfsp = (caddr_t)SCARG(uap, buf); + for (count = 0, + mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) { + nmp = mp->mnt_list.cqe_next; + if (sfsp && count < maxcount && + ((mp->mnt_flag & MNT_MLOCK) == 0)) { + sp = &mp->mnt_stat; + /* + * If MNT_NOWAIT is specified, do not refresh the + * fsstat cache. MNT_WAIT overrides MNT_NOWAIT. + */ + if (((SCARG(uap, flags) & MNT_NOWAIT) == 0 || + (SCARG(uap, flags) & MNT_WAIT)) && + (error = VFS_STATFS(mp, sp, p))) + continue; + sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; + if (error = copyout((caddr_t)sp, sfsp, sizeof(*sp))) + return (error); + sfsp += sizeof(*sp); + } + count++; + } + if (sfsp && count > maxcount) + *retval = maxcount; + else + *retval = count; + return (0); +} + +/* + * Change current working directory to a given file descriptor. + */ +/* ARGSUSED */ +sys_fchdir(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_fchdir_args /* { + syscallarg(int) fd; + } */ *uap = v; + register struct filedesc *fdp = p->p_fd; + struct vnode *vp, *tdp; + struct mount *mp; + struct file *fp; + int error; + + if (error = getvnode(fdp, SCARG(uap, fd), &fp)) + return (error); + vp = (struct vnode *)fp->f_data; + VREF(vp); + VOP_LOCK(vp); + if (vp->v_type != VDIR) + error = ENOTDIR; + else + error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); + while (!error && (mp = vp->v_mountedhere) != NULL) { + if (mp->mnt_flag & MNT_MLOCK) { + mp->mnt_flag |= MNT_MWAIT; + sleep((caddr_t)mp, PVFS); + continue; + } + if (error = VFS_ROOT(mp, &tdp)) + break; + vput(vp); + vp = tdp; + } + VOP_UNLOCK(vp); + if (error) { + vrele(vp); + return (error); + } + vrele(fdp->fd_cdir); + fdp->fd_cdir = vp; + return (0); +} + +/* + * Change current working directory (``.''). + */ +/* ARGSUSED */ +sys_chdir(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_chdir_args /* { + syscallarg(char *) path; + } */ *uap = v; + register struct filedesc *fdp = p->p_fd; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if (error = change_dir(&nd, p)) + return (error); + vrele(fdp->fd_cdir); + fdp->fd_cdir = nd.ni_vp; + return (0); +} + +/* + * Change notion of root (``/'') directory. + */ +/* ARGSUSED */ +sys_chroot(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_chroot_args /* { + syscallarg(char *) path; + } */ *uap = v; + register struct filedesc *fdp = p->p_fd; + int error; + struct nameidata nd; + + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if (error = change_dir(&nd, p)) + return (error); + if (fdp->fd_rdir != NULL) + vrele(fdp->fd_rdir); + fdp->fd_rdir = nd.ni_vp; + return (0); +} + +/* + * Common routine for chroot and chdir. + */ +static int +change_dir(ndp, p) + register struct nameidata *ndp; + struct proc *p; +{ + struct vnode *vp; + int error; + + if (error = namei(ndp)) + return (error); + vp = ndp->ni_vp; + if (vp->v_type != VDIR) + error = ENOTDIR; + else + error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); + VOP_UNLOCK(vp); + if (error) + vrele(vp); + return (error); +} + +/* + * Check permissions, allocate an open file structure, + * and call the device open routine if any. + */ +sys_open(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_open_args /* { + syscallarg(char *) path; + syscallarg(int) flags; + syscallarg(int) mode; + } */ *uap = v; + register struct filedesc *fdp = p->p_fd; + register struct file *fp; + register struct vnode *vp; + int flags, cmode; + struct file *nfp; + int type, indx, error; + struct flock lf; + struct nameidata nd; + extern struct fileops vnops; + + if (error = falloc(p, &nfp, &indx)) + return (error); + fp = nfp; + flags = FFLAGS(SCARG(uap, flags)); + cmode = ((SCARG(uap, mode) &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + p->p_dupfd = -indx - 1; /* XXX check for fdopen */ + if (error = vn_open(&nd, flags, cmode)) { + ffree(fp); + if ((error == ENODEV || error == ENXIO) && + p->p_dupfd >= 0 && /* XXX from fdopen */ + (error = + dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) { + *retval = indx; + return (0); + } + if (error == ERESTART) + error = EINTR; + fdp->fd_ofiles[indx] = NULL; + return (error); + } + p->p_dupfd = 0; + vp = nd.ni_vp; + fp->f_flag = flags & FMASK; + fp->f_type = DTYPE_VNODE; + fp->f_ops = &vnops; + fp->f_data = (caddr_t)vp; + if (flags & (O_EXLOCK | O_SHLOCK)) { + lf.l_whence = SEEK_SET; + lf.l_start = 0; + lf.l_len = 0; + if (flags & O_EXLOCK) + lf.l_type = F_WRLCK; + else + lf.l_type = F_RDLCK; + type = F_FLOCK; + if ((flags & FNONBLOCK) == 0) + type |= F_WAIT; + VOP_UNLOCK(vp); + if (error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) { + (void) vn_close(vp, fp->f_flag, fp->f_cred, p); + ffree(fp); + fdp->fd_ofiles[indx] = NULL; + return (error); + } + VOP_LOCK(vp); + fp->f_flag |= FHASLOCK; + } + VOP_UNLOCK(vp); + *retval = indx; + return (0); +} + +/* + * Create a special file. + */ +/* ARGSUSED */ +sys_mknod(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_mknod_args /* { + syscallarg(char *) path; + syscallarg(int) mode; + syscallarg(int) dev; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + int whiteout; + struct nameidata nd; + + if (error = suser(p->p_ucred, &p->p_acflag)) + return (error); + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp != NULL) + error = EEXIST; + else { + VATTR_NULL(&vattr); + vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; + vattr.va_rdev = SCARG(uap, dev); + whiteout = 0; + + switch (SCARG(uap, mode) & S_IFMT) { + case S_IFMT: /* used by badsect to flag bad sectors */ + vattr.va_type = VBAD; + break; + case S_IFCHR: + vattr.va_type = VCHR; + break; + case S_IFBLK: + vattr.va_type = VBLK; + break; + case S_IFWHT: + whiteout = 1; + break; + default: + error = EINVAL; + break; + } + } + if (!error) { + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + if (whiteout) { + error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); + if (error) + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + vput(nd.ni_dvp); + } else { + error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, + &nd.ni_cnd, &vattr); + } + } else { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + if (vp) + vrele(vp); + } + return (error); +} + +/* + * Create a named pipe. + */ +/* ARGSUSED */ +sys_mkfifo(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_mkfifo_args /* { + syscallarg(char *) path; + syscallarg(int) mode; + } */ *uap = v; + struct vattr vattr; + int error; + struct nameidata nd; + +#ifndef FIFO + return (EOPNOTSUPP); +#else + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + if (nd.ni_vp != NULL) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vrele(nd.ni_vp); + return (EEXIST); + } + VATTR_NULL(&vattr); + vattr.va_type = VFIFO; + vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr)); +#endif /* FIFO */ +} + +/* + * Make a hard file link. + */ +/* ARGSUSED */ +sys_link(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_link_args /* { + syscallarg(char *) path; + syscallarg(char *) link; + } */ *uap = v; + register struct vnode *vp; + struct nameidata nd; + int error; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_type != VDIR || + (error = suser(p->p_ucred, &p->p_acflag)) == 0) { + nd.ni_cnd.cn_nameiop = CREATE; + nd.ni_cnd.cn_flags = LOCKPARENT; + nd.ni_dirp = SCARG(uap, link); + if ((error = namei(&nd)) == 0) { + if (nd.ni_vp != NULL) + error = EEXIST; + if (!error) { + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, + LEASE_WRITE); + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); + } else { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + if (nd.ni_vp) + vrele(nd.ni_vp); + } + } + } + vrele(vp); + return (error); +} + +/* + * Make a symbolic link. + */ +/* ARGSUSED */ +sys_symlink(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_symlink_args /* { + syscallarg(char *) path; + syscallarg(char *) link; + } */ *uap = v; + struct vattr vattr; + char *path; + int error; + struct nameidata nd; + + MALLOC(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); + if (error = copyinstr(SCARG(uap, path), path, MAXPATHLEN, (size_t *)0)) + goto out; + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, link), p); + if (error = namei(&nd)) + goto out; + if (nd.ni_vp) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vrele(nd.ni_vp); + error = EEXIST; + goto out; + } + VATTR_NULL(&vattr); + vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask; + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path); +out: + FREE(path, M_NAMEI); + return (error); +} + +/* + * Delete a whiteout from the filesystem. + */ +/* ARGSUSED */ +sys_undelete(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_undelete_args /* { + syscallarg(char *) path; + } */ *uap = v; + int error; + struct nameidata nd; + + NDINIT(&nd, DELETE, LOCKPARENT|DOWHITEOUT, UIO_USERSPACE, + SCARG(uap, path), p); + error = namei(&nd); + if (error) + return (error); + + if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + if (nd.ni_vp) + vrele(nd.ni_vp); + return (EEXIST); + } + + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + if (error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE)) + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + vput(nd.ni_dvp); + return (error); +} + +/* + * Delete a name from the filesystem. + */ +/* ARGSUSED */ +sys_unlink(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_unlink_args /* { + syscallarg(char *) path; + } */ *uap = v; + register struct vnode *vp; + int error; + struct nameidata nd; + + NDINIT(&nd, DELETE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + + if (vp->v_type != VDIR || + (error = suser(p->p_ucred, &p->p_acflag)) == 0) { + /* + * The root of a mounted filesystem cannot be deleted. + */ + if (vp->v_flag & VROOT) + error = EBUSY; + else + (void)vnode_pager_uncache(vp); + } + + if (!error) { + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); + } else { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + if (vp != NULLVP) + vput(vp); + } + return (error); +} + +/* + * Reposition read/write file offset. + */ +sys_lseek(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_lseek_args /* { + syscallarg(int) fd; + syscallarg(int) pad; + syscallarg(off_t) offset; + syscallarg(int) whence; + } */ *uap = v; + struct ucred *cred = p->p_ucred; + register struct filedesc *fdp = p->p_fd; + register struct file *fp; + struct vattr vattr; + int error; + + if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) + return (EBADF); + if (fp->f_type != DTYPE_VNODE) + return (ESPIPE); + switch (SCARG(uap, whence)) { + case L_INCR: + fp->f_offset += SCARG(uap, offset); + break; + case L_XTND: + if (error = + VOP_GETATTR((struct vnode *)fp->f_data, &vattr, cred, p)) + return (error); + fp->f_offset = SCARG(uap, offset) + vattr.va_size; + break; + case L_SET: + fp->f_offset = SCARG(uap, offset); + break; + default: + return (EINVAL); + } + *(off_t *)retval = fp->f_offset; + return (0); +} + +/* + * Check access permissions. + */ +sys_access(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_access_args /* { + syscallarg(char *) path; + syscallarg(int) flags; + } */ *uap = v; + register struct ucred *cred = p->p_ucred; + register struct vnode *vp; + int error, flags, t_gid, t_uid; + struct nameidata nd; + + t_uid = cred->cr_uid; + t_gid = cred->cr_gid; + cred->cr_uid = p->p_cred->p_ruid; + cred->cr_gid = p->p_cred->p_rgid; + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if (error = namei(&nd)) + goto out1; + vp = nd.ni_vp; + + /* Flags == 0 means only check for existence. */ + if (SCARG(uap, flags)) { + flags = 0; + if (SCARG(uap, flags) & R_OK) + flags |= VREAD; + if (SCARG(uap, flags) & W_OK) + flags |= VWRITE; + if (SCARG(uap, flags) & X_OK) + flags |= VEXEC; + if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) + error = VOP_ACCESS(vp, flags, cred, p); + } + vput(vp); +out1: + cred->cr_uid = t_uid; + cred->cr_gid = t_gid; + return (error); +} + +/* + * Get file status; this version follows links. + */ +/* ARGSUSED */ +sys_stat(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_stat_args /* { + syscallarg(char *) path; + syscallarg(struct stat *) ub; + } */ *uap = v; + struct stat sb; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + error = vn_stat(nd.ni_vp, &sb, p); + vput(nd.ni_vp); + if (error) + return (error); + error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); + return (error); +} + +/* + * Get file status; this version does not follow links. + */ +/* ARGSUSED */ +sys_lstat(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_lstat_args /* { + syscallarg(char *) path; + syscallarg(struct stat *) ub; + } */ *uap = v; + int error; + struct vnode *vp, *dvp; + struct stat sb, sb1; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE, + SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + /* + * For symbolic links, always return the attributes of its + * containing directory, except for mode, size, and links. + */ + vp = nd.ni_vp; + dvp = nd.ni_dvp; + if (vp->v_type != VLNK) { + if (dvp == vp) + vrele(dvp); + else + vput(dvp); + error = vn_stat(vp, &sb, p); + vput(vp); + if (error) + return (error); + } else { + error = vn_stat(dvp, &sb, p); + vput(dvp); + if (error) { + vput(vp); + return (error); + } + error = vn_stat(vp, &sb1, p); + vput(vp); + if (error) + return (error); + sb.st_mode &= ~S_IFDIR; + sb.st_mode |= S_IFLNK; + sb.st_nlink = sb1.st_nlink; + sb.st_size = sb1.st_size; + sb.st_blocks = sb1.st_blocks; + } + error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); + return (error); +} + +/* + * Get configurable pathname variables. + */ +/* ARGSUSED */ +sys_pathconf(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_pathconf_args /* { + syscallarg(char *) path; + syscallarg(int) name; + } */ *uap = v; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + error = VOP_PATHCONF(nd.ni_vp, SCARG(uap, name), retval); + vput(nd.ni_vp); + return (error); +} + +/* + * Return target name of a symbolic link. + */ +/* ARGSUSED */ +sys_readlink(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_readlink_args /* { + syscallarg(char *) path; + syscallarg(char *) buf; + syscallarg(int) count; + } */ *uap = v; + register struct vnode *vp; + struct iovec aiov; + struct uio auio; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_type != VLNK) + error = EINVAL; + else { + aiov.iov_base = SCARG(uap, buf); + aiov.iov_len = SCARG(uap, count); + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_offset = 0; + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + auio.uio_resid = SCARG(uap, count); + error = VOP_READLINK(vp, &auio, p->p_ucred); + } + vput(vp); + *retval = SCARG(uap, count) - auio.uio_resid; + return (error); +} + +/* + * Change flags of a file given a path name. + */ +/* ARGSUSED */ +sys_chflags(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_chflags_args /* { + syscallarg(char *) path; + syscallarg(int) flags; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + VATTR_NULL(&vattr); + vattr.va_flags = SCARG(uap, flags); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + vput(vp); + return (error); +} + +/* + * Change flags of a file given a file descriptor. + */ +/* ARGSUSED */ +sys_fchflags(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_fchflags_args /* { + syscallarg(int) fd; + syscallarg(int) flags; + } */ *uap = v; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + VATTR_NULL(&vattr); + vattr.va_flags = SCARG(uap, flags); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + VOP_UNLOCK(vp); + return (error); +} + +/* + * Change mode of a file given path name. + */ +/* ARGSUSED */ +sys_chmod(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_chmod_args /* { + syscallarg(char *) path; + syscallarg(int) mode; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + VATTR_NULL(&vattr); + vattr.va_mode = SCARG(uap, mode) & ALLPERMS; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + vput(vp); + return (error); +} + +/* + * Change mode of a file given a file descriptor. + */ +/* ARGSUSED */ +sys_fchmod(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_fchmod_args /* { + syscallarg(int) fd; + syscallarg(int) mode; + } */ *uap = v; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + VATTR_NULL(&vattr); + vattr.va_mode = SCARG(uap, mode) & ALLPERMS; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + VOP_UNLOCK(vp); + return (error); +} + +/* + * Set ownership given a path name. + */ +/* ARGSUSED */ +sys_chown(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_chown_args /* { + syscallarg(char *) path; + syscallarg(int) uid; + syscallarg(int) gid; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + VATTR_NULL(&vattr); + vattr.va_uid = SCARG(uap, uid); + vattr.va_gid = SCARG(uap, gid); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + vput(vp); + return (error); +} + +/* + * Set ownership given a file descriptor. + */ +/* ARGSUSED */ +sys_fchown(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_fchown_args /* { + syscallarg(int) fd; + syscallarg(int) uid; + syscallarg(int) gid; + } */ *uap = v; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + VATTR_NULL(&vattr); + vattr.va_uid = SCARG(uap, uid); + vattr.va_gid = SCARG(uap, gid); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + VOP_UNLOCK(vp); + return (error); +} + +/* + * Set the access and modification times of a file. + */ +/* ARGSUSED */ +sys_utimes(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_utimes_args /* { + syscallarg(char *) path; + syscallarg(struct timeval *) tptr; + } */ *uap = v; + register struct vnode *vp; + struct timeval tv[2]; + struct vattr vattr; + int error; + struct nameidata nd; + + VATTR_NULL(&vattr); + if (SCARG(uap, tptr) == NULL) { + microtime(&tv[0]); + tv[1] = tv[0]; + vattr.va_vaflags |= VA_UTIMES_NULL; + } else if (error = copyin((caddr_t)SCARG(uap, tptr), (caddr_t)tv, + sizeof (tv))) + return (error); + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + vattr.va_atime.ts_sec = tv[0].tv_sec; + vattr.va_atime.ts_nsec = tv[0].tv_usec * 1000; + vattr.va_mtime.ts_sec = tv[1].tv_sec; + vattr.va_mtime.ts_nsec = tv[1].tv_usec * 1000; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + vput(vp); + return (error); +} + +/* + * Truncate a file given its path name. + */ +/* ARGSUSED */ +sys_truncate(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_truncate_args /* { + syscallarg(char *) path; + syscallarg(int) pad; + syscallarg(off_t) length; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_type == VDIR) + error = EISDIR; + else if ((error = vn_writechk(vp)) == 0 && + (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0) { + VATTR_NULL(&vattr); + vattr.va_size = SCARG(uap, length); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + vput(vp); + return (error); +} + +/* + * Truncate a file given a file descriptor. + */ +/* ARGSUSED */ +sys_ftruncate(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_ftruncate_args /* { + syscallarg(int) fd; + syscallarg(int) pad; + syscallarg(off_t) length; + } */ *uap = v; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) + return (error); + if ((fp->f_flag & FWRITE) == 0) + return (EINVAL); + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + VOP_LOCK(vp); + if (vp->v_type == VDIR) + error = EISDIR; + else if ((error = vn_writechk(vp)) == 0) { + VATTR_NULL(&vattr); + vattr.va_size = SCARG(uap, length); + error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); + } + VOP_UNLOCK(vp); + return (error); +} + +/* + * Sync an open file. + */ +/* ARGSUSED */ +sys_fsync(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_fsync_args /* { + syscallarg(int) fd; + } */ *uap = v; + register struct vnode *vp; + struct file *fp; + int error; + + if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LOCK(vp); + error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p); + VOP_UNLOCK(vp); + return (error); +} + +/* + * Rename files. Source and destination must either both be directories, + * or both not be directories. If target is a directory, it must be empty. + */ +/* ARGSUSED */ +sys_rename(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_rename_args /* { + syscallarg(char *) from; + syscallarg(char *) to; + } */ *uap = v; + register struct vnode *tvp, *fvp, *tdvp; + struct nameidata fromnd, tond; + int error; + + NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE, + SCARG(uap, from), p); + if (error = namei(&fromnd)) + return (error); + fvp = fromnd.ni_vp; + NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART, + UIO_USERSPACE, SCARG(uap, to), p); + if (error = namei(&tond)) { + VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); + vrele(fromnd.ni_dvp); + vrele(fvp); + goto out1; + } + tdvp = tond.ni_dvp; + tvp = tond.ni_vp; + if (tvp != NULL) { + if (fvp->v_type == VDIR && tvp->v_type != VDIR) { + error = ENOTDIR; + goto out; + } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { + error = EISDIR; + goto out; + } + } + if (fvp == tdvp) + error = EINVAL; + /* + * If source is the same as the destination (that is the + * same inode number with the same name in the same directory), + * then there is nothing to do. + */ + if (fvp == tvp && fromnd.ni_dvp == tdvp && + fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen && + !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr, + fromnd.ni_cnd.cn_namelen)) + error = -1; +out: + if (!error) { + VOP_LEASE(tdvp, p, p->p_ucred, LEASE_WRITE); + if (fromnd.ni_dvp != tdvp) + VOP_LEASE(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + if (tvp) + VOP_LEASE(tvp, p, p->p_ucred, LEASE_WRITE); + error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, + tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); + } else { + VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd); + if (tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + if (tvp) + vput(tvp); + VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); + vrele(fromnd.ni_dvp); + vrele(fvp); + } + vrele(tond.ni_startdir); + FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI); +out1: + if (fromnd.ni_startdir) + vrele(fromnd.ni_startdir); + FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); + if (error == -1) + return (0); + return (error); +} + +/* + * Make a directory file. + */ +/* ARGSUSED */ +sys_mkdir(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_mkdir_args /* { + syscallarg(char *) path; + syscallarg(int) mode; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp != NULL) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vrele(vp); + return (EEXIST); + } + VATTR_NULL(&vattr); + vattr.va_type = VDIR; + vattr.va_mode = (SCARG(uap, mode) & ACCESSPERMS) &~ p->p_fd->fd_cmask; + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); + if (!error) + vput(nd.ni_vp); + return (error); +} + +/* + * Remove a directory file. + */ +/* ARGSUSED */ +sys_rmdir(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_rmdir_args /* { + syscallarg(char *) path; + } */ *uap = v; + register struct vnode *vp; + int error; + struct nameidata nd; + + NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_type != VDIR) { + error = ENOTDIR; + goto out; + } + /* + * No rmdir "." please. + */ + if (nd.ni_dvp == vp) { + error = EINVAL; + goto out; + } + /* + * The root of a mounted filesystem cannot be deleted. + */ + if (vp->v_flag & VROOT) + error = EBUSY; +out: + if (!error) { + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); + } else { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vput(vp); + } + return (error); +} + +/* + * Read a block of directory entries in a file system independent format. + */ +sys_getdirentries(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_getdirentries_args /* { + syscallarg(int) fd; + syscallarg(char *) buf; + syscallarg(u_int) count; + syscallarg(long *) basep; + } */ *uap = v; + register struct vnode *vp; + struct file *fp; + struct uio auio; + struct iovec aiov; + long loff; + int error, eofflag; + + if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) + return (error); + if ((fp->f_flag & FREAD) == 0) + return (EBADF); + vp = (struct vnode *)fp->f_data; +unionread: + if (vp->v_type != VDIR) + return (EINVAL); + aiov.iov_base = SCARG(uap, buf); + aiov.iov_len = SCARG(uap, count); + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + auio.uio_resid = SCARG(uap, count); + VOP_LOCK(vp); + loff = auio.uio_offset = fp->f_offset; + error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, (u_long *)0, 0); + fp->f_offset = auio.uio_offset; + VOP_UNLOCK(vp); + if (error) + return (error); + +#ifdef UNION +{ + extern int (**union_vnodeop_p)(); + extern struct vnode *union_dircache __P((struct vnode *)); + + if ((SCARG(uap, count) == auio.uio_resid) && + (vp->v_op == union_vnodeop_p)) { + struct vnode *lvp; + + lvp = union_dircache(vp); + if (lvp != NULLVP) { + struct vattr va; + + /* + * If the directory is opaque, + * then don't show lower entries + */ + error = VOP_GETATTR(vp, &va, fp->f_cred, p); + if (va.va_flags & OPAQUE) { + vput(lvp); + lvp = NULL; + } + } + + if (lvp != NULLVP) { + error = VOP_OPEN(lvp, FREAD, fp->f_cred, p); + VOP_UNLOCK(lvp); + + if (error) { + vrele(lvp); + return (error); + } + fp->f_data = (caddr_t) lvp; + fp->f_offset = 0; + error = vn_close(vp, FREAD, fp->f_cred, p); + if (error) + return (error); + vp = lvp; + goto unionread; + } + } +} +#endif /* UNION */ + + if ((SCARG(uap, count) == auio.uio_resid) && + (vp->v_flag & VROOT) && + (vp->v_mount->mnt_flag & MNT_UNION)) { + struct vnode *tvp = vp; + vp = vp->v_mount->mnt_vnodecovered; + VREF(vp); + fp->f_data = (caddr_t) vp; + fp->f_offset = 0; + vrele(tvp); + goto unionread; + } + error = copyout((caddr_t)&loff, (caddr_t)SCARG(uap, basep), + sizeof(long)); + *retval = SCARG(uap, count) - auio.uio_resid; + return (error); +} + +/* + * Set the mode mask for creation of filesystem nodes. + */ +int +sys_umask(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_umask_args /* { + syscallarg(int) newmask; + } */ *uap = v; + register struct filedesc *fdp; + + fdp = p->p_fd; + *retval = fdp->fd_cmask; + fdp->fd_cmask = SCARG(uap, newmask) & ALLPERMS; + return (0); +} + +/* + * Void all references to file by ripping underlying filesystem + * away from vnode. + */ +/* ARGSUSED */ +sys_revoke(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_revoke_args /* { + syscallarg(char *) path; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if (error = namei(&nd)) + return (error); + vp = nd.ni_vp; + if (vp->v_type != VCHR && vp->v_type != VBLK) { + error = EINVAL; + goto out; + } + if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) + goto out; + if (p->p_ucred->cr_uid != vattr.va_uid && + (error = suser(p->p_ucred, &p->p_acflag))) + goto out; + if (vp->v_usecount > 1 || (vp->v_flag & VALIASED)) + vgoneall(vp); +out: + vrele(vp); + return (error); +} + +/* + * Convert a user file descriptor to a kernel file entry. + */ +getvnode(fdp, fd, fpp) + struct filedesc *fdp; + struct file **fpp; + int fd; +{ + struct file *fp; + + if ((u_int)fd >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[fd]) == NULL) + return (EBADF); + if (fp->f_type != DTYPE_VNODE) + return (EINVAL); + *fpp = fp; + return (0); +} diff --git a/sys/src/cmd/diff/test/diff-t9.2 b/sys/src/cmd/diff/test/diff-t9.2 new file mode 100644 index 000000000..0a2273c4f --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t9.2 @@ -0,0 +1,3216 @@ +/* $OpenBSD: t9.2,v 1.2 2013/12/01 16:40:56 krw Exp $ */ +/* $NetBSD: vfs_syscalls.c,v 1.71 1996/04/23 10:29:02 mycroft Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)vfs_syscalls.c 8.28 (Berkeley) 12/10/94 + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/namei.h> +#include <sys/filedesc.h> +#include <sys/kernel.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/vnode.h> +#include <sys/mount.h> +#include <sys/proc.h> +#include <sys/uio.h> +#include <sys/malloc.h> +#include <sys/dirent.h> +#include <sys/extattr.h> + +#include <sys/syscallargs.h> + +#include <uvm/uvm_extern.h> +#include <sys/sysctl.h> + +extern int suid_clear; +int usermount = 0; /* sysctl: by default, users may not mount */ + +static int change_dir(struct nameidata *, struct proc *); + +void checkdirs(struct vnode *); + +/* + * Redirection info so we don't have to include the union fs routines in + * the kernel directly. This way, we can build unionfs as an LKM. The + * pointer gets filled in later, when we modload the LKM, or when the + * compiled-in unionfs code gets initialized. For now, we just set + * it to a stub routine. + */ + +int (*union_check_p)(struct proc *, struct vnode **, + struct file *, struct uio, int *) = NULL; + +/* + * Virtual File System System Calls + */ + +/* + * Mount a file system. + */ +/* ARGSUSED */ +int +sys_mount(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_mount_args /* { + syscallarg(char *) type; + syscallarg(char *) path; + syscallarg(int) flags; + syscallarg(void *) data; + } */ *uap = v; + register struct vnode *vp; + register struct mount *mp; + int error, flag = 0; +#ifdef COMPAT_43 + u_long fstypenum = 0; +#endif + char fstypename[MFSNAMELEN]; + char fspath[MNAMELEN]; + struct vattr va; + struct nameidata nd; + struct vfsconf *vfsp; + struct timeval tv; + + if (usermount == 0 && (error = suser(p->p_ucred, &p->p_acflag))) + return (error); + + /* + * Mount points must fit in MNAMELEN, not MAXPATHLEN. + */ + error = copyinstr(SCARG(uap, path), fspath, MNAMELEN, NULL); + if (error) + return(error); + + /* + * Get vnode to be covered + */ + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspath, p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + if (SCARG(uap, flags) & MNT_UPDATE) { + if ((vp->v_flag & VROOT) == 0) { + vput(vp); + return (EINVAL); + } + mp = vp->v_mount; + flag = mp->mnt_flag; + /* + * We only allow the filesystem to be reloaded if it + * is currently mounted read-only. + */ + if ((SCARG(uap, flags) & MNT_RELOAD) && + ((mp->mnt_flag & MNT_RDONLY) == 0)) { + vput(vp); + return (EOPNOTSUPP); /* Needs translation */ + } + mp->mnt_flag |= + SCARG(uap, flags) & (MNT_RELOAD | MNT_UPDATE); + /* + * Only root, or the user that did the original mount is + * permitted to update it. + */ + if (mp->mnt_stat.f_owner != p->p_ucred->cr_uid && + (error = suser(p->p_ucred, &p->p_acflag))) { + vput(vp); + return (error); + } + /* + * Do not allow NFS export by non-root users. Silently + * enforce MNT_NOSUID and MNT_NODEV for non-root users. + */ + if (p->p_ucred->cr_uid != 0) { + if (SCARG(uap, flags) & MNT_EXPORTED) { + vput(vp); + return (EPERM); + } + SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; + } + if ((error = vfs_busy(mp, LK_NOWAIT, 0, p)) != 0) { + vput(vp); + return (error); + } + VOP_UNLOCK(vp, 0, p); + goto update; + } + /* + * If the user is not root, ensure that they own the directory + * onto which we are attempting to mount. + */ + if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) || + (va.va_uid != p->p_ucred->cr_uid && + (error = suser(p->p_ucred, &p->p_acflag)))) { + vput(vp); + return (error); + } + /* + * Do not allow NFS export by non-root users. Silently + * enforce MNT_NOSUID and MNT_NODEV for non-root users. + */ + if (p->p_ucred->cr_uid != 0) { + if (SCARG(uap, flags) & MNT_EXPORTED) { + vput(vp); + return (EPERM); + } + SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; + } + if ((error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) != 0) + return (error); + if (vp->v_type != VDIR) { + vput(vp); + return (ENOTDIR); + } + error = copyinstr(SCARG(uap, type), fstypename, MFSNAMELEN, NULL); + if (error) { +#ifdef COMPAT_43 + /* + * Historically filesystem types were identified by number. + * If we get an integer for the filesystem type instead of a + * string, we check to see if it matches one of the historic + * filesystem types. + */ + fstypenum = (u_long)SCARG(uap, type); + + for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) + if (vfsp->vfc_typenum == fstypenum) + break; + if (vfsp == NULL) { + vput(vp); + return (ENODEV); + } + strncpy(fstypename, vfsp->vfc_name, MFSNAMELEN); + +#else + vput(vp); + return (error); +#endif + } + for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) { + if (!strcmp(vfsp->vfc_name, fstypename)) + break; + } + + if (vfsp == NULL) { + vput(vp); + return (EOPNOTSUPP); + } + + if (vp->v_mountedhere != NULL) { + vput(vp); + return (EBUSY); + } + + /* + * Allocate and initialize the file system. + */ + mp = (struct mount *)malloc((u_long)sizeof(struct mount), + M_MOUNT, M_WAITOK); + bzero((char *)mp, (u_long)sizeof(struct mount)); + lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, 0); + /* This error never happens, but it makes auditing easier */ + if ((error = vfs_busy(mp, LK_NOWAIT, 0, p))) + return (error); + mp->mnt_op = vfsp->vfc_vfsops; + mp->mnt_vfc = vfsp; + mp->mnt_flag |= (vfsp->vfc_flags & MNT_VISFLAGMASK); + strncpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN); + mp->mnt_vnodecovered = vp; + mp->mnt_stat.f_owner = p->p_ucred->cr_uid; +update: + /* + * Set the mount level flags. + */ + if (SCARG(uap, flags) & MNT_RDONLY) + mp->mnt_flag |= MNT_RDONLY; + else if (mp->mnt_flag & MNT_RDONLY) + mp->mnt_flag |= MNT_WANTRDWR; + mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | + MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_SOFTDEP | + MNT_NOATIME | MNT_FORCE); + mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC | + MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | + MNT_SOFTDEP | MNT_NOATIME | MNT_FORCE); + /* + * Mount the filesystem. + */ + error = VFS_MOUNT(mp, SCARG(uap, path), SCARG(uap, data), &nd, p); + if (!error) { + microtime(&tv); + mp->mnt_stat.f_ctime = tv.tv_sec; + } + if (mp->mnt_flag & MNT_UPDATE) { + vrele(vp); + if (mp->mnt_flag & MNT_WANTRDWR) + mp->mnt_flag &= ~MNT_RDONLY; + mp->mnt_flag &=~ + (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_WANTRDWR); + if (error) + mp->mnt_flag = flag; + + if ((mp->mnt_flag & MNT_RDONLY) == 0) { + if (mp->mnt_syncer == NULL) + error = vfs_allocate_syncvnode(mp); + } else { + if (mp->mnt_syncer != NULL) + vgone(mp->mnt_syncer); + mp->mnt_syncer = NULL; + } + + vfs_unbusy(mp, p); + return (error); + } + + vp->v_mountedhere = mp; + + /* + * Put the new filesystem on the mount list after root. + */ + cache_purge(vp); + if (!error) { + vfsp->vfc_refcount++; + simple_lock(&mountlist_slock); + TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); + simple_unlock(&mountlist_slock); + checkdirs(vp); + VOP_UNLOCK(vp, 0, p); + if ((mp->mnt_flag & MNT_RDONLY) == 0) + error = vfs_allocate_syncvnode(mp); + vfs_unbusy(mp, p); + (void) VFS_STATFS(mp, &mp->mnt_stat, p); + if ((error = VFS_START(mp, 0, p)) != 0) + vrele(vp); + } else { + mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; + vfs_unbusy(mp, p); + free((caddr_t)mp, M_MOUNT); + vput(vp); + } + return (error); +} + +/* + * Scan all active processes to see if any of them have a current + * or root directory onto which the new filesystem has just been + * mounted. If so, replace them with the new mount point. + */ +void +checkdirs(olddp) + struct vnode *olddp; +{ + struct filedesc *fdp; + struct vnode *newdp; + struct proc *p; + + if (olddp->v_usecount == 1) + return; + if (VFS_ROOT(olddp->v_mountedhere, &newdp)) + panic("mount: lost mount"); + for (p = LIST_FIRST(&allproc); p != 0; p = LIST_NEXT(p, p_list)) { + fdp = p->p_fd; + if (fdp->fd_cdir == olddp) { + vrele(fdp->fd_cdir); + VREF(newdp); + fdp->fd_cdir = newdp; + } + if (fdp->fd_rdir == olddp) { + vrele(fdp->fd_rdir); + VREF(newdp); + fdp->fd_rdir = newdp; + } + } + if (rootvnode == olddp) { + vrele(rootvnode); + VREF(newdp); + rootvnode = newdp; + } + vput(newdp); +} + +/* + * Unmount a file system. + * + * Note: unmount takes a path to the vnode mounted on as argument, + * not special file (as before). + */ +/* ARGSUSED */ +int +sys_unmount(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_unmount_args /* { + syscallarg(char *) path; + syscallarg(int) flags; + } */ *uap = v; + register struct vnode *vp; + struct mount *mp; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + mp = vp->v_mount; + + /* + * Only root, or the user that did the original mount is + * permitted to unmount this filesystem. + */ + if ((mp->mnt_stat.f_owner != p->p_ucred->cr_uid) && + (error = suser(p->p_ucred, &p->p_acflag))) { + vput(vp); + return (error); + } + + /* + * Don't allow unmounting the root file system. + */ + if (mp->mnt_flag & MNT_ROOTFS) { + vput(vp); + return (EINVAL); + } + + /* + * Must be the root of the filesystem + */ + if ((vp->v_flag & VROOT) == 0) { + vput(vp); + return (EINVAL); + } + vput(vp); + + if (vfs_busy(mp, LK_EXCLUSIVE, NULL, p)) + return (EBUSY); + + return (dounmount(mp, SCARG(uap, flags), p, vp)); +} + +/* + * Do the actual file system unmount. + */ +int +dounmount(struct mount *mp, int flags, struct proc *p, struct vnode *olddp) +{ + struct vnode *coveredvp; + struct proc *p2; + int error; + int hadsyncer = 0; + + mp->mnt_flag &=~ MNT_ASYNC; + cache_purgevfs(mp); /* remove cache entries for this file sys */ + if (mp->mnt_syncer != NULL) { + hadsyncer = 1; + vgone(mp->mnt_syncer); + mp->mnt_syncer = NULL; + } + if (((mp->mnt_flag & MNT_RDONLY) || + (error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0) || + (flags & MNT_FORCE)) + error = VFS_UNMOUNT(mp, flags, p); + simple_lock(&mountlist_slock); + if (error) { + if ((mp->mnt_flag & MNT_RDONLY) == 0 && hadsyncer) + (void) vfs_allocate_syncvnode(mp); + lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, + &mountlist_slock, p); + return (error); + } + TAILQ_REMOVE(&mountlist, mp, mnt_list); + if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) { + if (olddp) { + /* + * Try to put processes back in a real directory + * after a forced unmount. + * XXX We're not holding a ref on olddp, which may + * change, so compare id numbers. + */ + LIST_FOREACH(p2, &allproc, p_list) { + struct filedesc *fdp = p2->p_fd; + if (fdp->fd_cdir && + fdp->fd_cdir->v_id == olddp->v_id) { + vrele(fdp->fd_cdir); + vref(coveredvp); + fdp->fd_cdir = coveredvp; + } + if (fdp->fd_rdir && + fdp->fd_rdir->v_id == olddp->v_id) { + vrele(fdp->fd_rdir); + vref(coveredvp); + fdp->fd_rdir = coveredvp; + } + } + } + coveredvp->v_mountedhere = NULL; + vrele(coveredvp); + } + mp->mnt_vfc->vfc_refcount--; + if (mp->mnt_vnodelist.lh_first != NULL) + panic("unmount: dangling vnode"); + lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_slock, p); + free((caddr_t)mp, M_MOUNT); + return (0); +} + +/* + * Sync each mounted filesystem. + */ +#ifdef DEBUG +int syncprt = 0; +struct ctldebug debug0 = { "syncprt", &syncprt }; +#endif + +/* ARGSUSED */ +int +sys_sync(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct mount *mp, *nmp; + int asyncflag; + + simple_lock(&mountlist_slock); + TAILQ_FOREACH_REVERSE_SAFE(mp, &mountlist, mnt_list, nmp) { + if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) + continue; + if ((mp->mnt_flag & MNT_RDONLY) == 0) { + asyncflag = mp->mnt_flag & MNT_ASYNC; + mp->mnt_flag &= ~MNT_ASYNC; + uvm_vnp_sync(mp); + VFS_SYNC(mp, MNT_NOWAIT, p->p_ucred, p); + if (asyncflag) + mp->mnt_flag |= MNT_ASYNC; + } + simple_lock(&mountlist_slock); + vfs_unbusy(mp, p); + } + simple_unlock(&mountlist_slock); + +#ifdef DEBUG + if (syncprt) + vfs_bufstats(); +#endif /* DEBUG */ + return (0); +} + +/* + * Change filesystem quotas. + */ +/* ARGSUSED */ +int +sys_quotactl(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_quotactl_args /* { + syscallarg(char *) path; + syscallarg(int) cmd; + syscallarg(int) uid; + syscallarg(caddr_t) arg; + } */ *uap = v; + register struct mount *mp; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + mp = nd.ni_vp->v_mount; + vrele(nd.ni_vp); + return (VFS_QUOTACTL(mp, SCARG(uap, cmd), SCARG(uap, uid), + SCARG(uap, arg), p)); +} + +/* + * Get filesystem statistics. + */ +/* ARGSUSED */ +int +sys_statfs(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_statfs_args /* { + syscallarg(char *) path; + syscallarg(struct statfs *) buf; + } */ *uap = v; + register struct mount *mp; + register struct statfs *sp; + int error; + struct nameidata nd; + struct statfs sb; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + mp = nd.ni_vp->v_mount; + sp = &mp->mnt_stat; + vrele(nd.ni_vp); + if ((error = VFS_STATFS(mp, sp, p)) != 0) + return (error); + sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; +#if notyet + if (mp->mnt_flag & MNT_SOFTDEP) + sp->f_eflags = STATFS_SOFTUPD; +#endif + /* Don't let non-root see filesystem id (for NFS security) */ + if (suser(p->p_ucred, &p->p_acflag)) { + bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); + sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; + sp = &sb; + } + return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); +} + +/* + * Get filesystem statistics. + */ +/* ARGSUSED */ +int +sys_fstatfs(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_fstatfs_args /* { + syscallarg(int) fd; + syscallarg(struct statfs *) buf; + } */ *uap = v; + struct file *fp; + struct mount *mp; + struct statfs *sp; + int error; + struct statfs sb; + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + mp = ((struct vnode *)fp->f_data)->v_mount; + sp = &mp->mnt_stat; + error = VFS_STATFS(mp, sp, p); + FRELE(fp); + if (error) + return (error); + sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; +#if notyet + if (mp->mnt_flag & MNT_SOFTDEP) + sp->f_eflags = STATFS_SOFTUPD; +#endif + /* Don't let non-root see filesystem id (for NFS security) */ + if (suser(p->p_ucred, &p->p_acflag)) { + bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); + sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; + sp = &sb; + } + return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); +} + +/* + * Get statistics on all filesystems. + */ +int +sys_getfsstat(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_getfsstat_args /* { + syscallarg(struct statfs *) buf; + syscallarg(size_t) bufsize; + syscallarg(int) flags; + } */ *uap = v; + register struct mount *mp *nmp; + register struct statfs *sp; + struct statfs sb; + caddr_t sfsp; + size_t count, maxcount; + int error, flags = SCARG(uap, flags); + + maxcount = SCARG(uap, bufsize) / sizeof(struct statfs); + sfsp = (caddr_t)SCARG(uap, buf); + count = 0; + simple_lock(&mountlist_slock); + TAILQ_FOREACH_REVERSE_SAFE(mp, &mountlist, mnt_list, nmp) { + if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) + continue; + if (sfsp && count < maxcount) { + sp = &mp->mnt_stat; + + /* Refresh stats unless MNT_NOWAIT is specified */ + if (flags != MNT_NOWAIT && + flags != MNT_LAZY && + (flags == MNT_WAIT || + flags == 0) && + (error = VFS_STATFS(mp, sp, p))) { + simple_lock(&mountlist_slock); + vfs_unbusy(mp, p); + continue; + } + + sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; +#if notyet + if (mp->mnt_flag & MNT_SOFTDEP) + sp->f_eflags = STATFS_SOFTUPD; +#endif + if (suser(p->p_ucred, &p->p_acflag)) { + bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); + sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; + sp = &sb; + } + error = copyout((caddr_t)sp, sfsp, sizeof(*sp)); + if (error) { + vfs_unbusy(mp, p); + return (error); + } + sfsp += sizeof(*sp); + } + count++; + simple_lock(&mountlist_slock); + vfs_unbusy(mp, p); + } + simple_unlock(&mountlist_slock); + if (sfsp && count > maxcount) + *retval = maxcount; + else + *retval = count; + return (0); +} + +/* + * Change current working directory to a given file descriptor. + */ +/* ARGSUSED */ +int +sys_fchdir(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_fchdir_args /* { + syscallarg(int) fd; + } */ *uap = v; + struct filedesc *fdp = p->p_fd; + struct vnode *vp, *tdp; + struct mount *mp; + struct file *fp; + int error; + + if ((error = getvnode(fdp, SCARG(uap, fd), &fp)) != 0) + return (error); + vp = (struct vnode *)fp->f_data; + VREF(vp); + FRELE(fp); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_type != VDIR) + error = ENOTDIR; + else + error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); + + while (!error && (mp = vp->v_mountedhere) != NULL) { + if (vfs_busy(mp, 0, 0, p)) + continue; + error = VFS_ROOT(mp, &tdp); + vfs_unbusy(mp, p); + if (error) + break; + vput(vp); + vp = tdp; + } + if (error) { + vput(vp); + return (error); + } + VOP_UNLOCK(vp, 0, p); + vrele(fdp->fd_cdir); + fdp->fd_cdir = vp; + return (0); +} + +/* + * Change current working directory (``.''). + */ +/* ARGSUSED */ +int +sys_chdir(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_chdir_args /* { + syscallarg(char *) path; + } */ *uap = v; + register struct filedesc *fdp = p->p_fd; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if ((error = change_dir(&nd, p)) != 0) + return (error); + vrele(fdp->fd_cdir); + fdp->fd_cdir = nd.ni_vp; + return (0); +} + +/* + * Change notion of root (``/'') directory. + */ +/* ARGSUSED */ +int +sys_chroot(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_chroot_args /* { + syscallarg(char *) path; + } */ *uap = v; + register struct filedesc *fdp = p->p_fd; + int error; + struct nameidata nd; + + if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) + return (error); + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if ((error = change_dir(&nd, p)) != 0) + return (error); + if (fdp->fd_rdir != NULL) { + /* + * A chroot() done inside a changed root environment does + * an automatic chdir to avoid the out-of-tree experience. + */ + vrele(fdp->fd_rdir); + vrele(fdp->fd_cdir); + VREF(nd.ni_vp); + fdp->fd_cdir = nd.ni_vp; + } + fdp->fd_rdir = nd.ni_vp; + return (0); +} + +/* + * Common routine for chroot and chdir. + */ +static int +change_dir(ndp, p) + register struct nameidata *ndp; + struct proc *p; +{ + struct vnode *vp; + int error; + + if ((error = namei(ndp)) != 0) + return (error); + vp = ndp->ni_vp; + if (vp->v_type != VDIR) + error = ENOTDIR; + else + error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); + if (error) + vput(vp); + else + VOP_UNLOCK(vp, 0, p); + return (error); +} + +/* + * Check permissions, allocate an open file structure, + * and call the device open routine if any. + */ +int +sys_open(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_open_args /* { + syscallarg(char *) path; + syscallarg(int) flags; + syscallarg(int) mode; + } */ *uap = v; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp; + struct vattr vattr; + int flags, cmode; + int type, indx, error, localtrunc = 0; + struct flock lf; + struct nameidata nd; + + if ((error = falloc(p, &fp, &indx)) != 0) + return (error); + + flags = FFLAGS(SCARG(uap, flags)); + cmode = ((SCARG(uap, mode) &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + p->p_dupfd = -indx - 1; /* XXX check for fdopen */ + if ((flags & O_TRUNC) && (flags & (O_EXLOCK | O_SHLOCK))) { + localtrunc = 1; + flags &= ~O_TRUNC; /* Must do truncate ourselves */ + } + if ((error = vn_open(&nd, flags, cmode)) != 0) { + if ((error == ENODEV || error == ENXIO) && + p->p_dupfd >= 0 && /* XXX from fdopen */ + (error = + dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) { + closef(fp, p); + *retval = indx; + return (0); + } + if (error == ERESTART) + error = EINTR; + fdremove(fdp, indx); + closef(fp, p); + return (error); + } + p->p_dupfd = 0; + vp = nd.ni_vp; + fp->f_flag = flags & FMASK; + fp->f_type = DTYPE_VNODE; + fp->f_ops = &vnops; + fp->f_data = (caddr_t)vp; + if (flags & (O_EXLOCK | O_SHLOCK)) { + lf.l_whence = SEEK_SET; + lf.l_start = 0; + lf.l_len = 0; + if (flags & O_EXLOCK) + lf.l_type = F_WRLCK; + else + lf.l_type = F_RDLCK; + type = F_FLOCK; + if ((flags & FNONBLOCK) == 0) + type |= F_WAIT; + VOP_UNLOCK(vp, 0, p); + error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type); + if (error) { + /* closef will vn_close the file for us. */ + fdremove(fdp, indx); + closef(fp, p); + return (error); + } + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + fp->f_flag |= FHASLOCK; + } + if (localtrunc) { + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + if ((fp->f_flag & FWRITE) == 0) + error = EACCES; + else if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else if (vp->v_type == VDIR) + error = EISDIR; + else if ((error = vn_writechk(vp)) == 0) { + VATTR_NULL(&vattr); + vattr.va_size = 0; + error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); + } + if (error) { + VOP_UNLOCK(vp, 0, p); + /* closef will close the file for us. */ + fdremove(fdp, indx); + closef(fp, p); + return (error); + } + } + VOP_UNLOCK(vp, 0, p); + *retval = indx; + FILE_SET_MATURE(fp); + return (0); +} + +/* + * Get file handle system call + */ +int +sys_getfh(p, v, retval) + struct proc *p; + register void *v; + register_t *retval; +{ + register struct sys_getfh_args /* { + syscallarg(char *) fname; + syscallarg(fhandle_t *) fhp; + } */ *uap = v; + register struct vnode *vp; + fhandle_t fh; + int error; + struct nameidata nd; + + /* + * Must be super user + */ + error = suser(p->p_ucred, &p->p_acflag); + if (error) + return (error); + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, fname), p); + error = namei(&nd); + if (error) + return (error); + vp = nd.ni_vp; + bzero((caddr_t)&fh, sizeof(fh)); + fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; + error = VFS_VPTOFH(vp, &fh.fh_fid); + vput(vp); + if (error) + return (error); + error = copyout((caddr_t)&fh, (caddr_t)SCARG(uap, fhp), sizeof (fh)); + return (error); +} + +/* + * Open a file given a file handle. + * + * Check permissions, allocate an open file structure, + * and call the device open routine if any. + */ +int +sys_fhopen(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_fhopen_args /* { + syscallarg(const fhandle_t *) fhp; + syscallarg(int) flags; + } */ *uap = v; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp = NULL; + struct mount *mp; + struct ucred *cred = p->p_ucred; + int flags; + int type, indx, error=0; + struct flock lf; + struct vattr va; + fhandle_t fh; + + /* + * Must be super user + */ + if ((error = suser(p->p_ucred, &p->p_acflag))) + return (error); + + flags = FFLAGS(SCARG(uap, flags)); + if ((flags & (FREAD | FWRITE)) == 0) + return (EINVAL); + if ((flags & O_CREAT)) + return (EINVAL); + + if ((error = falloc(p, &fp, &indx)) != 0) + return (error); + + if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fhandle_t))) != 0) + goto bad; + + if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) { + error = ESTALE; + goto bad; + } + + if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp)) != 0) { + vp = NULL; /* most likely unnecessary sanity for bad: */ + goto bad; + } + + /* Now do an effective vn_open */ + + if (vp->v_type == VSOCK) { + error = EOPNOTSUPP; + goto bad; + } + if (flags & FREAD) { + if ((error = VOP_ACCESS(vp, VREAD, cred, p)) != 0) + goto bad; + } + if (flags & (FWRITE | O_TRUNC)) { + if (vp->v_type == VDIR) { + error = EISDIR; + goto bad; + } + if ((error = vn_writechk(vp)) != 0 || + (error = VOP_ACCESS(vp, VWRITE, cred, p)) != 0) + goto bad; + } + if (flags & O_TRUNC) { + VOP_UNLOCK(vp, 0, p); /* XXX */ + VOP_LEASE(vp, p, cred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */ + VATTR_NULL(&va); + va.va_size = 0; + if ((error = VOP_SETATTR(vp, &va, cred, p)) != 0) + goto bad; + } + if ((error = VOP_OPEN(vp, flags, cred, p)) != 0) + goto bad; + if (flags & FWRITE) + vp->v_writecount++; + + /* done with modified vn_open, now finish what sys_open does. */ + + fp->f_flag = flags & FMASK; + fp->f_type = DTYPE_VNODE; + fp->f_ops = &vnops; + fp->f_data = (caddr_t)vp; + if (flags & (O_EXLOCK | O_SHLOCK)) { + lf.l_whence = SEEK_SET; + lf.l_start = 0; + lf.l_len = 0; + if (flags & O_EXLOCK) + lf.l_type = F_WRLCK; + else + lf.l_type = F_RDLCK; + type = F_FLOCK; + if ((flags & FNONBLOCK) == 0) + type |= F_WAIT; + VOP_UNLOCK(vp, 0, p); + error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type); + if (error) { + /* closef will vn_close the file for us. */ + fdremove(fdp, indx); + closef(fp, p); + return (error); + } + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + fp->f_flag |= FHASLOCK; + } + VOP_UNLOCK(vp, 0, p); + *retval = indx; + FILE_SET_MATURE(fp); + return (0); + +bad: + fdremove(fdp, indx); + closef(fp, p); + if (vp != NULL) + vput(vp); + return (error); +} + +/* ARGSUSED */ +int +sys_fhstat(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_fhstat_args /* { + syscallarg(const fhandle_t *) fhp; + syscallarg(struct stat *) sb; + } */ *uap = v; + struct stat sb; + int error; + fhandle_t fh; + struct mount *mp; + struct vnode *vp; + + /* + * Must be super user + */ + if ((error = suser(p->p_ucred, &p->p_acflag))) + return (error); + + if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fhandle_t))) != 0) + return (error); + + if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) + return (ESTALE); + if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) + return (error); + error = vn_stat(vp, &sb, p); + vput(vp); + if (error) + return (error); + error = copyout(&sb, SCARG(uap, sb), sizeof(sb)); + return (error); +} + +/* ARGSUSED */ +int +sys_fhstatfs(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_fhstatfs_args /* + syscallarg(const fhandle_t *) fhp; + syscallarg(struct statfs *) buf; + } */ *uap = v; + struct statfs sp; + fhandle_t fh; + struct mount *mp; + struct vnode *vp; + int error; + + /* + * Must be super user + */ + if ((error = suser(p->p_ucred, &p->p_acflag))) + return (error); + + if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fhandle_t))) != 0) + return (error); + + if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) + return (ESTALE); + if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) + return (error); + mp = vp->v_mount; + vput(vp); + if ((error = VFS_STATFS(mp, &sp, p)) != 0) + return (error); + sp.f_flags = mp->mnt_flag & MNT_VISFLAGMASK; + return (copyout(&sp, SCARG(uap, buf), sizeof(sp))); +} + +/* + * Create a special file. + */ +/* ARGSUSED */ +int +sys_mknod(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_mknod_args /* { + syscallarg(char *) path; + syscallarg(int) mode; + syscallarg(int) dev; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + int whiteout = 0; + struct nameidata nd; + + if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) + return (error); + if (p->p_fd->fd_rdir) + return (EINVAL); + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + if (vp != NULL) + error = EEXIST; + else { + VATTR_NULL(&vattr); + vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; + vattr.va_rdev = SCARG(uap, dev); + whiteout = 0; + + switch (SCARG(uap, mode) & S_IFMT) { + case S_IFMT: /* used by badsect to flag bad sectors */ + vattr.va_type = VBAD; + break; + case S_IFCHR: + vattr.va_type = VCHR; + break; + case S_IFBLK: + vattr.va_type = VBLK; + break; + case S_IFWHT: + whiteout = 1; + break; + default: + error = EINVAL; + break; + } + } + if (!error) { + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + if (whiteout) { + error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); + if (error) + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + vput(nd.ni_dvp); + } else { + error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, + &nd.ni_cnd, &vattr); + } + } else { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + if (vp) + vrele(vp); + } + return (error); +} + +/* + * Create a named pipe. + */ +/* ARGSUSED */ +int +sys_mkfifo(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ +#ifndef FIFO + return (EOPNOTSUPP); +#else + register struct sys_mkfifo_args /* { + syscallarg(char *) path; + syscallarg(int) mode; + } */ *uap = v; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + if (nd.ni_vp != NULL) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vrele(nd.ni_vp); + return (EEXIST); + } + VATTR_NULL(&vattr); + vattr.va_type = VFIFO; + vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr)); +#endif /* FIFO */ +} + +/* + * Make a hard file link. + */ +/* ARGSUSED */ +int +sys_link(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_link_args /* { + syscallarg(char *) path; + syscallarg(char *) link; + } */ *uap = v; + register struct vnode *vp; + struct nameidata nd; + int error; + int flags; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + + flags = LOCKPARENT; + if (vp->v_type == VDIR) { + flags |= STRIPSLASHES; + } + + NDINIT(&nd, CREATE, flags, UIO_USERSPACE, SCARG(uap, link), p); + if ((error = namei(&nd)) != 0) + goto out; + if (nd.ni_vp) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vrele(nd.ni_vp); + error = EEXIST; + goto out; + } + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); +out: + vrele(vp); + return (error); +} + +/* + * Make a symbolic link. + */ +/* ARGSUSED */ +int +sys_symlink(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_symlink_args /* { + syscallarg(char *) path; + syscallarg(char *) link; + } */ *uap = v; + struct vattr vattr; + char *path; + int error; + struct nameidata nd; + + MALLOC(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); + error = copyinstr(SCARG(uap, path), path, MAXPATHLEN, NULL); + if (error) + goto out; + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, link), p); + if ((error = namei(&nd)) != 0) + goto out; + if (nd.ni_vp) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vrele(nd.ni_vp); + error = EEXIST; + goto out; + } + VATTR_NULL(&vattr); + vattr.va_mode = ACCESSPERMS &~ p->p_fd->fd_cmask; + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + error = VOP_SYMLINK(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr, path); +out: + FREE(path, M_NAMEI); + return (error); +} + +/* + * Delete a whiteout from the filesystem. + */ +/* ARGSUSED */ +int +sys_undelete(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_undelete_args /* { + syscallarg(char *) path; + } */ *uap = v; + int error; + struct nameidata nd; + + NDINIT(&nd, DELETE, LOCKPARENT|DOWHITEOUT, UIO_USERSPACE, + SCARG(uap, path), p); + error = namei(&nd); + if (error) + return (error); + + if (nd.ni_vp != NULLVP || !(nd.ni_cnd.cn_flags & ISWHITEOUT)) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + if (nd.ni_vp) + vrele(nd.ni_vp); + return (EEXIST); + } + + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + if ((error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE)) != 0) + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + vput(nd.ni_dvp); + return (error); +} + +/* + * Delete a name from the filesystem. + */ +/* ARGSUSED */ +int +sys_unlink(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_unlink_args /* { + syscallarg(char *) path; + } */ *uap = v; + register struct vnode *vp; + int error; + struct nameidata nd; + + NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + + /* + * The root of a mounted filesystem cannot be deleted. + */ + if (vp->v_flag & VROOT) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vput(vp); + error = EBUSY; + goto out; + } + + (void)uvm_vnp_uncache(vp); + + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); +out: + return (error); +} + +/* + * Reposition read/write file offset. + */ +int +sys_lseek(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_lseek_args /* { + syscallarg(int) fd; + syscallarg(int) pad; + syscallarg(off_t) offset; + syscallarg(int) whence; + } */ *uap = v; + struct ucred *cred = p->p_ucred; + register struct filedesc *fdp = p->p_fd; + register struct file *fp; + struct vattr vattr; + struct vnode *vp; + int error, special; + + if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL) + return (EBADF); + if (fp->f_type != DTYPE_VNODE) + return (ESPIPE); + vp = (struct vnode *)fp->f_data; + if (vp->v_type == VFIFO) + return (ESPIPE); + if (vp->v_type == VCHR) + special = 1; + else + special = 0; + switch (SCARG(uap, whence)) { + case SEEK_CUR: + if (!special && fp->f_offset + SCARG(uap, offset) < 0) + return (EINVAL); + fp->f_offset += SCARG(uap, offset); + break; + case SEEK_END: + error = VOP_GETATTR((struct vnode *)fp->f_data, &vattr, + cred, p); + if (error) + return (error); + if (!special && (off_t)vattr.va_size + SCARG(uap, offset) < 0) + return (EINVAL); + fp->f_offset = SCARG(uap, offset) + vattr.va_size; + break; + case SEEK_SET: + if (!special && SCARG(uap, offset) < 0) + return (EINVAL); + fp->f_offset = SCARG(uap, offset); + break; + default: + return (EINVAL); + } + *(off_t *)retval = fp->f_offset; + return (0); +} + +/* + * Check access permissions. + */ +int +sys_access(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_access_args /* { + syscallarg(char *) path; + syscallarg(int) flags; + } */ *uap = v; + register struct ucred *cred = p->p_ucred; + register struct vnode *vp; + int error, flags, t_gid, t_uid; + struct nameidata nd; + + if (SCARG(uap, flags) & ~(R_OK | W_OK | X_OK)) + return (EINVAL); + t_uid = cred->cr_uid; + t_gid = cred->cr_gid; + cred->cr_uid = p->p_cred->p_ruid; + cred->cr_gid = p->p_cred->p_rgid; + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + goto out1; + vp = nd.ni_vp; + + /* Flags == 0 means only check for existence. */ + if (SCARG(uap, flags)) { + flags = 0; + if (SCARG(uap, flags) & R_OK) + flags |= VREAD; + if (SCARG(uap, flags) & W_OK) + flags |= VWRITE; + if (SCARG(uap, flags) & X_OK) + flags |= VEXEC; + if ((flags & VWRITE) == 0 || (error = vn_writechk(vp)) == 0) + error = VOP_ACCESS(vp, flags, cred, p); + } + vput(vp); +out1: + cred->cr_uid = t_uid; + cred->cr_gid = t_gid; + return (error); +} + +/* + * Get file status; this version follows links. + */ +/* ARGSUSED */ +int +sys_stat(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_stat_args /* { + syscallarg(char *) path; + syscallarg(struct stat *) ub; + } */ *uap = v; + struct stat sb; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + error = vn_stat(nd.ni_vp, &sb, p); + vput(nd.ni_vp); + if (error) + return (error); + /* Don't let non-root see generation numbers (for NFS security) */ + if (suser(p->p_ucred, &p->p_acflag)) + sb.st_gen = 0; + error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); + return (error); +} + +/* + * Get file status; this version does not follow links. + */ +/* ARGSUSED */ +int +sys_lstat(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_lstat_args /* { + syscallarg(char *) path; + syscallarg(struct stat *) ub; + } */ *uap = v; + struct stat sb; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + error = vn_stat(nd.ni_vp, &sb, p); + vput(nd.ni_vp); + if (error) + return (error); + /* Don't let non-root see generation numbers (for NFS security) */ + if (suser(p->p_ucred, &p->p_acflag)) + sb.st_gen = 0; + error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); + return (error); +} + +/* + * Get configurable pathname variables. + */ +/* ARGSUSED */ +int +sys_pathconf(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_pathconf_args /* { + syscallarg(char *) path; + syscallarg(int) name; + } */ *uap = v; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + error = VOP_PATHCONF(nd.ni_vp, SCARG(uap, name), retval); + vput(nd.ni_vp); + return (error); +} + +/* + * Return target name of a symbolic link. + */ +/* ARGSUSED */ +int +sys_readlink(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_readlink_args /* { + syscallarg(char *) path; + syscallarg(char *) buf; + syscallarg(size_t) count; + } */ *uap = v; + register struct vnode *vp; + struct iovec aiov; + struct uio auio; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + if (vp->v_type != VLNK) + error = EINVAL; + else { + aiov.iov_base = SCARG(uap, buf); + aiov.iov_len = SCARG(uap, count); + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_offset = 0; + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + auio.uio_resid = SCARG(uap, count); + error = VOP_READLINK(vp, &auio, p->p_ucred); + } + vput(vp); + *retval = SCARG(uap, count) - auio.uio_resid; + return (error); +} + +/* + * Change flags of a file given a path name. + */ +/* ARGSUSED */ +int +sys_chflags(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_chflags_args /* { + syscallarg(char *) path; + syscallarg(unsigned int) flags; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else if (SCARG(uap, flags) == VNOVAL) + error = EINVAL; + else { + if (suser(p->p_ucred, &p->p_acflag)) { + if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) != 0) + goto out; + if (vattr.va_type == VCHR || vattr.va_type == VBLK) { + error = EINVAL; + goto out; + } + } + VATTR_NULL(&vattr); + vattr.va_flags = SCARG(uap, flags); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } +out: + vput(vp); + return (error); +} + +/* + * Change flags of a file given a file descriptor. + */ +/* ARGSUSED */ +int +sys_fchflags(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_fchflags_args /* { + syscallarg(int) fd; + syscallarg(unsigned int) flags; + } */ *uap = v; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else if (SCARG(uap, flags) == VNOVAL) + error = EINVAL; + else { + if (suser(p->p_ucred, &p->p_acflag)) { + if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) + != 0) + goto out; + if (vattr.va_type == VCHR || vattr.va_type == VBLK) { + error = EINVAL; + goto out; + } + } + VATTR_NULL(&vattr); + vattr.va_flags = SCARG(uap, flags); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } +out: + VOP_UNLOCK(vp, 0, p); + FRELE(fp); + return (error); +} + +/* + * Change mode of a file given path name. + */ +/* ARGSUSED */ +int +sys_chmod(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_chmod_args /* { + syscallarg(char *) path; + syscallarg(int) mode; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + if (SCARG(uap, mode) & ~(S_IFMT | ALLPERMS)) + return (EINVAL); + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + VATTR_NULL(&vattr); + vattr.va_mode = SCARG(uap, mode) & ALLPERMS; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + vput(vp); + return (error); +} + +/* + * Change mode of a file given a file descriptor. + */ +/* ARGSUSED */ +int +sys_fchmod(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_fchmod_args /* { + syscallarg(int) fd; + syscallarg(int) mode; + } */ *uap = v; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if (SCARG(uap, mode) & ~(S_IFMT | ALLPERMS)) + return (EINVAL); + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + VATTR_NULL(&vattr); + vattr.va_mode = SCARG(uap, mode) & ALLPERMS; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + VOP_UNLOCK(vp, 0, p); + FRELE(fp); + return (error); +} + +/* + * Set ownership given a path name. + */ +/* ARGSUSED */ +int +sys_chown(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_chown_args /* { + syscallarg(char *) path; + syscallarg(int) uid; + syscallarg(int) gid; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + u_short mode; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + if ((SCARG(uap, uid) != -1 || SCARG(uap, gid) != -1) && + (suser(p->p_ucred, &p->p_acflag) || suid_clear)) { + error = VOP_GETATTR(vp, &vattr, p->p_ucred, p); + if (error) + goto out; + mode = vattr.va_mode & ~(VSUID | VSGID); + if (mode == vattr.va_mode) + mode = VNOVAL; + } + else + mode = VNOVAL; + VATTR_NULL(&vattr); + vattr.va_uid = SCARG(uap, uid); + vattr.va_gid = SCARG(uap, gid); + vattr.va_mode = mode; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } +out: + vput(vp); + return (error); +} + +/* + * Set ownership given a path name, without following links. + */ +/* ARGSUSED */ +int +sys_lchown(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_lchown_args /* { + syscallarg(char *) path; + syscallarg(int) uid; + syscallarg(int) gid; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + u_short mode; + + NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + if ((SCARG(uap, uid) != -1 || SCARG(uap, gid) != -1) && + (suser(p->p_ucred, &p->p_acflag) || suid_clear)) { + error = VOP_GETATTR(vp, &vattr, p->p_ucred, p); + if (error) + goto out; + mode = vattr.va_mode & ~(VSUID | VSGID); + if (mode == vattr.va_mode) + mode = VNOVAL; + } + else + mode = VNOVAL; + VATTR_NULL(&vattr); + vattr.va_uid = SCARG(uap, uid); + vattr.va_gid = SCARG(uap, gid); + vattr.va_mode = mode; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } +out: + vput(vp); + return (error); +} + +/* + * Set ownership given a file descriptor. + */ +/* ARGSUSED */ +int +sys_fchown(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_fchown_args /* { + syscallarg(int) fd; + syscallarg(int) uid; + syscallarg(int) gid; + } */ *uap = v; + struct vnode *vp; + struct vattr vattr; + int error; + struct file *fp; + u_short mode; + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + if ((SCARG(uap, uid) != -1 || SCARG(uap, gid) != -1) && + (suser(p->p_ucred, &p->p_acflag) || suid_clear)) { + error = VOP_GETATTR(vp, &vattr, p->p_ucred, p); + if (error) + goto out; + mode = vattr.va_mode & ~(VSUID | VSGID); + if (mode == vattr.va_mode) + mode = VNOVAL; + } else + mode = VNOVAL; + VATTR_NULL(&vattr); + vattr.va_uid = SCARG(uap, uid); + vattr.va_gid = SCARG(uap, gid); + vattr.va_mode = mode; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } +out: + VOP_UNLOCK(vp, 0, p); + FRELE(fp); + return (error); +} + +/* + * Set the access and modification times given a path name. + */ +/* ARGSUSED */ +int +sys_utimes(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_utimes_args /* { + syscallarg(char *) path; + syscallarg(struct timeval *) tptr; + } */ *uap = v; + register struct vnode *vp; + struct timeval tv[2]; + struct vattr vattr; + int error; + struct nameidata nd; + + VATTR_NULL(&vattr); + if (SCARG(uap, tptr) == NULL) { + microtime(&tv[0]); + tv[1] = tv[0]; + vattr.va_vaflags |= VA_UTIMES_NULL; + } else { + error = copyin((caddr_t)SCARG(uap, tptr), (caddr_t)tv, + sizeof (tv)); + if (error) + return (error); + /* XXX workaround timeval matching the VFS constant VNOVAL */ + if (tv[0].tv_sec == VNOVAL) + tv[0].tv_sec = VNOVAL - 1; + if (tv[1].tv_sec == VNOVAL) + tv[1].tv_sec = VNOVAL - 1; + } + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + vattr.va_atime.tv_sec = tv[0].tv_sec; + vattr.va_atime.tv_nsec = tv[0].tv_usec * 1000; + vattr.va_mtime.tv_sec = tv[1].tv_sec; + vattr.va_mtime.tv_nsec = tv[1].tv_usec * 1000; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + vput(vp); + return (error); +} + + +/* + * Set the access and modification times given a file descriptor. + */ +/* ARGSUSED */ +int +sys_futimes(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_futimes_args /* { + syscallarg(int) fd; + syscallarg(struct timeval *) tptr; + } */ *uap = v; + struct vnode *vp; + struct timeval tv[2]; + struct vattr vattr; + int error; + struct file *fp; + + VATTR_NULL(&vattr); + if (SCARG(uap, tptr) == NULL) { + microtime(&tv[0]); + tv[1] = tv[0]; + vattr.va_vaflags |= VA_UTIMES_NULL; + } else { + error = copyin((caddr_t)SCARG(uap, tptr), (caddr_t)tv, + sizeof (tv)); + if (error) + return (error); + /* XXX workaround timeval matching the VFS constant VNOVAL */ + if (tv[0].tv_sec == VNOVAL) + tv[0].tv_sec = VNOVAL - 1; + if (tv[1].tv_sec == VNOVAL) + tv[1].tv_sec = VNOVAL - 1; + } + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { + vattr.va_atime.tv_sec = tv[0].tv_sec; + vattr.va_atime.tv_nsec = tv[0].tv_usec * 1000; + vattr.va_mtime.tv_sec = tv[1].tv_sec; + vattr.va_mtime.tv_nsec = tv[1].tv_usec * 1000; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + VOP_UNLOCK(vp, 0, p); + FRELE(fp); + return (error); +} + +/* + * Truncate a file given its path name. + */ +/* ARGSUSED */ +int +sys_truncate(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_truncate_args /* { + syscallarg(char *) path; + syscallarg(int) pad; + syscallarg(off_t) length; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_type == VDIR) + error = EISDIR; + else if ((error = vn_writechk(vp)) == 0 && + (error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p)) == 0) { + VATTR_NULL(&vattr); + vattr.va_size = SCARG(uap, length); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + vput(vp); + return (error); +} + +/* + * Truncate a file given a file descriptor. + */ +/* ARGSUSED */ +int +sys_ftruncate(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_ftruncate_args /* { + syscallarg(int) fd; + syscallarg(int) pad; + syscallarg(off_t) length; + } */ *uap = v; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + if ((fp->f_flag & FWRITE) == 0) { + error = EINVAL; + goto bad; + } + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_type == VDIR) + error = EISDIR; + else if ((error = vn_writechk(vp)) == 0) { + VATTR_NULL(&vattr); + vattr.va_size = SCARG(uap, length); + error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); + } + VOP_UNLOCK(vp, 0, p); +bad: + FRELE(fp); + return (error); +} + +/* + * Sync an open file. + */ +/* ARGSUSED */ +int +sys_fsync(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_fsync_args /* { + syscallarg(int) fd; + } */ *uap = v; + struct vnode *vp; + struct file *fp; + int error; + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + vp = (struct vnode *)fp->f_data; + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p); +#ifdef FFS_SOFTUPDATES + if (error == 0 && vp->v_mount && (vp->v_mount->mnt_flag & MNT_SOFTDEP)) + error = softdep_fsync(vp); +#endif + + VOP_UNLOCK(vp, 0, p); + FRELE(fp); + return (error); +} + +/* + * Rename files. Source and destination must either both be directories, + * or both not be directories. If target is a directory, it must be empty. + */ +/* ARGSUSED */ +int +sys_rename(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_rename_args /* { + syscallarg(char *) from; + syscallarg(char *) to; + } */ *uap = v; + register struct vnode *tvp, *fvp, *tdvp; + struct nameidata fromnd, tond; + int error; + int flags; + + NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE, + SCARG(uap, from), p); + if ((error = namei(&fromnd)) != 0) + return (error); + fvp = fromnd.ni_vp; + + flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART; + /* + * rename("foo/", "bar/"); is OK + */ + if (fvp->v_type == VDIR) + flags |= STRIPSLASHES; + + NDINIT(&tond, RENAME, flags, + UIO_USERSPACE, SCARG(uap, to), p); + if ((error = namei(&tond)) != 0) { + VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); + vrele(fromnd.ni_dvp); + vrele(fvp); + goto out1; + } + tdvp = tond.ni_dvp; + tvp = tond.ni_vp; + if (tvp != NULL) { + if (fvp->v_type == VDIR && tvp->v_type != VDIR) { + error = ENOTDIR; + goto out; + } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) { + error = EISDIR; + goto out; + } + } + if (fvp == tdvp) + error = EINVAL; + /* + * If source is the same as the destination (that is the + * same inode number) + */ + if (fvp == tvp) + error = -1; +out: + if (!error) { + VOP_LEASE(tdvp, p, p->p_ucred, LEASE_WRITE); + if (fromnd.ni_dvp != tdvp) + VOP_LEASE(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + if (tvp) { + (void)uvm_vnp_uncache(tvp); + VOP_LEASE(tvp, p, p->p_ucred, LEASE_WRITE); + } + error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, + tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); + } else { + VOP_ABORTOP(tond.ni_dvp, &tond.ni_cnd); + if (tdvp == tvp) + vrele(tdvp); + else + vput(tdvp); + if (tvp) + vput(tvp); + VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); + vrele(fromnd.ni_dvp); + vrele(fvp); + } + vrele(tond.ni_startdir); + FREE(tond.ni_cnd.cn_pnbuf, M_NAMEI); +out1: + if (fromnd.ni_startdir) + vrele(fromnd.ni_startdir); + FREE(fromnd.ni_cnd.cn_pnbuf, M_NAMEI); + if (error == -1) + return (0); + return (error); +} + +/* + * Make a directory file. + */ +/* ARGSUSED */ +int +sys_mkdir(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_mkdir_args /* { + syscallarg(char *) path; + syscallarg(int) mode; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, CREATE, LOCKPARENT | STRIPSLASHES, + UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + if (vp != NULL) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vrele(vp); + return (EEXIST); + } + VATTR_NULL(&vattr); + vattr.va_type = VDIR; + vattr.va_mode = (SCARG(uap, mode) & ACCESSPERMS) &~ p->p_fd->fd_cmask; + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + error = VOP_MKDIR(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr); + if (!error) + vput(nd.ni_vp); + return (error); +} + +/* + * Remove a directory file. + */ +/* ARGSUSED */ +int +sys_rmdir(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_rmdir_args /* { + syscallarg(char *) path; + } */ *uap = v; + register struct vnode *vp; + int error; + struct nameidata nd; + + NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + if (vp->v_type != VDIR) { + error = ENOTDIR; + goto out; + } + /* + * No rmdir "." please. + */ + if (nd.ni_dvp == vp) { + error = EBUSY; + goto out; + } + /* + * The root of a mounted filesystem cannot be deleted. + */ + if (vp->v_flag & VROOT) + error = EBUSY; +out: + if (!error) { + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + error = VOP_RMDIR(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); + } else { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vput(vp); + } + return (error); +} + +/* + * Read a block of directory entries in a file system independent format. + */ +int +sys_getdirentries(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_getdirentries_args /* { + syscallarg(int) fd; + syscallarg(char *) buf; + syscallarg(int) count; + syscallarg(long *) basep; + } */ *uap = v; + struct vnode *vp; + struct file *fp; + struct uio auio; + struct iovec aiov; + long loff; + int error, eofflag; + + if (SCARG(uap, count) < 0) + return EINVAL; + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + if ((fp->f_flag & FREAD) == 0) { + error = EBADF; + goto bad; + } + vp = (struct vnode *)fp->f_data; +unionread: + if (vp->v_type != VDIR) { + error = EINVAL; + goto bad; + } + aiov.iov_base = SCARG(uap, buf); + aiov.iov_len = SCARG(uap, count); + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + auio.uio_resid = SCARG(uap, count); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + loff = auio.uio_offset = fp->f_offset; + error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, 0, 0); + fp->f_offset = auio.uio_offset; + VOP_UNLOCK(vp, 0, p); + if (error) + goto bad; + if ((SCARG(uap, count) == auio.uio_resid) && + union_check_p && + (union_check_p(p, &vp, fp, auio, &error) != 0)) + goto unionread; + if (error) + goto bad; + + if ((SCARG(uap, count) == auio.uio_resid) && + (vp->v_flag & VROOT) && + (vp->v_mount->mnt_flag & MNT_UNION)) { + struct vnode *tvp = vp; + vp = vp->v_mount->mnt_vnodecovered; + VREF(vp); + fp->f_data = (caddr_t) vp; + fp->f_offset = 0; + vrele(tvp); + goto unionread; + } + error = copyout((caddr_t)&loff, (caddr_t)SCARG(uap, basep), + sizeof(long)); + *retval = SCARG(uap, count) - auio.uio_resid; +bad: + FRELE(fp); + return (error); +} + +/* + * Set the mode mask for creation of filesystem nodes. + */ +int +sys_umask(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_umask_args /* { + syscallarg(int) newmask; + } */ *uap = v; + register struct filedesc *fdp; + + fdp = p->p_fd; + *retval = fdp->fd_cmask; + fdp->fd_cmask = SCARG(uap, newmask) & ACCESSPERMS; + return (0); +} + +/* + * Void all references to file by ripping underlying filesystem + * away from vnode. + */ +/* ARGSUSED */ +int +sys_revoke(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + register struct sys_revoke_args /* { + syscallarg(char *) path; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; + int error; + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) != 0) + goto out; + if (p->p_ucred->cr_uid != vattr.va_uid && + (error = suser(p->p_ucred, &p->p_acflag))) + goto out; + if (vp->v_usecount > 1 || (vp->v_flag & (VALIASED | VLAYER))) + VOP_REVOKE(vp, REVOKEALL); +out: + vrele(vp); + return (error); +} + +/* + * Convert a user file descriptor to a kernel file entry. + * + * On return *fpp is FREF:ed. + */ +int +getvnode(fdp, fd, fpp) + struct filedesc *fdp; + struct file **fpp; + int fd; +{ + struct file *fp; + + if ((fp = fd_getfile(fdp, fd)) == NULL) + return (EBADF); + if (fp->f_type != DTYPE_VNODE) + return (EINVAL); + FREF(fp); + *fpp = fp; + + return (0); +} + +/* + * Positional read system call. + */ +int +sys_pread(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_pread_args /* { + syscallarg(int) fd; + syscallarg(void *) buf; + syscallarg(size_t) nbyte; + syscallarg(int) pad; + syscallarg(off_t) offset; + } */ *uap = v; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp; + off_t offset; + int fd = SCARG(uap, fd); + + if ((fp = fd_getfile(fdp, fd)) == NULL) + return (EBADF); + if ((fp->f_flag & FREAD) == 0) + return (EBADF); + + vp = (struct vnode *)fp->f_data; + if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { + return (ESPIPE); + } + + offset = SCARG(uap, offset); + + FREF(fp); + + /* dofileread() will FRELE the descriptor for us */ + return (dofileread(p, fd, fp, SCARG(uap, buf), SCARG(uap, nbyte), + &offset, retval)); +} + +/* + * Positional scatter read system call. + */ +int +sys_preadv(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_preadv_args /* { + syscallarg(int) fd; + syscallarg(const struct iovec *) iovp; + syscallarg(int) iovcnt; + syscallarg(int) pad; + syscallarg(off_t) offset; + } */ *uap = v; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp; + off_t offset; + int fd = SCARG(uap, fd); + + if ((fp = fd_getfile(fdp, fd)) == NULL) + return (EBADF); + if ((fp->f_flag & FREAD) == 0) + return (EBADF); + + vp = (struct vnode *)fp->f_data; + if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { + return (ESPIPE); + } + + FREF(fp); + + offset = SCARG(uap, offset); + + /* dofilereadv() will FRELE the descriptor for us */ + return (dofilereadv(p, fd, fp, SCARG(uap, iovp), SCARG(uap, iovcnt), + &offset, retval)); +} + +/* + * Positional write system call. + */ +int +sys_pwrite(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_pwrite_args /* { + syscallarg(int) fd; + syscallarg(const void *) buf; + syscallarg(size_t) nbyte; + syscallarg(int) pad; + syscallarg(off_t) offset; + } */ *uap = v; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp; + off_t offset; + int fd = SCARG(uap, fd); + + if ((fp = fd_getfile(fdp, fd)) == NULL) + return (EBADF); + if ((fp->f_flag & FWRITE) == 0) + return (EBADF); + + vp = (struct vnode *)fp->f_data; + if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { + return (ESPIPE); + } + + FREF(fp); + + offset = SCARG(uap, offset); + + /* dofilewrite() will FRELE the descriptor for us */ + return (dofilewrite(p, fd, fp, SCARG(uap, buf), SCARG(uap, nbyte), + &offset, retval)); +} + + +/* + * Positional gather write system call. + */ +int +sys_pwritev(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_pwritev_args /* { + syscallarg(int) fd; + syscallarg(const struct iovec *) iovp; + syscallarg(int) iovcnt; + syscallarg(int) pad; + syscallarg(off_t) offset; + } */ *uap = v; + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct vnode *vp; + off_t offset; + int fd = SCARG(uap, fd); + + if ((fp = fd_getfile(fdp, fd)) == NULL) + return (EBADF); + if ((fp->f_flag & FWRITE) == 0) + return (EBADF); + + vp = (struct vnode *)fp->f_data; + if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { + return (ESPIPE); + } + + FREF(fp); + + offset = SCARG(uap, offset); + + /* dofilewritev() will FRELE the descriptor for us */ + return (dofilewritev(p, fd, fp, SCARG(uap, iovp), SCARG(uap, iovcnt), + &offset, retval)); +} + +#ifdef UFS_EXTATTR +/* + * Syscall to push extended attribute configuration information into the + * VFS. Accepts a path, which it converts to a mountpoint, as well as + * a command (int cmd), and attribute name and misc data. For now, the + * attribute name is left in userspace for consumption by the VFS_op. + * It will probably be changed to be copied into sysspace by the + * syscall in the future, once issues with various consumers of the + * attribute code have raised their hands. + * + * Currently this is used only by UFS Extended Attributes. + */ +int +sys_extattrctl(struct proc *p, void *v, register_t *reval) +{ + struct sys_extattrctl_args /* { + syscallarg(const char *) path; + syscallarg(int) cmd; + syscallarg(const char *) filename; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + } */ *uap = v; + struct vnode *filename_vp; + struct nameidata nd; + struct mount *mp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + /* + * SCARG(uap, attrname) not always defined. We check again later + * when we invoke the VFS call so as to pass in NULL there if needed. + */ + if (SCARG(uap, attrname) != NULL) { + error = copyinstr(SCARG(uap, attrname), attrname, + EXTATTR_MAXNAMELEN, NULL); + if (error) + return (error); + } + + /* + * SCARG(uap, filename) not always defined. If it is, grab + * a vnode lock, which VFS_EXTATTRCTL() will later release. + */ + filename_vp = NULL; + if (SCARG(uap, filename) != NULL) { + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, filename), p); + if ((error = namei(&nd)) != 0) + return (error); + filename_vp = nd.ni_vp; + } + + /* SCARG(uap, path) always defined. */ + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) { + if (filename_vp != NULL) + vput(filename_vp); + return (error); + } + + mp = nd.ni_vp->v_mount; + if (error) { + if (filename_vp != NULL) + vput(filename_vp); + return (error); + } + + if (SCARG(uap, attrname) != NULL) { + error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), filename_vp, + SCARG(uap, attrnamespace), attrname, p); + } else { + error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), filename_vp, + SCARG(uap, attrnamespace), NULL, p); + } + + /* + * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, + * filename_vp, so vrele it if it is defined. + */ + if (filename_vp != NULL) + vrele(filename_vp); + + return (error); +} + +/*- + * Set a named extended attribute on a file or directory + * + * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", + * kernelspace string pointer "attrname", userspace buffer + * pointer "data", buffer length "nbytes", thread "td". + * Returns: 0 on success, an error number otherwise + * Locks: none + * References: vp must be a valid reference for the duration of the call + */ +static int +extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname, + void *data, size_t nbytes, struct proc *p, register_t *retval) +{ + struct uio auio; + struct iovec aiov; + ssize_t cnt; + int error; + + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + aiov.iov_base = data; + aiov.iov_len = nbytes; + auio.uio_iov = &aiov; + auio.uio_iovcnt = 1; + auio.uio_offset = 0; + if (nbytes > INT_MAX) { + error = EINVAL; + goto done; + } + auio.uio_resid = nbytes; + auio.uio_rw = UIO_WRITE; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + cnt = nbytes; + + error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio, + p->p_ucred, p); + cnt -= auio.uio_resid; + retval[0] = cnt; + +done: + VOP_UNLOCK(vp, 0, p); + return (error); +} + +int +sys_extattr_set_file(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_set_file_args /* { + syscallarg(const char *) path; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + syscallarg(void *) data; + syscallarg(size_t) nbytes; + } */ *uap = v; + struct nameidata nd; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + + error = extattr_set_vp(nd.ni_vp, SCARG(uap, attrnamespace), attrname, + SCARG(uap, data), SCARG(uap, nbytes), p, retval); + + vrele(nd.ni_vp); + return (error); +} + +int +sys_extattr_set_fd(struct proc *p, void *v, register_t *retval) +{ + struct sys_extattr_set_fd_args /* { + syscallarg(int) fd; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + syscallarg(struct iovec *) iovp; + syscallarg(int) iovcnt; + } */ *uap = v; + struct file *fp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + + error = extattr_set_vp((struct vnode *)fp->f_data, + SCARG(uap, attrnamespace), attrname, SCARG(uap, data), + SCARG(uap, nbytes), p, retval); + FRELE(fp); + + return (error); +} + +/*- + * Get a named extended attribute on a file or directory + * + * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", + * kernelspace string pointer "attrname", userspace buffer + * pointer "data", buffer length "nbytes", thread "td". + * Returns: 0 on success, an error number otherwise + * Locks: none + * References: vp must be a valid reference for the duration of the call + */ +static int +extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname, + void *data, size_t nbytes, struct proc *p, register_t *retval) +{ + struct uio auio; + struct iovec aiov; + ssize_t cnt; + size_t size; + int error; + + VOP_LEASE(vp, p, p->p_ucred, LEASE_READ); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + /* + * Slightly unusual semantics: if the user provides a NULL data + * pointer, they don't want to receive the data, just the + * maximum read length. + */ + if (data != NULL) { + aiov.iov_base = data; + aiov.iov_len = nbytes; + auio.uio_iov = &aiov; + auio.uio_offset = 0; + if (nbytes > INT_MAX) { + error = EINVAL; + goto done; + } + auio.uio_resid = nbytes; + auio.uio_rw = UIO_READ; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + cnt = nbytes; + error = VOP_GETEXTATTR(vp, attrnamespace, attrname, &auio, + NULL, p->p_ucred, p); + cnt -= auio.uio_resid; + retval[0] = cnt; + } else { + error = VOP_GETEXTATTR(vp, attrnamespace, attrname, NULL, + &size, p->p_ucred, p); + retval[0] = size; + } +done: + VOP_UNLOCK(vp, 0, p); + return (error); +} + +int +sys_extattr_get_file(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_extattr_get_file_args /* { + syscallarg(const char *) path; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + syscallarg(void *) data; + syscallarg(size_t) nbytes; + } */ *uap = v; + struct nameidata nd; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return (error); + + error = extattr_get_vp(nd.ni_vp, SCARG(uap, attrnamespace), attrname, + SCARG(uap, data), SCARG(uap, nbytes), p, retval); + + vrele(nd.ni_vp); + return (error); +} + +int +sys_extattr_get_fd(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_extattr_get_fd_args /* { + syscallarg(int) fd; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + syscallarg(void *) data; + syscallarg(size_t) nbytes; + } */ *uap = v; + struct file *fp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + + error = extattr_get_vp((struct vnode *)fp->f_data, + SCARG(uap, attrnamespace), attrname, SCARG(uap, data), + SCARG(uap, nbytes), p, retval); + FRELE(fp); + + return (error); +} + +/* + * extattr_delete_vp(): Delete a named extended attribute on a file or + * directory + * + * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", + * kernelspace string pointer "attrname", proc "p" + * Returns: 0 on success, an error number otherwise + * Locks: none + * References: vp must be a valid reference for the duration of the call + */ +static int +extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname, + struct proc *p) +{ + int error; + + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + + error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL, + p->p_ucred, p); + + VOP_UNLOCK(vp, 0, p); + return (error); +} + +int +sys_extattr_delete_file(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_extattr_delete_file_args /* { + syscallarg(int) fd; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + } */ *uap = v; + struct nameidata nd; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return(error); + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + if ((error = namei(&nd)) != 0) + return(error); + + error = extattr_delete_vp(nd.ni_vp, SCARG(uap, attrnamespace), + attrname, p); + + vrele(nd.ni_vp); + return(error); +} + +int +sys_extattr_delete_fd(p, v, retval) + struct proc *p; + void *v; + register_t *retval; +{ + struct sys_extattr_delete_fd_args /* { + syscallarg(int) fd; + syscallarg(int) attrnamespace; + syscallarg(const char *) attrname; + } */ *uap = v; + struct file *fp; + char attrname[EXTATTR_MAXNAMELEN]; + int error; + + error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, + NULL); + if (error) + return (error); + + if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + + error = extattr_delete_vp((struct vnode *)fp->f_data, + SCARG(uap, attrnamespace), attrname, p); + FRELE(fp); + + return (error); +} +#endif diff --git a/sys/src/cmd/diff/test/diff-t9.expected b/sys/src/cmd/diff/test/diff-t9.expected new file mode 100644 index 000000000..6071e0f2a --- /dev/null +++ b/sys/src/cmd/diff/test/diff-t9.expected @@ -0,0 +1,3182 @@ +--- diff-t9.1 ++++ diff-t9.2 +@@ -1,4 +1,5 @@ +-/* $NetBSD: vfs_syscalls.c,v 1.57 1995/10/07 06:28:51 mycroft Exp $ */ ++/* $OpenBSD: t9.2,v 1.2 2013/12/01 16:40:56 krw Exp $ */ ++/* $NetBSD: vfs_syscalls.c,v 1.71 1996/04/23 10:29:02 mycroft Exp $ */ + + /* + * Copyright (c) 1989, 1993 +@@ -17,11 +18,7 @@ + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. +- * 3. All advertising materials mentioning features or use of this software +- * must display the following acknowledgement: +- * This product includes software developed by the University of +- * California, Berkeley and its contributors. +- * 4. Neither the name of the University nor the names of its contributors ++ * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * +@@ -53,13 +50,30 @@ + #include <sys/uio.h> + #include <sys/malloc.h> + #include <sys/dirent.h> ++#include <sys/extattr.h> + + #include <sys/syscallargs.h> + +-#include <vm/vm.h> ++#include <uvm/uvm_extern.h> + #include <sys/sysctl.h> + +-static int change_dir __P((struct nameidata *ndp, struct proc *p)); ++extern int suid_clear; ++int usermount = 0; /* sysctl: by default, users may not mount */ ++ ++static int change_dir(struct nameidata *, struct proc *); ++ ++void checkdirs(struct vnode *); ++ ++/* ++ * Redirection info so we don't have to include the union fs routines in ++ * the kernel directly. This way, we can build unionfs as an LKM. The ++ * pointer gets filled in later, when we modload the LKM, or when the ++ * compiled-in unionfs code gets initialized. For now, we just set ++ * it to a stub routine. ++ */ ++ ++int (*union_check_p)(struct proc *, struct vnode **, ++ struct file *, struct uio, int *) = NULL; + + /* + * Virtual File System System Calls +@@ -69,6 +83,7 @@ + * Mount a file system. + */ + /* ARGSUSED */ ++int + sys_mount(p, v, retval) + struct proc *p; + void *v; +@@ -78,22 +93,36 @@ + syscallarg(char *) type; + syscallarg(char *) path; + syscallarg(int) flags; +- syscallarg(caddr_t) data; ++ syscallarg(void *) data; + } */ *uap = v; + register struct vnode *vp; + register struct mount *mp; +- int error, flag; +- u_long fsindex; ++ int error, flag = 0; ++#ifdef COMPAT_43 ++ u_long fstypenum = 0; ++#endif + char fstypename[MFSNAMELEN]; ++ char fspath[MNAMELEN]; + struct vattr va; + struct nameidata nd; ++ struct vfsconf *vfsp; ++ struct timeval tv; ++ ++ if (usermount == 0 && (error = suser(p->p_ucred, &p->p_acflag))) ++ return (error); ++ ++ /* ++ * Mount points must fit in MNAMELEN, not MAXPATHLEN. ++ */ ++ error = copyinstr(SCARG(uap, path), fspath, MNAMELEN, NULL); ++ if (error) ++ return(error); + + /* + * Get vnode to be covered + */ +- NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, +- SCARG(uap, path), p); +- if (error = namei(&nd)) ++ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, fspath, p); ++ if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + if (SCARG(uap, flags) & MNT_UPDATE) { +@@ -113,7 +142,7 @@ + return (EOPNOTSUPP); /* Needs translation */ + } + mp->mnt_flag |= +- SCARG(uap, flags) & (MNT_RELOAD | MNT_FORCE | MNT_UPDATE); ++ SCARG(uap, flags) & (MNT_RELOAD | MNT_UPDATE); + /* + * Only root, or the user that did the original mount is + * permitted to update it. +@@ -134,7 +163,11 @@ + } + SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; + } +- VOP_UNLOCK(vp); ++ if ((error = vfs_busy(mp, LK_NOWAIT, 0, p)) != 0) { ++ vput(vp); ++ return (error); ++ } ++ VOP_UNLOCK(vp, 0, p); + goto update; + } + /* +@@ -143,7 +176,7 @@ + */ + if ((error = VOP_GETATTR(vp, &va, p->p_ucred, p)) || + (va.va_uid != p->p_ucred->cr_uid && +- (error = suser(p->p_ucred, &p->p_acflag)))) { ++ (error = suser(p->p_ucred, &p->p_acflag)))) { + vput(vp); + return (error); + } +@@ -158,40 +191,47 @@ + } + SCARG(uap, flags) |= MNT_NOSUID | MNT_NODEV; + } +- if (error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) ++ if ((error = vinvalbuf(vp, V_SAVE, p->p_ucred, p, 0, 0)) != 0) + return (error); + if (vp->v_type != VDIR) { + vput(vp); + return (ENOTDIR); + } +- if (error = copyinstr(SCARG(uap, type), fstypename, MFSNAMELEN, +- (size_t *)0)) { +-#if defined(COMPAT_09) || defined(COMPAT_43) ++ error = copyinstr(SCARG(uap, type), fstypename, MFSNAMELEN, NULL); ++ if (error) { ++#ifdef COMPAT_43 + /* + * Historically filesystem types were identified by number. + * If we get an integer for the filesystem type instead of a + * string, we check to see if it matches one of the historic + * filesystem types. +- */ +- fsindex = (u_long)SCARG(uap, type); +- if (fsindex >= nvfssw || vfssw[fsindex] == NULL) { ++ */ ++ fstypenum = (u_long)SCARG(uap, type); ++ ++ for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) ++ if (vfsp->vfc_typenum == fstypenum) ++ break; ++ if (vfsp == NULL) { + vput(vp); + return (ENODEV); + } +- strncpy(fstypename, vfssw[fsindex]->vfs_name, MFSNAMELEN); ++ strncpy(fstypename, vfsp->vfc_name, MFSNAMELEN); ++ + #else + vput(vp); + return (error); + #endif + } +- for (fsindex = 0; fsindex < nvfssw; fsindex++) +- if (vfssw[fsindex] != NULL && +- !strncmp(vfssw[fsindex]->vfs_name, fstypename, MFSNAMELEN)) ++ for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next) { ++ if (!strcmp(vfsp->vfc_name, fstypename)) + break; +- if (fsindex >= nvfssw) { ++ } ++ ++ if (vfsp == NULL) { + vput(vp); +- return (ENODEV); ++ return (EOPNOTSUPP); + } ++ + if (vp->v_mountedhere != NULL) { + vput(vp); + return (EBUSY); +@@ -203,15 +243,14 @@ + mp = (struct mount *)malloc((u_long)sizeof(struct mount), + M_MOUNT, M_WAITOK); + bzero((char *)mp, (u_long)sizeof(struct mount)); +- mp->mnt_op = vfssw[fsindex]; +- if (error = vfs_lock(mp)) { +- free((caddr_t)mp, M_MOUNT); +- vput(vp); +- return (error); +- } +- /* Do this early in case we block later. */ +- vfssw[fsindex]->vfs_refcount++; +- vp->v_mountedhere = mp; ++ lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, 0); ++ /* This error never happens, but it makes auditing easier */ ++ if ((error = vfs_busy(mp, LK_NOWAIT, 0, p))) ++ return (error); ++ mp->mnt_op = vfsp->vfc_vfsops; ++ mp->mnt_vfc = vfsp; ++ mp->mnt_flag |= (vfsp->vfc_flags & MNT_VISFLAGMASK); ++ strncpy(mp->mnt_stat.f_fstypename, vfsp->vfc_name, MFSNAMELEN); + mp->mnt_vnodecovered = vp; + mp->mnt_stat.f_owner = p->p_ucred->cr_uid; + update: +@@ -223,13 +262,19 @@ + else if (mp->mnt_flag & MNT_RDONLY) + mp->mnt_flag |= MNT_WANTRDWR; + mp->mnt_flag &=~ (MNT_NOSUID | MNT_NOEXEC | MNT_NODEV | +- MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); ++ MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | MNT_SOFTDEP | ++ MNT_NOATIME | MNT_FORCE); + mp->mnt_flag |= SCARG(uap, flags) & (MNT_NOSUID | MNT_NOEXEC | +- MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC); ++ MNT_NODEV | MNT_SYNCHRONOUS | MNT_UNION | MNT_ASYNC | ++ MNT_SOFTDEP | MNT_NOATIME | MNT_FORCE); + /* + * Mount the filesystem. + */ + error = VFS_MOUNT(mp, SCARG(uap, path), SCARG(uap, data), &nd, p); ++ if (!error) { ++ microtime(&tv); ++ mp->mnt_stat.f_ctime = tv.tv_sec; ++ } + if (mp->mnt_flag & MNT_UPDATE) { + vrele(vp); + if (mp->mnt_flag & MNT_WANTRDWR) +@@ -238,23 +283,42 @@ + (MNT_UPDATE | MNT_RELOAD | MNT_FORCE | MNT_WANTRDWR); + if (error) + mp->mnt_flag = flag; ++ ++ if ((mp->mnt_flag & MNT_RDONLY) == 0) { ++ if (mp->mnt_syncer == NULL) ++ error = vfs_allocate_syncvnode(mp); ++ } else { ++ if (mp->mnt_syncer != NULL) ++ vgone(mp->mnt_syncer); ++ mp->mnt_syncer = NULL; ++ } ++ ++ vfs_unbusy(mp, p); + return (error); + } ++ ++ vp->v_mountedhere = mp; ++ + /* + * Put the new filesystem on the mount list after root. + */ + cache_purge(vp); + if (!error) { ++ vfsp->vfc_refcount++; ++ simple_lock(&mountlist_slock); + TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list); ++ simple_unlock(&mountlist_slock); + checkdirs(vp); +- VOP_UNLOCK(vp); +- vfs_unlock(mp); ++ VOP_UNLOCK(vp, 0, p); ++ if ((mp->mnt_flag & MNT_RDONLY) == 0) ++ error = vfs_allocate_syncvnode(mp); ++ vfs_unbusy(mp, p); + (void) VFS_STATFS(mp, &mp->mnt_stat, p); +- error = VFS_START(mp, 0, p); ++ if ((error = VFS_START(mp, 0, p)) != 0) ++ vrele(vp); + } else { + mp->mnt_vnodecovered->v_mountedhere = (struct mount *)0; +- vfssw[fsindex]->vfs_refcount--; +- vfs_unlock(mp); ++ vfs_unbusy(mp, p); + free((caddr_t)mp, M_MOUNT); + vput(vp); + } +@@ -266,6 +330,7 @@ + * or root directory onto which the new filesystem has just been + * mounted. If so, replace them with the new mount point. + */ ++void + checkdirs(olddp) + struct vnode *olddp; + { +@@ -277,7 +342,7 @@ + return; + if (VFS_ROOT(olddp->v_mountedhere, &newdp)) + panic("mount: lost mount"); +- for (p = allproc.lh_first; p != 0; p = p->p_list.le_next) { ++ for (p = LIST_FIRST(&allproc); p != 0; p = LIST_NEXT(p, p_list)) { + fdp = p->p_fd; + if (fdp->fd_cdir == olddp) { + vrele(fdp->fd_cdir); +@@ -305,6 +370,7 @@ + * not special file (as before). + */ + /* ARGSUSED */ ++int + sys_unmount(p, v, retval) + struct proc *p; + void *v; +@@ -321,7 +387,7 @@ + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + mp = vp->v_mount; +@@ -352,50 +418,77 @@ + return (EINVAL); + } + vput(vp); +- return (dounmount(mp, SCARG(uap, flags), p)); ++ ++ if (vfs_busy(mp, LK_EXCLUSIVE, NULL, p)) ++ return (EBUSY); ++ ++ return (dounmount(mp, SCARG(uap, flags), p, vp)); + } + + /* + * Do the actual file system unmount. + */ +-dounmount(mp, flags, p) +- register struct mount *mp; +- int flags; +- struct proc *p; ++int ++dounmount(struct mount *mp, int flags, struct proc *p, struct vnode *olddp) + { + struct vnode *coveredvp; ++ struct proc *p2; + int error; ++ int hadsyncer = 0; + +- coveredvp = mp->mnt_vnodecovered; +- if (vfs_busy(mp)) +- return (EBUSY); +- mp->mnt_flag |= MNT_UNMOUNT; +- if (error = vfs_lock(mp)) +- return (error); +- +- mp->mnt_flag &=~ MNT_ASYNC; +- vnode_pager_umount(mp); /* release cached vnodes */ +- cache_purgevfs(mp); /* remove cache entries for this file sys */ +- if ((error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0 || +- (flags & MNT_FORCE)) +- error = VFS_UNMOUNT(mp, flags, p); +- mp->mnt_flag &= ~MNT_UNMOUNT; +- vfs_unbusy(mp); +- if (error) { +- vfs_unlock(mp); +- } else { +- TAILQ_REMOVE(&mountlist, mp, mnt_list); +- if (coveredvp != NULLVP) { +- vrele(coveredvp); +- coveredvp->v_mountedhere = (struct mount *)0; +- } +- mp->mnt_op->vfs_refcount--; +- vfs_unlock(mp); +- if (mp->mnt_vnodelist.lh_first != NULL) +- panic("unmount: dangling vnode"); +- free((caddr_t)mp, M_MOUNT); +- } +- return (error); ++ mp->mnt_flag &=~ MNT_ASYNC; ++ cache_purgevfs(mp); /* remove cache entries for this file sys */ ++ if (mp->mnt_syncer != NULL) { ++ hadsyncer = 1; ++ vgone(mp->mnt_syncer); ++ mp->mnt_syncer = NULL; ++ } ++ if (((mp->mnt_flag & MNT_RDONLY) || ++ (error = VFS_SYNC(mp, MNT_WAIT, p->p_ucred, p)) == 0) || ++ (flags & MNT_FORCE)) ++ error = VFS_UNMOUNT(mp, flags, p); ++ simple_lock(&mountlist_slock); ++ if (error) { ++ if ((mp->mnt_flag & MNT_RDONLY) == 0 && hadsyncer) ++ (void) vfs_allocate_syncvnode(mp); ++ lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, ++ &mountlist_slock, p); ++ return (error); ++ } ++ TAILQ_REMOVE(&mountlist, mp, mnt_list); ++ if ((coveredvp = mp->mnt_vnodecovered) != NULLVP) { ++ if (olddp) { ++ /* ++ * Try to put processes back in a real directory ++ * after a forced unmount. ++ * XXX We're not holding a ref on olddp, which may ++ * change, so compare id numbers. ++ */ ++ LIST_FOREACH(p2, &allproc, p_list) { ++ struct filedesc *fdp = p2->p_fd; ++ if (fdp->fd_cdir && ++ fdp->fd_cdir->v_id == olddp->v_id) { ++ vrele(fdp->fd_cdir); ++ vref(coveredvp); ++ fdp->fd_cdir = coveredvp; ++ } ++ if (fdp->fd_rdir && ++ fdp->fd_rdir->v_id == olddp->v_id) { ++ vrele(fdp->fd_rdir); ++ vref(coveredvp); ++ fdp->fd_rdir = coveredvp; ++ } ++ } ++ } ++ coveredvp->v_mountedhere = NULL; ++ vrele(coveredvp); ++ } ++ mp->mnt_vfc->vfc_refcount--; ++ if (mp->mnt_vnodelist.lh_first != NULL) ++ panic("unmount: dangling vnode"); ++ lockmgr(&mp->mnt_lock, LK_RELEASE | LK_INTERLOCK, &mountlist_slock, p); ++ free((caddr_t)mp, M_MOUNT); ++ return (0); + } + + /* +@@ -407,6 +500,7 @@ + #endif + + /* ARGSUSED */ ++int + sys_sync(p, v, retval) + struct proc *p; + void *v; +@@ -415,31 +509,23 @@ + register struct mount *mp, *nmp; + int asyncflag; + +- for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) { +- /* +- * Get the next pointer in case we hang on vfs_busy +- * while we are being unmounted. +- */ +- nmp = mp->mnt_list.cqe_next; +- /* +- * The lock check below is to avoid races with mount +- * and unmount. +- */ +- if ((mp->mnt_flag & (MNT_MLOCK|MNT_RDONLY|MNT_MPBUSY)) == 0 && +- !vfs_busy(mp)) { ++ simple_lock(&mountlist_slock); ++ TAILQ_FOREACH_REVERSE_SAFE(mp, &mountlist, mnt_list, nmp) { ++ if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) ++ continue; ++ if ((mp->mnt_flag & MNT_RDONLY) == 0) { + asyncflag = mp->mnt_flag & MNT_ASYNC; + mp->mnt_flag &= ~MNT_ASYNC; ++ uvm_vnp_sync(mp); + VFS_SYNC(mp, MNT_NOWAIT, p->p_ucred, p); + if (asyncflag) + mp->mnt_flag |= MNT_ASYNC; +- /* +- * Get the next pointer again, as the next filesystem +- * might have been unmounted while we were sync'ing. +- */ +- nmp = mp->mnt_list.cqe_next; +- vfs_unbusy(mp); + } ++ simple_lock(&mountlist_slock); ++ vfs_unbusy(mp, p); + } ++ simple_unlock(&mountlist_slock); ++ + #ifdef DEBUG + if (syncprt) + vfs_bufstats(); +@@ -451,6 +537,7 @@ + * Change filesystem quotas. + */ + /* ARGSUSED */ ++int + sys_quotactl(p, v, retval) + struct proc *p; + void *v; +@@ -467,7 +554,7 @@ + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + return (error); + mp = nd.ni_vp->v_mount; + vrele(nd.ni_vp); +@@ -479,6 +566,7 @@ + * Get filesystem statistics. + */ + /* ARGSUSED */ ++int + sys_statfs(p, v, retval) + struct proc *p; + void *v; +@@ -492,16 +580,27 @@ + register struct statfs *sp; + int error; + struct nameidata nd; ++ struct statfs sb; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + return (error); + mp = nd.ni_vp->v_mount; + sp = &mp->mnt_stat; + vrele(nd.ni_vp); +- if (error = VFS_STATFS(mp, sp, p)) ++ if ((error = VFS_STATFS(mp, sp, p)) != 0) + return (error); + sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; ++#if notyet ++ if (mp->mnt_flag & MNT_SOFTDEP) ++ sp->f_eflags = STATFS_SOFTUPD; ++#endif ++ /* Don't let non-root see filesystem id (for NFS security) */ ++ if (suser(p->p_ucred, &p->p_acflag)) { ++ bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); ++ sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; ++ sp = &sb; ++ } + return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); + } + +@@ -509,33 +608,48 @@ + * Get filesystem statistics. + */ + /* ARGSUSED */ ++int + sys_fstatfs(p, v, retval) + struct proc *p; + void *v; + register_t *retval; + { +- register struct sys_fstatfs_args /* { ++ struct sys_fstatfs_args /* { + syscallarg(int) fd; + syscallarg(struct statfs *) buf; + } */ *uap = v; + struct file *fp; + struct mount *mp; +- register struct statfs *sp; ++ struct statfs *sp; + int error; ++ struct statfs sb; + +- if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) ++ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + mp = ((struct vnode *)fp->f_data)->v_mount; + sp = &mp->mnt_stat; +- if (error = VFS_STATFS(mp, sp, p)) ++ error = VFS_STATFS(mp, sp, p); ++ FRELE(fp); ++ if (error) + return (error); + sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; ++#if notyet ++ if (mp->mnt_flag & MNT_SOFTDEP) ++ sp->f_eflags = STATFS_SOFTUPD; ++#endif ++ /* Don't let non-root see filesystem id (for NFS security) */ ++ if (suser(p->p_ucred, &p->p_acflag)) { ++ bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); ++ sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; ++ sp = &sb; ++ } + return (copyout((caddr_t)sp, (caddr_t)SCARG(uap, buf), sizeof(*sp))); + } + + /* + * Get statistics on all filesystems. + */ ++int + sys_getfsstat(p, v, retval) + struct proc *p; + void *v; +@@ -543,37 +657,59 @@ + { + register struct sys_getfsstat_args /* { + syscallarg(struct statfs *) buf; +- syscallarg(long) bufsize; ++ syscallarg(size_t) bufsize; + syscallarg(int) flags; + } */ *uap = v; +- register struct mount *mp, *nmp; ++ register struct mount *mp *nmp; + register struct statfs *sp; ++ struct statfs sb; + caddr_t sfsp; +- long count, maxcount, error; ++ size_t count, maxcount; ++ int error, flags = SCARG(uap, flags); + + maxcount = SCARG(uap, bufsize) / sizeof(struct statfs); + sfsp = (caddr_t)SCARG(uap, buf); +- for (count = 0, +- mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) { +- nmp = mp->mnt_list.cqe_next; +- if (sfsp && count < maxcount && +- ((mp->mnt_flag & MNT_MLOCK) == 0)) { ++ count = 0; ++ simple_lock(&mountlist_slock); ++ TAILQ_FOREACH_REVERSE_SAFE(mp, &mountlist, mnt_list, nmp) { ++ if (vfs_busy(mp, LK_NOWAIT, &mountlist_slock, p)) ++ continue; ++ if (sfsp && count < maxcount) { + sp = &mp->mnt_stat; +- /* +- * If MNT_NOWAIT is specified, do not refresh the +- * fsstat cache. MNT_WAIT overrides MNT_NOWAIT. +- */ +- if (((SCARG(uap, flags) & MNT_NOWAIT) == 0 || +- (SCARG(uap, flags) & MNT_WAIT)) && +- (error = VFS_STATFS(mp, sp, p))) +- continue; ++ ++ /* Refresh stats unless MNT_NOWAIT is specified */ ++ if (flags != MNT_NOWAIT && ++ flags != MNT_LAZY && ++ (flags == MNT_WAIT || ++ flags == 0) && ++ (error = VFS_STATFS(mp, sp, p))) { ++ simple_lock(&mountlist_slock); ++ vfs_unbusy(mp, p); ++ continue; ++ } ++ + sp->f_flags = mp->mnt_flag & MNT_VISFLAGMASK; +- if (error = copyout((caddr_t)sp, sfsp, sizeof(*sp))) ++#if notyet ++ if (mp->mnt_flag & MNT_SOFTDEP) ++ sp->f_eflags = STATFS_SOFTUPD; ++#endif ++ if (suser(p->p_ucred, &p->p_acflag)) { ++ bcopy((caddr_t)sp, (caddr_t)&sb, sizeof(sb)); ++ sb.f_fsid.val[0] = sb.f_fsid.val[1] = 0; ++ sp = &sb; ++ } ++ error = copyout((caddr_t)sp, sfsp, sizeof(*sp)); ++ if (error) { ++ vfs_unbusy(mp, p); + return (error); ++ } + sfsp += sizeof(*sp); + } + count++; ++ simple_lock(&mountlist_slock); ++ vfs_unbusy(mp, p); + } ++ simple_unlock(&mountlist_slock); + if (sfsp && count > maxcount) + *retval = maxcount; + else +@@ -585,6 +721,7 @@ + * Change current working directory to a given file descriptor. + */ + /* ARGSUSED */ ++int + sys_fchdir(p, v, retval) + struct proc *p; + void *v; +@@ -593,37 +730,38 @@ + struct sys_fchdir_args /* { + syscallarg(int) fd; + } */ *uap = v; +- register struct filedesc *fdp = p->p_fd; ++ struct filedesc *fdp = p->p_fd; + struct vnode *vp, *tdp; + struct mount *mp; + struct file *fp; + int error; + +- if (error = getvnode(fdp, SCARG(uap, fd), &fp)) ++ if ((error = getvnode(fdp, SCARG(uap, fd), &fp)) != 0) + return (error); + vp = (struct vnode *)fp->f_data; + VREF(vp); +- VOP_LOCK(vp); ++ FRELE(fp); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_type != VDIR) + error = ENOTDIR; + else + error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); ++ + while (!error && (mp = vp->v_mountedhere) != NULL) { +- if (mp->mnt_flag & MNT_MLOCK) { +- mp->mnt_flag |= MNT_MWAIT; +- sleep((caddr_t)mp, PVFS); ++ if (vfs_busy(mp, 0, 0, p)) + continue; +- } +- if (error = VFS_ROOT(mp, &tdp)) ++ error = VFS_ROOT(mp, &tdp); ++ vfs_unbusy(mp, p); ++ if (error) + break; + vput(vp); + vp = tdp; + } +- VOP_UNLOCK(vp); + if (error) { +- vrele(vp); ++ vput(vp); + return (error); + } ++ VOP_UNLOCK(vp, 0, p); + vrele(fdp->fd_cdir); + fdp->fd_cdir = vp; + return (0); +@@ -633,6 +771,7 @@ + * Change current working directory (``.''). + */ + /* ARGSUSED */ ++int + sys_chdir(p, v, retval) + struct proc *p; + void *v; +@@ -647,7 +786,7 @@ + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); +- if (error = change_dir(&nd, p)) ++ if ((error = change_dir(&nd, p)) != 0) + return (error); + vrele(fdp->fd_cdir); + fdp->fd_cdir = nd.ni_vp; +@@ -658,6 +797,7 @@ + * Change notion of root (``/'') directory. + */ + /* ARGSUSED */ ++int + sys_chroot(p, v, retval) + struct proc *p; + void *v; +@@ -670,14 +810,22 @@ + int error; + struct nameidata nd; + +- if (error = suser(p->p_ucred, &p->p_acflag)) ++ if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) + return (error); + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); +- if (error = change_dir(&nd, p)) ++ if ((error = change_dir(&nd, p)) != 0) + return (error); +- if (fdp->fd_rdir != NULL) ++ if (fdp->fd_rdir != NULL) { ++ /* ++ * A chroot() done inside a changed root environment does ++ * an automatic chdir to avoid the out-of-tree experience. ++ */ + vrele(fdp->fd_rdir); ++ vrele(fdp->fd_cdir); ++ VREF(nd.ni_vp); ++ fdp->fd_cdir = nd.ni_vp; ++ } + fdp->fd_rdir = nd.ni_vp; + return (0); + } +@@ -693,16 +841,17 @@ + struct vnode *vp; + int error; + +- if (error = namei(ndp)) ++ if ((error = namei(ndp)) != 0) + return (error); + vp = ndp->ni_vp; + if (vp->v_type != VDIR) + error = ENOTDIR; + else + error = VOP_ACCESS(vp, VEXEC, p->p_ucred, p); +- VOP_UNLOCK(vp); + if (error) +- vrele(vp); ++ vput(vp); ++ else ++ VOP_UNLOCK(vp, 0, p); + return (error); + } + +@@ -710,45 +859,50 @@ + * Check permissions, allocate an open file structure, + * and call the device open routine if any. + */ ++int + sys_open(p, v, retval) + struct proc *p; + void *v; + register_t *retval; + { +- register struct sys_open_args /* { ++ struct sys_open_args /* { + syscallarg(char *) path; + syscallarg(int) flags; + syscallarg(int) mode; + } */ *uap = v; +- register struct filedesc *fdp = p->p_fd; +- register struct file *fp; +- register struct vnode *vp; ++ struct filedesc *fdp = p->p_fd; ++ struct file *fp; ++ struct vnode *vp; ++ struct vattr vattr; + int flags, cmode; +- struct file *nfp; +- int type, indx, error; ++ int type, indx, error, localtrunc = 0; + struct flock lf; + struct nameidata nd; +- extern struct fileops vnops; + +- if (error = falloc(p, &nfp, &indx)) ++ if ((error = falloc(p, &fp, &indx)) != 0) + return (error); +- fp = nfp; ++ + flags = FFLAGS(SCARG(uap, flags)); + cmode = ((SCARG(uap, mode) &~ fdp->fd_cmask) & ALLPERMS) &~ S_ISTXT; + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); + p->p_dupfd = -indx - 1; /* XXX check for fdopen */ +- if (error = vn_open(&nd, flags, cmode)) { +- ffree(fp); ++ if ((flags & O_TRUNC) && (flags & (O_EXLOCK | O_SHLOCK))) { ++ localtrunc = 1; ++ flags &= ~O_TRUNC; /* Must do truncate ourselves */ ++ } ++ if ((error = vn_open(&nd, flags, cmode)) != 0) { + if ((error == ENODEV || error == ENXIO) && + p->p_dupfd >= 0 && /* XXX from fdopen */ + (error = + dupfdopen(fdp, indx, p->p_dupfd, flags, error)) == 0) { ++ closef(fp, p); + *retval = indx; + return (0); + } + if (error == ERESTART) + error = EINTR; +- fdp->fd_ofiles[indx] = NULL; ++ fdremove(fdp, indx); ++ closef(fp, p); + return (error); + } + p->p_dupfd = 0; +@@ -768,189 +922,463 @@ + type = F_FLOCK; + if ((flags & FNONBLOCK) == 0) + type |= F_WAIT; +- VOP_UNLOCK(vp); +- if (error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type)) { +- (void) vn_close(vp, fp->f_flag, fp->f_cred, p); +- ffree(fp); +- fdp->fd_ofiles[indx] = NULL; ++ VOP_UNLOCK(vp, 0, p); ++ error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type); ++ if (error) { ++ /* closef will vn_close the file for us. */ ++ fdremove(fdp, indx); ++ closef(fp, p); + return (error); + } +- VOP_LOCK(vp); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + fp->f_flag |= FHASLOCK; + } +- VOP_UNLOCK(vp); ++ if (localtrunc) { ++ VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); ++ if ((fp->f_flag & FWRITE) == 0) ++ error = EACCES; ++ else if (vp->v_mount->mnt_flag & MNT_RDONLY) ++ error = EROFS; ++ else if (vp->v_type == VDIR) ++ error = EISDIR; ++ else if ((error = vn_writechk(vp)) == 0) { ++ VATTR_NULL(&vattr); ++ vattr.va_size = 0; ++ error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); ++ } ++ if (error) { ++ VOP_UNLOCK(vp, 0, p); ++ /* closef will close the file for us. */ ++ fdremove(fdp, indx); ++ closef(fp, p); ++ return (error); ++ } ++ } ++ VOP_UNLOCK(vp, 0, p); + *retval = indx; ++ FILE_SET_MATURE(fp); + return (0); + } + + /* +- * Create a special file. ++ * Get file handle system call + */ +-/* ARGSUSED */ +-sys_mknod(p, v, retval) ++int ++sys_getfh(p, v, retval) + struct proc *p; +- void *v; ++ register void *v; + register_t *retval; + { +- register struct sys_mknod_args /* { +- syscallarg(char *) path; +- syscallarg(int) mode; +- syscallarg(int) dev; ++ register struct sys_getfh_args /* { ++ syscallarg(char *) fname; ++ syscallarg(fhandle_t *) fhp; + } */ *uap = v; + register struct vnode *vp; +- struct vattr vattr; ++ fhandle_t fh; + int error; +- int whiteout; + struct nameidata nd; + +- if (error = suser(p->p_ucred, &p->p_acflag)) ++ /* ++ * Must be super user ++ */ ++ error = suser(p->p_ucred, &p->p_acflag); ++ if (error) + return (error); +- NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); +- if (error = namei(&nd)) ++ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, ++ SCARG(uap, fname), p); ++ error = namei(&nd); ++ if (error) + return (error); + vp = nd.ni_vp; +- if (vp != NULL) +- error = EEXIST; +- else { +- VATTR_NULL(&vattr); +- vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; +- vattr.va_rdev = SCARG(uap, dev); +- whiteout = 0; +- +- switch (SCARG(uap, mode) & S_IFMT) { +- case S_IFMT: /* used by badsect to flag bad sectors */ +- vattr.va_type = VBAD; +- break; +- case S_IFCHR: +- vattr.va_type = VCHR; +- break; +- case S_IFBLK: +- vattr.va_type = VBLK; +- break; +- case S_IFWHT: +- whiteout = 1; +- break; +- default: +- error = EINVAL; +- break; +- } +- } +- if (!error) { +- VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); +- if (whiteout) { +- error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); +- if (error) +- VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); +- vput(nd.ni_dvp); +- } else { +- error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, +- &nd.ni_cnd, &vattr); +- } +- } else { +- VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); +- if (nd.ni_dvp == vp) +- vrele(nd.ni_dvp); +- else +- vput(nd.ni_dvp); +- if (vp) +- vrele(vp); +- } ++ bzero((caddr_t)&fh, sizeof(fh)); ++ fh.fh_fsid = vp->v_mount->mnt_stat.f_fsid; ++ error = VFS_VPTOFH(vp, &fh.fh_fid); ++ vput(vp); ++ if (error) ++ return (error); ++ error = copyout((caddr_t)&fh, (caddr_t)SCARG(uap, fhp), sizeof (fh)); + return (error); + } + + /* +- * Create a named pipe. ++ * Open a file given a file handle. ++ * ++ * Check permissions, allocate an open file structure, ++ * and call the device open routine if any. + */ +-/* ARGSUSED */ +-sys_mkfifo(p, v, retval) ++int ++sys_fhopen(p, v, retval) + struct proc *p; + void *v; + register_t *retval; + { +- register struct sys_mkfifo_args /* { +- syscallarg(char *) path; +- syscallarg(int) mode; ++ register struct sys_fhopen_args /* { ++ syscallarg(const fhandle_t *) fhp; ++ syscallarg(int) flags; + } */ *uap = v; +- struct vattr vattr; +- int error; +- struct nameidata nd; ++ struct filedesc *fdp = p->p_fd; ++ struct file *fp; ++ struct vnode *vp = NULL; ++ struct mount *mp; ++ struct ucred *cred = p->p_ucred; ++ int flags; ++ int type, indx, error=0; ++ struct flock lf; ++ struct vattr va; ++ fhandle_t fh; + +-#ifndef FIFO +- return (EOPNOTSUPP); +-#else +- NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); +- if (error = namei(&nd)) ++ /* ++ * Must be super user ++ */ ++ if ((error = suser(p->p_ucred, &p->p_acflag))) + return (error); +- if (nd.ni_vp != NULL) { +- VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); +- if (nd.ni_dvp == nd.ni_vp) +- vrele(nd.ni_dvp); +- else +- vput(nd.ni_dvp); +- vrele(nd.ni_vp); +- return (EEXIST); +- } +- VATTR_NULL(&vattr); +- vattr.va_type = VFIFO; +- vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; +- VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); +- return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr)); +-#endif /* FIFO */ +-} + +-/* +- * Make a hard file link. +- */ +-/* ARGSUSED */ +-sys_link(p, v, retval) +- struct proc *p; +- void *v; +- register_t *retval; +-{ +- register struct sys_link_args /* { +- syscallarg(char *) path; +- syscallarg(char *) link; +- } */ *uap = v; +- register struct vnode *vp; +- struct nameidata nd; +- int error; ++ flags = FFLAGS(SCARG(uap, flags)); ++ if ((flags & (FREAD | FWRITE)) == 0) ++ return (EINVAL); ++ if ((flags & O_CREAT)) ++ return (EINVAL); + +- NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = falloc(p, &fp, &indx)) != 0) + return (error); +- vp = nd.ni_vp; +- if (vp->v_type != VDIR || +- (error = suser(p->p_ucred, &p->p_acflag)) == 0) { +- nd.ni_cnd.cn_nameiop = CREATE; +- nd.ni_cnd.cn_flags = LOCKPARENT; +- nd.ni_dirp = SCARG(uap, link); +- if ((error = namei(&nd)) == 0) { +- if (nd.ni_vp != NULL) +- error = EEXIST; +- if (!error) { +- VOP_LEASE(nd.ni_dvp, p, p->p_ucred, +- LEASE_WRITE); +- VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); +- error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); +- } else { +- VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); +- if (nd.ni_dvp == nd.ni_vp) +- vrele(nd.ni_dvp); +- else +- vput(nd.ni_dvp); +- if (nd.ni_vp) +- vrele(nd.ni_vp); +- } +- } ++ ++ if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fhandle_t))) != 0) ++ goto bad; ++ ++ if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) { ++ error = ESTALE; ++ goto bad; + } +- vrele(vp); +- return (error); +-} ++ ++ if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp)) != 0) { ++ vp = NULL; /* most likely unnecessary sanity for bad: */ ++ goto bad; ++ } ++ ++ /* Now do an effective vn_open */ ++ ++ if (vp->v_type == VSOCK) { ++ error = EOPNOTSUPP; ++ goto bad; ++ } ++ if (flags & FREAD) { ++ if ((error = VOP_ACCESS(vp, VREAD, cred, p)) != 0) ++ goto bad; ++ } ++ if (flags & (FWRITE | O_TRUNC)) { ++ if (vp->v_type == VDIR) { ++ error = EISDIR; ++ goto bad; ++ } ++ if ((error = vn_writechk(vp)) != 0 || ++ (error = VOP_ACCESS(vp, VWRITE, cred, p)) != 0) ++ goto bad; ++ } ++ if (flags & O_TRUNC) { ++ VOP_UNLOCK(vp, 0, p); /* XXX */ ++ VOP_LEASE(vp, p, cred, LEASE_WRITE); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); /* XXX */ ++ VATTR_NULL(&va); ++ va.va_size = 0; ++ if ((error = VOP_SETATTR(vp, &va, cred, p)) != 0) ++ goto bad; ++ } ++ if ((error = VOP_OPEN(vp, flags, cred, p)) != 0) ++ goto bad; ++ if (flags & FWRITE) ++ vp->v_writecount++; ++ ++ /* done with modified vn_open, now finish what sys_open does. */ ++ ++ fp->f_flag = flags & FMASK; ++ fp->f_type = DTYPE_VNODE; ++ fp->f_ops = &vnops; ++ fp->f_data = (caddr_t)vp; ++ if (flags & (O_EXLOCK | O_SHLOCK)) { ++ lf.l_whence = SEEK_SET; ++ lf.l_start = 0; ++ lf.l_len = 0; ++ if (flags & O_EXLOCK) ++ lf.l_type = F_WRLCK; ++ else ++ lf.l_type = F_RDLCK; ++ type = F_FLOCK; ++ if ((flags & FNONBLOCK) == 0) ++ type |= F_WAIT; ++ VOP_UNLOCK(vp, 0, p); ++ error = VOP_ADVLOCK(vp, (caddr_t)fp, F_SETLK, &lf, type); ++ if (error) { ++ /* closef will vn_close the file for us. */ ++ fdremove(fdp, indx); ++ closef(fp, p); ++ return (error); ++ } ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); ++ fp->f_flag |= FHASLOCK; ++ } ++ VOP_UNLOCK(vp, 0, p); ++ *retval = indx; ++ FILE_SET_MATURE(fp); ++ return (0); ++ ++bad: ++ fdremove(fdp, indx); ++ closef(fp, p); ++ if (vp != NULL) ++ vput(vp); ++ return (error); ++} ++ ++/* ARGSUSED */ ++int ++sys_fhstat(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ register struct sys_fhstat_args /* { ++ syscallarg(const fhandle_t *) fhp; ++ syscallarg(struct stat *) sb; ++ } */ *uap = v; ++ struct stat sb; ++ int error; ++ fhandle_t fh; ++ struct mount *mp; ++ struct vnode *vp; ++ ++ /* ++ * Must be super user ++ */ ++ if ((error = suser(p->p_ucred, &p->p_acflag))) ++ return (error); ++ ++ if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fhandle_t))) != 0) ++ return (error); ++ ++ if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) ++ return (ESTALE); ++ if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) ++ return (error); ++ error = vn_stat(vp, &sb, p); ++ vput(vp); ++ if (error) ++ return (error); ++ error = copyout(&sb, SCARG(uap, sb), sizeof(sb)); ++ return (error); ++} ++ ++/* ARGSUSED */ ++int ++sys_fhstatfs(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ register struct sys_fhstatfs_args /* ++ syscallarg(const fhandle_t *) fhp; ++ syscallarg(struct statfs *) buf; ++ } */ *uap = v; ++ struct statfs sp; ++ fhandle_t fh; ++ struct mount *mp; ++ struct vnode *vp; ++ int error; ++ ++ /* ++ * Must be super user ++ */ ++ if ((error = suser(p->p_ucred, &p->p_acflag))) ++ return (error); ++ ++ if ((error = copyin(SCARG(uap, fhp), &fh, sizeof(fhandle_t))) != 0) ++ return (error); ++ ++ if ((mp = vfs_getvfs(&fh.fh_fsid)) == NULL) ++ return (ESTALE); ++ if ((error = VFS_FHTOVP(mp, &fh.fh_fid, &vp))) ++ return (error); ++ mp = vp->v_mount; ++ vput(vp); ++ if ((error = VFS_STATFS(mp, &sp, p)) != 0) ++ return (error); ++ sp.f_flags = mp->mnt_flag & MNT_VISFLAGMASK; ++ return (copyout(&sp, SCARG(uap, buf), sizeof(sp))); ++} ++ ++/* ++ * Create a special file. ++ */ ++/* ARGSUSED */ ++int ++sys_mknod(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ register struct sys_mknod_args /* { ++ syscallarg(char *) path; ++ syscallarg(int) mode; ++ syscallarg(int) dev; ++ } */ *uap = v; ++ register struct vnode *vp; ++ struct vattr vattr; ++ int error; ++ int whiteout = 0; ++ struct nameidata nd; ++ ++ if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) ++ return (error); ++ if (p->p_fd->fd_rdir) ++ return (EINVAL); ++ NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); ++ if ((error = namei(&nd)) != 0) ++ return (error); ++ vp = nd.ni_vp; ++ if (vp != NULL) ++ error = EEXIST; ++ else { ++ VATTR_NULL(&vattr); ++ vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; ++ vattr.va_rdev = SCARG(uap, dev); ++ whiteout = 0; ++ ++ switch (SCARG(uap, mode) & S_IFMT) { ++ case S_IFMT: /* used by badsect to flag bad sectors */ ++ vattr.va_type = VBAD; ++ break; ++ case S_IFCHR: ++ vattr.va_type = VCHR; ++ break; ++ case S_IFBLK: ++ vattr.va_type = VBLK; ++ break; ++ case S_IFWHT: ++ whiteout = 1; ++ break; ++ default: ++ error = EINVAL; ++ break; ++ } ++ } ++ if (!error) { ++ VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); ++ if (whiteout) { ++ error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, CREATE); ++ if (error) ++ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); ++ vput(nd.ni_dvp); ++ } else { ++ error = VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, ++ &nd.ni_cnd, &vattr); ++ } ++ } else { ++ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); ++ if (nd.ni_dvp == vp) ++ vrele(nd.ni_dvp); ++ else ++ vput(nd.ni_dvp); ++ if (vp) ++ vrele(vp); ++ } ++ return (error); ++} ++ ++/* ++ * Create a named pipe. ++ */ ++/* ARGSUSED */ ++int ++sys_mkfifo(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++#ifndef FIFO ++ return (EOPNOTSUPP); ++#else ++ register struct sys_mkfifo_args /* { ++ syscallarg(char *) path; ++ syscallarg(int) mode; ++ } */ *uap = v; ++ struct vattr vattr; ++ int error; ++ struct nameidata nd; ++ ++ NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); ++ if ((error = namei(&nd)) != 0) ++ return (error); ++ if (nd.ni_vp != NULL) { ++ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); ++ if (nd.ni_dvp == nd.ni_vp) ++ vrele(nd.ni_dvp); ++ else ++ vput(nd.ni_dvp); ++ vrele(nd.ni_vp); ++ return (EEXIST); ++ } ++ VATTR_NULL(&vattr); ++ vattr.va_type = VFIFO; ++ vattr.va_mode = (SCARG(uap, mode) & ALLPERMS) &~ p->p_fd->fd_cmask; ++ VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); ++ return (VOP_MKNOD(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr)); ++#endif /* FIFO */ ++} ++ ++/* ++ * Make a hard file link. ++ */ ++/* ARGSUSED */ ++int ++sys_link(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ register struct sys_link_args /* { ++ syscallarg(char *) path; ++ syscallarg(char *) link; ++ } */ *uap = v; ++ register struct vnode *vp; ++ struct nameidata nd; ++ int error; ++ int flags; ++ ++ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); ++ if ((error = namei(&nd)) != 0) ++ return (error); ++ vp = nd.ni_vp; ++ ++ flags = LOCKPARENT; ++ if (vp->v_type == VDIR) { ++ flags |= STRIPSLASHES; ++ } ++ ++ NDINIT(&nd, CREATE, flags, UIO_USERSPACE, SCARG(uap, link), p); ++ if ((error = namei(&nd)) != 0) ++ goto out; ++ if (nd.ni_vp) { ++ VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); ++ if (nd.ni_dvp == nd.ni_vp) ++ vrele(nd.ni_dvp); ++ else ++ vput(nd.ni_dvp); ++ vrele(nd.ni_vp); ++ error = EEXIST; ++ goto out; ++ } ++ VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); ++ VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); ++ error = VOP_LINK(nd.ni_dvp, vp, &nd.ni_cnd); ++out: ++ vrele(vp); ++ return (error); ++} + + /* + * Make a symbolic link. + */ + /* ARGSUSED */ ++int + sys_symlink(p, v, retval) + struct proc *p; + void *v; +@@ -966,10 +1394,11 @@ + struct nameidata nd; + + MALLOC(path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK); +- if (error = copyinstr(SCARG(uap, path), path, MAXPATHLEN, (size_t *)0)) ++ error = copyinstr(SCARG(uap, path), path, MAXPATHLEN, NULL); ++ if (error) + goto out; + NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, link), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + goto out; + if (nd.ni_vp) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); +@@ -994,6 +1423,7 @@ + * Delete a whiteout from the filesystem. + */ + /* ARGSUSED */ ++int + sys_undelete(p, v, retval) + struct proc *p; + void *v; +@@ -1023,7 +1453,7 @@ + } + + VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); +- if (error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE)) ++ if ((error = VOP_WHITEOUT(nd.ni_dvp, &nd.ni_cnd, DELETE)) != 0) + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + vput(nd.ni_dvp); + return (error); +@@ -1033,6 +1463,7 @@ + * Delete a name from the filesystem. + */ + /* ARGSUSED */ ++int + sys_unlink(p, v, retval) + struct proc *p; + void *v; +@@ -1045,42 +1476,39 @@ + int error; + struct nameidata nd; + +- NDINIT(&nd, DELETE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); +- if (error = namei(&nd)) ++ NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, ++ SCARG(uap, path), p); ++ if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; +- VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); +- VOP_LOCK(vp); +- +- if (vp->v_type != VDIR || +- (error = suser(p->p_ucred, &p->p_acflag)) == 0) { +- /* +- * The root of a mounted filesystem cannot be deleted. +- */ +- if (vp->v_flag & VROOT) +- error = EBUSY; +- else +- (void)vnode_pager_uncache(vp); +- } + +- if (!error) { +- VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); +- error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); +- } else { ++ /* ++ * The root of a mounted filesystem cannot be deleted. ++ */ ++ if (vp->v_flag & VROOT) { + VOP_ABORTOP(nd.ni_dvp, &nd.ni_cnd); + if (nd.ni_dvp == vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); +- if (vp != NULLVP) +- vput(vp); ++ vput(vp); ++ error = EBUSY; ++ goto out; + } ++ ++ (void)uvm_vnp_uncache(vp); ++ ++ VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE); ++ VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); ++ error = VOP_REMOVE(nd.ni_dvp, nd.ni_vp, &nd.ni_cnd); ++out: + return (error); + } + + /* + * Reposition read/write file offset. + */ ++int + sys_lseek(p, v, retval) + struct proc *p; + void *v; +@@ -1096,24 +1524,38 @@ + register struct filedesc *fdp = p->p_fd; + register struct file *fp; + struct vattr vattr; +- int error; ++ struct vnode *vp; ++ int error, special; + +- if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || +- (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) ++ if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL) + return (EBADF); + if (fp->f_type != DTYPE_VNODE) + return (ESPIPE); ++ vp = (struct vnode *)fp->f_data; ++ if (vp->v_type == VFIFO) ++ return (ESPIPE); ++ if (vp->v_type == VCHR) ++ special = 1; ++ else ++ special = 0; + switch (SCARG(uap, whence)) { +- case L_INCR: ++ case SEEK_CUR: ++ if (!special && fp->f_offset + SCARG(uap, offset) < 0) ++ return (EINVAL); + fp->f_offset += SCARG(uap, offset); + break; +- case L_XTND: +- if (error = +- VOP_GETATTR((struct vnode *)fp->f_data, &vattr, cred, p)) ++ case SEEK_END: ++ error = VOP_GETATTR((struct vnode *)fp->f_data, &vattr, ++ cred, p); ++ if (error) + return (error); ++ if (!special && (off_t)vattr.va_size + SCARG(uap, offset) < 0) ++ return (EINVAL); + fp->f_offset = SCARG(uap, offset) + vattr.va_size; + break; +- case L_SET: ++ case SEEK_SET: ++ if (!special && SCARG(uap, offset) < 0) ++ return (EINVAL); + fp->f_offset = SCARG(uap, offset); + break; + default: +@@ -1126,6 +1568,7 @@ + /* + * Check access permissions. + */ ++int + sys_access(p, v, retval) + struct proc *p; + void *v; +@@ -1140,13 +1583,15 @@ + int error, flags, t_gid, t_uid; + struct nameidata nd; + ++ if (SCARG(uap, flags) & ~(R_OK | W_OK | X_OK)) ++ return (EINVAL); + t_uid = cred->cr_uid; + t_gid = cred->cr_gid; + cred->cr_uid = p->p_cred->p_ruid; + cred->cr_gid = p->p_cred->p_rgid; + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + goto out1; + vp = nd.ni_vp; + +@@ -1173,6 +1618,7 @@ + * Get file status; this version follows links. + */ + /* ARGSUSED */ ++int + sys_stat(p, v, retval) + struct proc *p; + void *v; +@@ -1188,12 +1634,15 @@ + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + return (error); + error = vn_stat(nd.ni_vp, &sb, p); + vput(nd.ni_vp); + if (error) + return (error); ++ /* Don't let non-root see generation numbers (for NFS security) */ ++ if (suser(p->p_ucred, &p->p_acflag)) ++ sb.st_gen = 0; + error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); + return (error); + } +@@ -1202,6 +1651,7 @@ + * Get file status; this version does not follow links. + */ + /* ARGSUSED */ ++int + sys_lstat(p, v, retval) + struct proc *p; + void *v; +@@ -1211,47 +1661,21 @@ + syscallarg(char *) path; + syscallarg(struct stat *) ub; + } */ *uap = v; ++ struct stat sb; + int error; +- struct vnode *vp, *dvp; +- struct stat sb, sb1; + struct nameidata nd; + +- NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | LOCKPARENT, UIO_USERSPACE, ++ NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + return (error); +- /* +- * For symbolic links, always return the attributes of its +- * containing directory, except for mode, size, and links. +- */ +- vp = nd.ni_vp; +- dvp = nd.ni_dvp; +- if (vp->v_type != VLNK) { +- if (dvp == vp) +- vrele(dvp); +- else +- vput(dvp); +- error = vn_stat(vp, &sb, p); +- vput(vp); +- if (error) +- return (error); +- } else { +- error = vn_stat(dvp, &sb, p); +- vput(dvp); +- if (error) { +- vput(vp); +- return (error); +- } +- error = vn_stat(vp, &sb1, p); +- vput(vp); +- if (error) +- return (error); +- sb.st_mode &= ~S_IFDIR; +- sb.st_mode |= S_IFLNK; +- sb.st_nlink = sb1.st_nlink; +- sb.st_size = sb1.st_size; +- sb.st_blocks = sb1.st_blocks; +- } ++ error = vn_stat(nd.ni_vp, &sb, p); ++ vput(nd.ni_vp); ++ if (error) ++ return (error); ++ /* Don't let non-root see generation numbers (for NFS security) */ ++ if (suser(p->p_ucred, &p->p_acflag)) ++ sb.st_gen = 0; + error = copyout((caddr_t)&sb, (caddr_t)SCARG(uap, ub), sizeof (sb)); + return (error); + } +@@ -1260,6 +1684,7 @@ + * Get configurable pathname variables. + */ + /* ARGSUSED */ ++int + sys_pathconf(p, v, retval) + struct proc *p; + void *v; +@@ -1274,7 +1699,7 @@ + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + return (error); + error = VOP_PATHCONF(nd.ni_vp, SCARG(uap, name), retval); + vput(nd.ni_vp); +@@ -1285,6 +1710,7 @@ + * Return target name of a symbolic link. + */ + /* ARGSUSED */ ++int + sys_readlink(p, v, retval) + struct proc *p; + void *v; +@@ -1293,7 +1719,7 @@ + register struct sys_readlink_args /* { + syscallarg(char *) path; + syscallarg(char *) buf; +- syscallarg(int) count; ++ syscallarg(size_t) count; + } */ *uap = v; + register struct vnode *vp; + struct iovec aiov; +@@ -1303,7 +1729,7 @@ + + NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + if (vp->v_type != VLNK) +@@ -1329,6 +1755,7 @@ + * Change flags of a file given a path name. + */ + /* ARGSUSED */ ++int + sys_chflags(p, v, retval) + struct proc *p; + void *v; +@@ -1336,7 +1763,7 @@ + { + register struct sys_chflags_args /* { + syscallarg(char *) path; +- syscallarg(int) flags; ++ syscallarg(unsigned int) flags; + } */ *uap = v; + register struct vnode *vp; + struct vattr vattr; +@@ -1344,18 +1771,29 @@ + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); +- VOP_LOCK(vp); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; ++ else if (SCARG(uap, flags) == VNOVAL) ++ error = EINVAL; + else { ++ if (suser(p->p_ucred, &p->p_acflag)) { ++ if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) != 0) ++ goto out; ++ if (vattr.va_type == VCHR || vattr.va_type == VBLK) { ++ error = EINVAL; ++ goto out; ++ } ++ } + VATTR_NULL(&vattr); + vattr.va_flags = SCARG(uap, flags); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } ++out: + vput(vp); + return (error); + } +@@ -1364,33 +1802,47 @@ + * Change flags of a file given a file descriptor. + */ + /* ARGSUSED */ ++int + sys_fchflags(p, v, retval) + struct proc *p; + void *v; + register_t *retval; + { +- register struct sys_fchflags_args /* { ++ struct sys_fchflags_args /* { + syscallarg(int) fd; +- syscallarg(int) flags; ++ syscallarg(unsigned int) flags; + } */ *uap = v; + struct vattr vattr; + struct vnode *vp; + struct file *fp; + int error; + +- if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) ++ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); +- VOP_LOCK(vp); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; ++ else if (SCARG(uap, flags) == VNOVAL) ++ error = EINVAL; + else { ++ if (suser(p->p_ucred, &p->p_acflag)) { ++ if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) ++ != 0) ++ goto out; ++ if (vattr.va_type == VCHR || vattr.va_type == VBLK) { ++ error = EINVAL; ++ goto out; ++ } ++ } + VATTR_NULL(&vattr); + vattr.va_flags = SCARG(uap, flags); + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } +- VOP_UNLOCK(vp); ++out: ++ VOP_UNLOCK(vp, 0, p); ++ FRELE(fp); + return (error); + } + +@@ -1398,6 +1850,7 @@ + * Change mode of a file given path name. + */ + /* ARGSUSED */ ++int + sys_chmod(p, v, retval) + struct proc *p; + void *v; +@@ -1412,12 +1865,15 @@ + int error; + struct nameidata nd; + ++ if (SCARG(uap, mode) & ~(S_IFMT | ALLPERMS)) ++ return (EINVAL); ++ + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); +- VOP_LOCK(vp); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { +@@ -1433,12 +1889,13 @@ + * Change mode of a file given a file descriptor. + */ + /* ARGSUSED */ ++int + sys_fchmod(p, v, retval) + struct proc *p; + void *v; + register_t *retval; + { +- register struct sys_fchmod_args /* { ++ struct sys_fchmod_args /* { + syscallarg(int) fd; + syscallarg(int) mode; + } */ *uap = v; +@@ -1447,11 +1904,14 @@ + struct file *fp; + int error; + +- if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) ++ if (SCARG(uap, mode) & ~(S_IFMT | ALLPERMS)) ++ return (EINVAL); ++ ++ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); +- VOP_LOCK(vp); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { +@@ -1459,7 +1919,8 @@ + vattr.va_mode = SCARG(uap, mode) & ALLPERMS; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } +- VOP_UNLOCK(vp); ++ VOP_UNLOCK(vp, 0, p); ++ FRELE(fp); + return (error); + } + +@@ -1467,6 +1928,7 @@ + * Set ownership given a path name. + */ + /* ARGSUSED */ ++int + sys_chown(p, v, retval) + struct proc *p; + void *v; +@@ -1481,21 +1943,87 @@ + struct vattr vattr; + int error; + struct nameidata nd; ++ u_short mode; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) ++ return (error); ++ vp = nd.ni_vp; ++ VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); ++ if (vp->v_mount->mnt_flag & MNT_RDONLY) ++ error = EROFS; ++ else { ++ if ((SCARG(uap, uid) != -1 || SCARG(uap, gid) != -1) && ++ (suser(p->p_ucred, &p->p_acflag) || suid_clear)) { ++ error = VOP_GETATTR(vp, &vattr, p->p_ucred, p); ++ if (error) ++ goto out; ++ mode = vattr.va_mode & ~(VSUID | VSGID); ++ if (mode == vattr.va_mode) ++ mode = VNOVAL; ++ } ++ else ++ mode = VNOVAL; ++ VATTR_NULL(&vattr); ++ vattr.va_uid = SCARG(uap, uid); ++ vattr.va_gid = SCARG(uap, gid); ++ vattr.va_mode = mode; ++ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); ++ } ++out: ++ vput(vp); ++ return (error); ++} ++ ++/* ++ * Set ownership given a path name, without following links. ++ */ ++/* ARGSUSED */ ++int ++sys_lchown(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ register struct sys_lchown_args /* { ++ syscallarg(char *) path; ++ syscallarg(int) uid; ++ syscallarg(int) gid; ++ } */ *uap = v; ++ register struct vnode *vp; ++ struct vattr vattr; ++ int error; ++ struct nameidata nd; ++ u_short mode; ++ ++ NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_USERSPACE, SCARG(uap, path), p); ++ if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); +- VOP_LOCK(vp); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { ++ if ((SCARG(uap, uid) != -1 || SCARG(uap, gid) != -1) && ++ (suser(p->p_ucred, &p->p_acflag) || suid_clear)) { ++ error = VOP_GETATTR(vp, &vattr, p->p_ucred, p); ++ if (error) ++ goto out; ++ mode = vattr.va_mode & ~(VSUID | VSGID); ++ if (mode == vattr.va_mode) ++ mode = VNOVAL; ++ } ++ else ++ mode = VNOVAL; + VATTR_NULL(&vattr); + vattr.va_uid = SCARG(uap, uid); + vattr.va_gid = SCARG(uap, gid); ++ vattr.va_mode = mode; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } ++out: + vput(vp); + return (error); + } +@@ -1504,42 +2032,58 @@ + * Set ownership given a file descriptor. + */ + /* ARGSUSED */ ++int + sys_fchown(p, v, retval) + struct proc *p; + void *v; + register_t *retval; + { +- register struct sys_fchown_args /* { ++ struct sys_fchown_args /* { + syscallarg(int) fd; + syscallarg(int) uid; + syscallarg(int) gid; + } */ *uap = v; +- struct vattr vattr; + struct vnode *vp; +- struct file *fp; ++ struct vattr vattr; + int error; ++ struct file *fp; ++ u_short mode; + +- if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) ++ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); +- VOP_LOCK(vp); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { ++ if ((SCARG(uap, uid) != -1 || SCARG(uap, gid) != -1) && ++ (suser(p->p_ucred, &p->p_acflag) || suid_clear)) { ++ error = VOP_GETATTR(vp, &vattr, p->p_ucred, p); ++ if (error) ++ goto out; ++ mode = vattr.va_mode & ~(VSUID | VSGID); ++ if (mode == vattr.va_mode) ++ mode = VNOVAL; ++ } else ++ mode = VNOVAL; + VATTR_NULL(&vattr); + vattr.va_uid = SCARG(uap, uid); + vattr.va_gid = SCARG(uap, gid); ++ vattr.va_mode = mode; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } +- VOP_UNLOCK(vp); ++out: ++ VOP_UNLOCK(vp, 0, p); ++ FRELE(fp); + return (error); + } + + /* +- * Set the access and modification times of a file. ++ * Set the access and modification times given a path name. + */ + /* ARGSUSED */ ++int + sys_utimes(p, v, retval) + struct proc *p; + void *v; +@@ -1560,32 +2104,97 @@ + microtime(&tv[0]); + tv[1] = tv[0]; + vattr.va_vaflags |= VA_UTIMES_NULL; +- } else if (error = copyin((caddr_t)SCARG(uap, tptr), (caddr_t)tv, +- sizeof (tv))) +- return (error); ++ } else { ++ error = copyin((caddr_t)SCARG(uap, tptr), (caddr_t)tv, ++ sizeof (tv)); ++ if (error) ++ return (error); ++ /* XXX workaround timeval matching the VFS constant VNOVAL */ ++ if (tv[0].tv_sec == VNOVAL) ++ tv[0].tv_sec = VNOVAL - 1; ++ if (tv[1].tv_sec == VNOVAL) ++ tv[1].tv_sec = VNOVAL - 1; ++ } + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); +- VOP_LOCK(vp); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_mount->mnt_flag & MNT_RDONLY) + error = EROFS; + else { +- vattr.va_atime.ts_sec = tv[0].tv_sec; +- vattr.va_atime.ts_nsec = tv[0].tv_usec * 1000; +- vattr.va_mtime.ts_sec = tv[1].tv_sec; +- vattr.va_mtime.ts_nsec = tv[1].tv_usec * 1000; ++ vattr.va_atime.tv_sec = tv[0].tv_sec; ++ vattr.va_atime.tv_nsec = tv[0].tv_usec * 1000; ++ vattr.va_mtime.tv_sec = tv[1].tv_sec; ++ vattr.va_mtime.tv_nsec = tv[1].tv_usec * 1000; + error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); + } + vput(vp); + return (error); + } + ++ ++/* ++ * Set the access and modification times given a file descriptor. ++ */ ++/* ARGSUSED */ ++int ++sys_futimes(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ register struct sys_futimes_args /* { ++ syscallarg(int) fd; ++ syscallarg(struct timeval *) tptr; ++ } */ *uap = v; ++ struct vnode *vp; ++ struct timeval tv[2]; ++ struct vattr vattr; ++ int error; ++ struct file *fp; ++ ++ VATTR_NULL(&vattr); ++ if (SCARG(uap, tptr) == NULL) { ++ microtime(&tv[0]); ++ tv[1] = tv[0]; ++ vattr.va_vaflags |= VA_UTIMES_NULL; ++ } else { ++ error = copyin((caddr_t)SCARG(uap, tptr), (caddr_t)tv, ++ sizeof (tv)); ++ if (error) ++ return (error); ++ /* XXX workaround timeval matching the VFS constant VNOVAL */ ++ if (tv[0].tv_sec == VNOVAL) ++ tv[0].tv_sec = VNOVAL - 1; ++ if (tv[1].tv_sec == VNOVAL) ++ tv[1].tv_sec = VNOVAL - 1; ++ } ++ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) ++ return (error); ++ vp = (struct vnode *)fp->f_data; ++ VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); ++ if (vp->v_mount->mnt_flag & MNT_RDONLY) ++ error = EROFS; ++ else { ++ vattr.va_atime.tv_sec = tv[0].tv_sec; ++ vattr.va_atime.tv_nsec = tv[0].tv_usec * 1000; ++ vattr.va_mtime.tv_sec = tv[1].tv_sec; ++ vattr.va_mtime.tv_nsec = tv[1].tv_usec * 1000; ++ error = VOP_SETATTR(vp, &vattr, p->p_ucred, p); ++ } ++ VOP_UNLOCK(vp, 0, p); ++ FRELE(fp); ++ return (error); ++} ++ + /* + * Truncate a file given its path name. + */ + /* ARGSUSED */ ++int + sys_truncate(p, v, retval) + struct proc *p; + void *v; +@@ -1602,11 +2211,11 @@ + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); +- VOP_LOCK(vp); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_type == VDIR) + error = EISDIR; + else if ((error = vn_writechk(vp)) == 0 && +@@ -1623,12 +2232,13 @@ + * Truncate a file given a file descriptor. + */ + /* ARGSUSED */ ++int + sys_ftruncate(p, v, retval) + struct proc *p; + void *v; + register_t *retval; + { +- register struct sys_ftruncate_args /* { ++ struct sys_ftruncate_args /* { + syscallarg(int) fd; + syscallarg(int) pad; + syscallarg(off_t) length; +@@ -1638,13 +2248,15 @@ + struct file *fp; + int error; + +- if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) ++ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); +- if ((fp->f_flag & FWRITE) == 0) +- return (EINVAL); ++ if ((fp->f_flag & FWRITE) == 0) { ++ error = EINVAL; ++ goto bad; ++ } + vp = (struct vnode *)fp->f_data; + VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); +- VOP_LOCK(vp); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + if (vp->v_type == VDIR) + error = EISDIR; + else if ((error = vn_writechk(vp)) == 0) { +@@ -1652,7 +2264,9 @@ + vattr.va_size = SCARG(uap, length); + error = VOP_SETATTR(vp, &vattr, fp->f_cred, p); + } +- VOP_UNLOCK(vp); ++ VOP_UNLOCK(vp, 0, p); ++bad: ++ FRELE(fp); + return (error); + } + +@@ -1660,6 +2274,7 @@ + * Sync an open file. + */ + /* ARGSUSED */ ++int + sys_fsync(p, v, retval) + struct proc *p; + void *v; +@@ -1668,16 +2283,22 @@ + struct sys_fsync_args /* { + syscallarg(int) fd; + } */ *uap = v; +- register struct vnode *vp; ++ struct vnode *vp; + struct file *fp; + int error; + +- if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) ++ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) + return (error); + vp = (struct vnode *)fp->f_data; +- VOP_LOCK(vp); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + error = VOP_FSYNC(vp, fp->f_cred, MNT_WAIT, p); +- VOP_UNLOCK(vp); ++#ifdef FFS_SOFTUPDATES ++ if (error == 0 && vp->v_mount && (vp->v_mount->mnt_flag & MNT_SOFTDEP)) ++ error = softdep_fsync(vp); ++#endif ++ ++ VOP_UNLOCK(vp, 0, p); ++ FRELE(fp); + return (error); + } + +@@ -1686,6 +2307,7 @@ + * or both not be directories. If target is a directory, it must be empty. + */ + /* ARGSUSED */ ++int + sys_rename(p, v, retval) + struct proc *p; + void *v; +@@ -1698,15 +2320,24 @@ + register struct vnode *tvp, *fvp, *tdvp; + struct nameidata fromnd, tond; + int error; ++ int flags; + + NDINIT(&fromnd, DELETE, WANTPARENT | SAVESTART, UIO_USERSPACE, + SCARG(uap, from), p); +- if (error = namei(&fromnd)) ++ if ((error = namei(&fromnd)) != 0) + return (error); + fvp = fromnd.ni_vp; +- NDINIT(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART, ++ ++ flags = LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART; ++ /* ++ * rename("foo/", "bar/"); is OK ++ */ ++ if (fvp->v_type == VDIR) ++ flags |= STRIPSLASHES; ++ ++ NDINIT(&tond, RENAME, flags, + UIO_USERSPACE, SCARG(uap, to), p); +- if (error = namei(&tond)) { ++ if ((error = namei(&tond)) != 0) { + VOP_ABORTOP(fromnd.ni_dvp, &fromnd.ni_cnd); + vrele(fromnd.ni_dvp); + vrele(fvp); +@@ -1727,21 +2358,19 @@ + error = EINVAL; + /* + * If source is the same as the destination (that is the +- * same inode number with the same name in the same directory), +- * then there is nothing to do. ++ * same inode number) + */ +- if (fvp == tvp && fromnd.ni_dvp == tdvp && +- fromnd.ni_cnd.cn_namelen == tond.ni_cnd.cn_namelen && +- !bcmp(fromnd.ni_cnd.cn_nameptr, tond.ni_cnd.cn_nameptr, +- fromnd.ni_cnd.cn_namelen)) ++ if (fvp == tvp) + error = -1; + out: + if (!error) { + VOP_LEASE(tdvp, p, p->p_ucred, LEASE_WRITE); + if (fromnd.ni_dvp != tdvp) + VOP_LEASE(fromnd.ni_dvp, p, p->p_ucred, LEASE_WRITE); +- if (tvp) ++ if (tvp) { ++ (void)uvm_vnp_uncache(tvp); + VOP_LEASE(tvp, p, p->p_ucred, LEASE_WRITE); ++ } + error = VOP_RENAME(fromnd.ni_dvp, fromnd.ni_vp, &fromnd.ni_cnd, + tond.ni_dvp, tond.ni_vp, &tond.ni_cnd); + } else { +@@ -1771,6 +2400,7 @@ + * Make a directory file. + */ + /* ARGSUSED */ ++int + sys_mkdir(p, v, retval) + struct proc *p; + void *v; +@@ -1785,8 +2415,9 @@ + int error; + struct nameidata nd; + +- NDINIT(&nd, CREATE, LOCKPARENT, UIO_USERSPACE, SCARG(uap, path), p); +- if (error = namei(&nd)) ++ NDINIT(&nd, CREATE, LOCKPARENT | STRIPSLASHES, ++ UIO_USERSPACE, SCARG(uap, path), p); ++ if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + if (vp != NULL) { +@@ -1812,6 +2443,7 @@ + * Remove a directory file. + */ + /* ARGSUSED */ ++int + sys_rmdir(p, v, retval) + struct proc *p; + void *v; +@@ -1826,7 +2458,7 @@ + + NDINIT(&nd, DELETE, LOCKPARENT | LOCKLEAF, UIO_USERSPACE, + SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; + if (vp->v_type != VDIR) { +@@ -1837,7 +2469,7 @@ + * No rmdir "." please. + */ + if (nd.ni_dvp == vp) { +- error = EINVAL; ++ error = EBUSY; + goto out; + } + /* +@@ -1864,32 +2496,39 @@ + /* + * Read a block of directory entries in a file system independent format. + */ ++int + sys_getdirentries(p, v, retval) + struct proc *p; + void *v; + register_t *retval; + { +- register struct sys_getdirentries_args /* { ++ struct sys_getdirentries_args /* { + syscallarg(int) fd; + syscallarg(char *) buf; +- syscallarg(u_int) count; ++ syscallarg(int) count; + syscallarg(long *) basep; + } */ *uap = v; +- register struct vnode *vp; ++ struct vnode *vp; + struct file *fp; + struct uio auio; + struct iovec aiov; + long loff; + int error, eofflag; + +- if (error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) +- return (error); +- if ((fp->f_flag & FREAD) == 0) +- return (EBADF); ++ if (SCARG(uap, count) < 0) ++ return EINVAL; ++ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) ++ return (error); ++ if ((fp->f_flag & FREAD) == 0) { ++ error = EBADF; ++ goto bad; ++ } + vp = (struct vnode *)fp->f_data; + unionread: +- if (vp->v_type != VDIR) +- return (EINVAL); ++ if (vp->v_type != VDIR) { ++ error = EINVAL; ++ goto bad; ++ } + aiov.iov_base = SCARG(uap, buf); + aiov.iov_len = SCARG(uap, count); + auio.uio_iov = &aiov; +@@ -1898,57 +2537,19 @@ + auio.uio_segflg = UIO_USERSPACE; + auio.uio_procp = p; + auio.uio_resid = SCARG(uap, count); +- VOP_LOCK(vp); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); + loff = auio.uio_offset = fp->f_offset; +- error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, (u_long *)0, 0); ++ error = VOP_READDIR(vp, &auio, fp->f_cred, &eofflag, 0, 0); + fp->f_offset = auio.uio_offset; +- VOP_UNLOCK(vp); ++ VOP_UNLOCK(vp, 0, p); + if (error) +- return (error); +- +-#ifdef UNION +-{ +- extern int (**union_vnodeop_p)(); +- extern struct vnode *union_dircache __P((struct vnode *)); +- ++ goto bad; + if ((SCARG(uap, count) == auio.uio_resid) && +- (vp->v_op == union_vnodeop_p)) { +- struct vnode *lvp; +- +- lvp = union_dircache(vp); +- if (lvp != NULLVP) { +- struct vattr va; +- +- /* +- * If the directory is opaque, +- * then don't show lower entries +- */ +- error = VOP_GETATTR(vp, &va, fp->f_cred, p); +- if (va.va_flags & OPAQUE) { +- vput(lvp); +- lvp = NULL; +- } +- } +- +- if (lvp != NULLVP) { +- error = VOP_OPEN(lvp, FREAD, fp->f_cred, p); +- VOP_UNLOCK(lvp); +- +- if (error) { +- vrele(lvp); +- return (error); +- } +- fp->f_data = (caddr_t) lvp; +- fp->f_offset = 0; +- error = vn_close(vp, FREAD, fp->f_cred, p); +- if (error) +- return (error); +- vp = lvp; +- goto unionread; +- } +- } +-} +-#endif /* UNION */ ++ union_check_p && ++ (union_check_p(p, &vp, fp, auio, &error) != 0)) ++ goto unionread; ++ if (error) ++ goto bad; + + if ((SCARG(uap, count) == auio.uio_resid) && + (vp->v_flag & VROOT) && +@@ -1964,6 +2565,8 @@ + error = copyout((caddr_t)&loff, (caddr_t)SCARG(uap, basep), + sizeof(long)); + *retval = SCARG(uap, count) - auio.uio_resid; ++bad: ++ FRELE(fp); + return (error); + } + +@@ -1983,7 +2586,7 @@ + + fdp = p->p_fd; + *retval = fdp->fd_cmask; +- fdp->fd_cmask = SCARG(uap, newmask) & ALLPERMS; ++ fdp->fd_cmask = SCARG(uap, newmask) & ACCESSPERMS; + return (0); + } + +@@ -1992,6 +2595,7 @@ + * away from vnode. + */ + /* ARGSUSED */ ++int + sys_revoke(p, v, retval) + struct proc *p; + void *v; +@@ -2006,20 +2610,16 @@ + struct nameidata nd; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); +- if (error = namei(&nd)) ++ if ((error = namei(&nd)) != 0) + return (error); + vp = nd.ni_vp; +- if (vp->v_type != VCHR && vp->v_type != VBLK) { +- error = EINVAL; +- goto out; +- } +- if (error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) ++ if ((error = VOP_GETATTR(vp, &vattr, p->p_ucred, p)) != 0) + goto out; + if (p->p_ucred->cr_uid != vattr.va_uid && + (error = suser(p->p_ucred, &p->p_acflag))) + goto out; +- if (vp->v_usecount > 1 || (vp->v_flag & VALIASED)) +- vgoneall(vp); ++ if (vp->v_usecount > 1 || (vp->v_flag & (VALIASED | VLAYER))) ++ VOP_REVOKE(vp, REVOKEALL); + out: + vrele(vp); + return (error); +@@ -2027,7 +2627,10 @@ + + /* + * Convert a user file descriptor to a kernel file entry. ++ * ++ * On return *fpp is FREF:ed. + */ ++int + getvnode(fdp, fd, fpp) + struct filedesc *fdp; + struct file **fpp; +@@ -2035,11 +2638,579 @@ + { + struct file *fp; + +- if ((u_int)fd >= fdp->fd_nfiles || +- (fp = fdp->fd_ofiles[fd]) == NULL) ++ if ((fp = fd_getfile(fdp, fd)) == NULL) + return (EBADF); + if (fp->f_type != DTYPE_VNODE) + return (EINVAL); ++ FREF(fp); + *fpp = fp; ++ + return (0); + } ++ ++/* ++ * Positional read system call. ++ */ ++int ++sys_pread(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ struct sys_pread_args /* { ++ syscallarg(int) fd; ++ syscallarg(void *) buf; ++ syscallarg(size_t) nbyte; ++ syscallarg(int) pad; ++ syscallarg(off_t) offset; ++ } */ *uap = v; ++ struct filedesc *fdp = p->p_fd; ++ struct file *fp; ++ struct vnode *vp; ++ off_t offset; ++ int fd = SCARG(uap, fd); ++ ++ if ((fp = fd_getfile(fdp, fd)) == NULL) ++ return (EBADF); ++ if ((fp->f_flag & FREAD) == 0) ++ return (EBADF); ++ ++ vp = (struct vnode *)fp->f_data; ++ if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { ++ return (ESPIPE); ++ } ++ ++ offset = SCARG(uap, offset); ++ ++ FREF(fp); ++ ++ /* dofileread() will FRELE the descriptor for us */ ++ return (dofileread(p, fd, fp, SCARG(uap, buf), SCARG(uap, nbyte), ++ &offset, retval)); ++} ++ ++/* ++ * Positional scatter read system call. ++ */ ++int ++sys_preadv(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ struct sys_preadv_args /* { ++ syscallarg(int) fd; ++ syscallarg(const struct iovec *) iovp; ++ syscallarg(int) iovcnt; ++ syscallarg(int) pad; ++ syscallarg(off_t) offset; ++ } */ *uap = v; ++ struct filedesc *fdp = p->p_fd; ++ struct file *fp; ++ struct vnode *vp; ++ off_t offset; ++ int fd = SCARG(uap, fd); ++ ++ if ((fp = fd_getfile(fdp, fd)) == NULL) ++ return (EBADF); ++ if ((fp->f_flag & FREAD) == 0) ++ return (EBADF); ++ ++ vp = (struct vnode *)fp->f_data; ++ if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { ++ return (ESPIPE); ++ } ++ ++ FREF(fp); ++ ++ offset = SCARG(uap, offset); ++ ++ /* dofilereadv() will FRELE the descriptor for us */ ++ return (dofilereadv(p, fd, fp, SCARG(uap, iovp), SCARG(uap, iovcnt), ++ &offset, retval)); ++} ++ ++/* ++ * Positional write system call. ++ */ ++int ++sys_pwrite(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ struct sys_pwrite_args /* { ++ syscallarg(int) fd; ++ syscallarg(const void *) buf; ++ syscallarg(size_t) nbyte; ++ syscallarg(int) pad; ++ syscallarg(off_t) offset; ++ } */ *uap = v; ++ struct filedesc *fdp = p->p_fd; ++ struct file *fp; ++ struct vnode *vp; ++ off_t offset; ++ int fd = SCARG(uap, fd); ++ ++ if ((fp = fd_getfile(fdp, fd)) == NULL) ++ return (EBADF); ++ if ((fp->f_flag & FWRITE) == 0) ++ return (EBADF); ++ ++ vp = (struct vnode *)fp->f_data; ++ if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { ++ return (ESPIPE); ++ } ++ ++ FREF(fp); ++ ++ offset = SCARG(uap, offset); ++ ++ /* dofilewrite() will FRELE the descriptor for us */ ++ return (dofilewrite(p, fd, fp, SCARG(uap, buf), SCARG(uap, nbyte), ++ &offset, retval)); ++} ++ ++ ++/* ++ * Positional gather write system call. ++ */ ++int ++sys_pwritev(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ struct sys_pwritev_args /* { ++ syscallarg(int) fd; ++ syscallarg(const struct iovec *) iovp; ++ syscallarg(int) iovcnt; ++ syscallarg(int) pad; ++ syscallarg(off_t) offset; ++ } */ *uap = v; ++ struct filedesc *fdp = p->p_fd; ++ struct file *fp; ++ struct vnode *vp; ++ off_t offset; ++ int fd = SCARG(uap, fd); ++ ++ if ((fp = fd_getfile(fdp, fd)) == NULL) ++ return (EBADF); ++ if ((fp->f_flag & FWRITE) == 0) ++ return (EBADF); ++ ++ vp = (struct vnode *)fp->f_data; ++ if (fp->f_type != DTYPE_VNODE || vp->v_type == VFIFO) { ++ return (ESPIPE); ++ } ++ ++ FREF(fp); ++ ++ offset = SCARG(uap, offset); ++ ++ /* dofilewritev() will FRELE the descriptor for us */ ++ return (dofilewritev(p, fd, fp, SCARG(uap, iovp), SCARG(uap, iovcnt), ++ &offset, retval)); ++} ++ ++#ifdef UFS_EXTATTR ++/* ++ * Syscall to push extended attribute configuration information into the ++ * VFS. Accepts a path, which it converts to a mountpoint, as well as ++ * a command (int cmd), and attribute name and misc data. For now, the ++ * attribute name is left in userspace for consumption by the VFS_op. ++ * It will probably be changed to be copied into sysspace by the ++ * syscall in the future, once issues with various consumers of the ++ * attribute code have raised their hands. ++ * ++ * Currently this is used only by UFS Extended Attributes. ++ */ ++int ++sys_extattrctl(struct proc *p, void *v, register_t *reval) ++{ ++ struct sys_extattrctl_args /* { ++ syscallarg(const char *) path; ++ syscallarg(int) cmd; ++ syscallarg(const char *) filename; ++ syscallarg(int) attrnamespace; ++ syscallarg(const char *) attrname; ++ } */ *uap = v; ++ struct vnode *filename_vp; ++ struct nameidata nd; ++ struct mount *mp; ++ char attrname[EXTATTR_MAXNAMELEN]; ++ int error; ++ ++ /* ++ * SCARG(uap, attrname) not always defined. We check again later ++ * when we invoke the VFS call so as to pass in NULL there if needed. ++ */ ++ if (SCARG(uap, attrname) != NULL) { ++ error = copyinstr(SCARG(uap, attrname), attrname, ++ EXTATTR_MAXNAMELEN, NULL); ++ if (error) ++ return (error); ++ } ++ ++ /* ++ * SCARG(uap, filename) not always defined. If it is, grab ++ * a vnode lock, which VFS_EXTATTRCTL() will later release. ++ */ ++ filename_vp = NULL; ++ if (SCARG(uap, filename) != NULL) { ++ NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, ++ SCARG(uap, filename), p); ++ if ((error = namei(&nd)) != 0) ++ return (error); ++ filename_vp = nd.ni_vp; ++ } ++ ++ /* SCARG(uap, path) always defined. */ ++ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); ++ if ((error = namei(&nd)) != 0) { ++ if (filename_vp != NULL) ++ vput(filename_vp); ++ return (error); ++ } ++ ++ mp = nd.ni_vp->v_mount; ++ if (error) { ++ if (filename_vp != NULL) ++ vput(filename_vp); ++ return (error); ++ } ++ ++ if (SCARG(uap, attrname) != NULL) { ++ error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), filename_vp, ++ SCARG(uap, attrnamespace), attrname, p); ++ } else { ++ error = VFS_EXTATTRCTL(mp, SCARG(uap, cmd), filename_vp, ++ SCARG(uap, attrnamespace), NULL, p); ++ } ++ ++ /* ++ * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, ++ * filename_vp, so vrele it if it is defined. ++ */ ++ if (filename_vp != NULL) ++ vrele(filename_vp); ++ ++ return (error); ++} ++ ++/*- ++ * Set a named extended attribute on a file or directory ++ * ++ * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", ++ * kernelspace string pointer "attrname", userspace buffer ++ * pointer "data", buffer length "nbytes", thread "td". ++ * Returns: 0 on success, an error number otherwise ++ * Locks: none ++ * References: vp must be a valid reference for the duration of the call ++ */ ++static int ++extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname, ++ void *data, size_t nbytes, struct proc *p, register_t *retval) ++{ ++ struct uio auio; ++ struct iovec aiov; ++ ssize_t cnt; ++ int error; ++ ++ VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); ++ ++ aiov.iov_base = data; ++ aiov.iov_len = nbytes; ++ auio.uio_iov = &aiov; ++ auio.uio_iovcnt = 1; ++ auio.uio_offset = 0; ++ if (nbytes > INT_MAX) { ++ error = EINVAL; ++ goto done; ++ } ++ auio.uio_resid = nbytes; ++ auio.uio_rw = UIO_WRITE; ++ auio.uio_segflg = UIO_USERSPACE; ++ auio.uio_procp = p; ++ cnt = nbytes; ++ ++ error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio, ++ p->p_ucred, p); ++ cnt -= auio.uio_resid; ++ retval[0] = cnt; ++ ++done: ++ VOP_UNLOCK(vp, 0, p); ++ return (error); ++} ++ ++int ++sys_extattr_set_file(struct proc *p, void *v, register_t *retval) ++{ ++ struct sys_extattr_set_file_args /* { ++ syscallarg(const char *) path; ++ syscallarg(int) attrnamespace; ++ syscallarg(const char *) attrname; ++ syscallarg(void *) data; ++ syscallarg(size_t) nbytes; ++ } */ *uap = v; ++ struct nameidata nd; ++ char attrname[EXTATTR_MAXNAMELEN]; ++ int error; ++ ++ error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, ++ NULL); ++ if (error) ++ return (error); ++ ++ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); ++ if ((error = namei(&nd)) != 0) ++ return (error); ++ ++ error = extattr_set_vp(nd.ni_vp, SCARG(uap, attrnamespace), attrname, ++ SCARG(uap, data), SCARG(uap, nbytes), p, retval); ++ ++ vrele(nd.ni_vp); ++ return (error); ++} ++ ++int ++sys_extattr_set_fd(struct proc *p, void *v, register_t *retval) ++{ ++ struct sys_extattr_set_fd_args /* { ++ syscallarg(int) fd; ++ syscallarg(int) attrnamespace; ++ syscallarg(const char *) attrname; ++ syscallarg(struct iovec *) iovp; ++ syscallarg(int) iovcnt; ++ } */ *uap = v; ++ struct file *fp; ++ char attrname[EXTATTR_MAXNAMELEN]; ++ int error; ++ ++ error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, ++ NULL); ++ if (error) ++ return (error); ++ ++ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) ++ return (error); ++ ++ error = extattr_set_vp((struct vnode *)fp->f_data, ++ SCARG(uap, attrnamespace), attrname, SCARG(uap, data), ++ SCARG(uap, nbytes), p, retval); ++ FRELE(fp); ++ ++ return (error); ++} ++ ++/*- ++ * Get a named extended attribute on a file or directory ++ * ++ * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", ++ * kernelspace string pointer "attrname", userspace buffer ++ * pointer "data", buffer length "nbytes", thread "td". ++ * Returns: 0 on success, an error number otherwise ++ * Locks: none ++ * References: vp must be a valid reference for the duration of the call ++ */ ++static int ++extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname, ++ void *data, size_t nbytes, struct proc *p, register_t *retval) ++{ ++ struct uio auio; ++ struct iovec aiov; ++ ssize_t cnt; ++ size_t size; ++ int error; ++ ++ VOP_LEASE(vp, p, p->p_ucred, LEASE_READ); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); ++ ++ /* ++ * Slightly unusual semantics: if the user provides a NULL data ++ * pointer, they don't want to receive the data, just the ++ * maximum read length. ++ */ ++ if (data != NULL) { ++ aiov.iov_base = data; ++ aiov.iov_len = nbytes; ++ auio.uio_iov = &aiov; ++ auio.uio_offset = 0; ++ if (nbytes > INT_MAX) { ++ error = EINVAL; ++ goto done; ++ } ++ auio.uio_resid = nbytes; ++ auio.uio_rw = UIO_READ; ++ auio.uio_segflg = UIO_USERSPACE; ++ auio.uio_procp = p; ++ cnt = nbytes; ++ error = VOP_GETEXTATTR(vp, attrnamespace, attrname, &auio, ++ NULL, p->p_ucred, p); ++ cnt -= auio.uio_resid; ++ retval[0] = cnt; ++ } else { ++ error = VOP_GETEXTATTR(vp, attrnamespace, attrname, NULL, ++ &size, p->p_ucred, p); ++ retval[0] = size; ++ } ++done: ++ VOP_UNLOCK(vp, 0, p); ++ return (error); ++} ++ ++int ++sys_extattr_get_file(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ struct sys_extattr_get_file_args /* { ++ syscallarg(const char *) path; ++ syscallarg(int) attrnamespace; ++ syscallarg(const char *) attrname; ++ syscallarg(void *) data; ++ syscallarg(size_t) nbytes; ++ } */ *uap = v; ++ struct nameidata nd; ++ char attrname[EXTATTR_MAXNAMELEN]; ++ int error; ++ ++ error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, ++ NULL); ++ if (error) ++ return (error); ++ ++ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); ++ if ((error = namei(&nd)) != 0) ++ return (error); ++ ++ error = extattr_get_vp(nd.ni_vp, SCARG(uap, attrnamespace), attrname, ++ SCARG(uap, data), SCARG(uap, nbytes), p, retval); ++ ++ vrele(nd.ni_vp); ++ return (error); ++} ++ ++int ++sys_extattr_get_fd(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ struct sys_extattr_get_fd_args /* { ++ syscallarg(int) fd; ++ syscallarg(int) attrnamespace; ++ syscallarg(const char *) attrname; ++ syscallarg(void *) data; ++ syscallarg(size_t) nbytes; ++ } */ *uap = v; ++ struct file *fp; ++ char attrname[EXTATTR_MAXNAMELEN]; ++ int error; ++ ++ error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, ++ NULL); ++ if (error) ++ return (error); ++ ++ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) ++ return (error); ++ ++ error = extattr_get_vp((struct vnode *)fp->f_data, ++ SCARG(uap, attrnamespace), attrname, SCARG(uap, data), ++ SCARG(uap, nbytes), p, retval); ++ FRELE(fp); ++ ++ return (error); ++} ++ ++/* ++ * extattr_delete_vp(): Delete a named extended attribute on a file or ++ * directory ++ * ++ * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace", ++ * kernelspace string pointer "attrname", proc "p" ++ * Returns: 0 on success, an error number otherwise ++ * Locks: none ++ * References: vp must be a valid reference for the duration of the call ++ */ ++static int ++extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname, ++ struct proc *p) ++{ ++ int error; ++ ++ VOP_LEASE(vp, p, p->p_ucred, LEASE_WRITE); ++ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p); ++ ++ error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL, ++ p->p_ucred, p); ++ ++ VOP_UNLOCK(vp, 0, p); ++ return (error); ++} ++ ++int ++sys_extattr_delete_file(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ struct sys_extattr_delete_file_args /* { ++ syscallarg(int) fd; ++ syscallarg(int) attrnamespace; ++ syscallarg(const char *) attrname; ++ } */ *uap = v; ++ struct nameidata nd; ++ char attrname[EXTATTR_MAXNAMELEN]; ++ int error; ++ ++ error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, ++ NULL); ++ if (error) ++ return(error); ++ ++ NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, SCARG(uap, path), p); ++ if ((error = namei(&nd)) != 0) ++ return(error); ++ ++ error = extattr_delete_vp(nd.ni_vp, SCARG(uap, attrnamespace), ++ attrname, p); ++ ++ vrele(nd.ni_vp); ++ return(error); ++} ++ ++int ++sys_extattr_delete_fd(p, v, retval) ++ struct proc *p; ++ void *v; ++ register_t *retval; ++{ ++ struct sys_extattr_delete_fd_args /* { ++ syscallarg(int) fd; ++ syscallarg(int) attrnamespace; ++ syscallarg(const char *) attrname; ++ } */ *uap = v; ++ struct file *fp; ++ char attrname[EXTATTR_MAXNAMELEN]; ++ int error; ++ ++ error = copyinstr(SCARG(uap, attrname), attrname, EXTATTR_MAXNAMELEN, ++ NULL); ++ if (error) ++ return (error); ++ ++ if ((error = getvnode(p->p_fd, SCARG(uap, fd), &fp)) != 0) ++ return (error); ++ ++ error = extattr_delete_vp((struct vnode *)fp->f_data, ++ SCARG(uap, attrnamespace), attrname, p); ++ FRELE(fp); ++ ++ return (error); ++} ++#endif diff --git a/sys/src/cmd/diff/test/diff.rc b/sys/src/cmd/diff/test/diff.rc new file mode 100755 index 000000000..5801a4634 --- /dev/null +++ b/sys/src/cmd/diff/test/diff.rc @@ -0,0 +1,11 @@ +#!/bin/rc + +# tests with no line endings +# are broken; fix them later +# tests=`{seq 15} +tests=(1 2 3 4 5 11 12 13 15) +for(t in $tests){ + echo ../$O.diff -u diff-t$t.1 diff-t$t.2 + ../$O.diff -u diff-t$t.1 diff-t$t.2 > diff-t$t.out + cmp diff-t$t.out diff-t$t.expected #|| {echo 'failed '$t; exit failed} +} diff --git a/sys/src/cmd/diff/test/merge-t1.c b/sys/src/cmd/diff/test/merge-t1.c new file mode 100644 index 000000000..f00c965d8 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t1.c @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t1.expected b/sys/src/cmd/diff/test/merge-t1.expected new file mode 100644 index 000000000..f00c965d8 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t1.expected @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t1.l b/sys/src/cmd/diff/test/merge-t1.l new file mode 100644 index 000000000..f00c965d8 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t1.l @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t1.r b/sys/src/cmd/diff/test/merge-t1.r new file mode 100644 index 000000000..f00c965d8 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t1.r @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t2.c b/sys/src/cmd/diff/test/merge-t2.c new file mode 100644 index 000000000..5327dcb2e --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t2.c @@ -0,0 +1,8 @@ +1 +2 +4 +5 +6 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t2.expected b/sys/src/cmd/diff/test/merge-t2.expected new file mode 100644 index 000000000..f00c965d8 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t2.expected @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t2.l b/sys/src/cmd/diff/test/merge-t2.l new file mode 100644 index 000000000..2b8908310 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t2.l @@ -0,0 +1,9 @@ +1 +2 +3 +4 +5 +6 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t2.r b/sys/src/cmd/diff/test/merge-t2.r new file mode 100644 index 000000000..fdc5160e7 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t2.r @@ -0,0 +1,9 @@ +1 +2 +4 +5 +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t3.c b/sys/src/cmd/diff/test/merge-t3.c new file mode 100644 index 000000000..f00c965d8 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t3.c @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t3.expected b/sys/src/cmd/diff/test/merge-t3.expected new file mode 100644 index 000000000..f2708215c --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t3.expected @@ -0,0 +1,16 @@ +1 +2 +3 +4 +<<<<<<<<<< merge-t3.l +y +========== original +5 +========== merge-t3.r +x +>>>>>>>>>> +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t3.l b/sys/src/cmd/diff/test/merge-t3.l new file mode 100644 index 000000000..92635d4de --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t3.l @@ -0,0 +1,10 @@ +1 +2 +3 +4 +y +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t3.r b/sys/src/cmd/diff/test/merge-t3.r new file mode 100644 index 000000000..54fba2f38 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t3.r @@ -0,0 +1,10 @@ +1 +2 +3 +4 +x +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t4.c b/sys/src/cmd/diff/test/merge-t4.c new file mode 100644 index 000000000..f00c965d8 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t4.c @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t4.expected b/sys/src/cmd/diff/test/merge-t4.expected new file mode 100644 index 000000000..b5a6c2515 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t4.expected @@ -0,0 +1,22 @@ +1 +2 +3 +<<<<<<<<<< merge-t4.l +4 +x +6 +7 +========== original +4 +5 +6 +7 +========== merge-t4.r +w +x +y +z +>>>>>>>>>> +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t4.l b/sys/src/cmd/diff/test/merge-t4.l new file mode 100644 index 000000000..54fba2f38 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t4.l @@ -0,0 +1,10 @@ +1 +2 +3 +4 +x +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t4.r b/sys/src/cmd/diff/test/merge-t4.r new file mode 100644 index 000000000..049221650 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t4.r @@ -0,0 +1,10 @@ +1 +2 +3 +w +x +y +z +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t5.c b/sys/src/cmd/diff/test/merge-t5.c new file mode 100644 index 000000000..f00c965d8 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t5.c @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t5.expected b/sys/src/cmd/diff/test/merge-t5.expected new file mode 100644 index 000000000..f3d6c07a8 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t5.expected @@ -0,0 +1,25 @@ +1 +a +b +c +2 +3 +<<<<<<<<<< merge-t5.l +4 +x +6 +7 +========== original +4 +5 +6 +7 +========== merge-t5.r +w +x +y +z +>>>>>>>>>> +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t5.l b/sys/src/cmd/diff/test/merge-t5.l new file mode 100644 index 000000000..dea3f6bc5 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t5.l @@ -0,0 +1,13 @@ +1 +a +b +c +2 +3 +4 +x +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t5.r b/sys/src/cmd/diff/test/merge-t5.r new file mode 100644 index 000000000..049221650 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t5.r @@ -0,0 +1,10 @@ +1 +2 +3 +w +x +y +z +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t6.c b/sys/src/cmd/diff/test/merge-t6.c new file mode 100644 index 000000000..f00c965d8 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t6.c @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t6.expected b/sys/src/cmd/diff/test/merge-t6.expected new file mode 100644 index 000000000..3bb459b83 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t6.expected @@ -0,0 +1,11 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 diff --git a/sys/src/cmd/diff/test/merge-t6.l b/sys/src/cmd/diff/test/merge-t6.l new file mode 100644 index 000000000..3bb459b83 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t6.l @@ -0,0 +1,11 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 diff --git a/sys/src/cmd/diff/test/merge-t6.r b/sys/src/cmd/diff/test/merge-t6.r new file mode 100644 index 000000000..f00c965d8 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t6.r @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t7.c b/sys/src/cmd/diff/test/merge-t7.c new file mode 100644 index 000000000..f00c965d8 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t7.c @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t7.expected b/sys/src/cmd/diff/test/merge-t7.expected new file mode 100644 index 000000000..151cf9c3e --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t7.expected @@ -0,0 +1,12 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 diff --git a/sys/src/cmd/diff/test/merge-t7.l b/sys/src/cmd/diff/test/merge-t7.l new file mode 100644 index 000000000..151cf9c3e --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t7.l @@ -0,0 +1,12 @@ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 diff --git a/sys/src/cmd/diff/test/merge-t7.r b/sys/src/cmd/diff/test/merge-t7.r new file mode 100644 index 000000000..f00c965d8 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t7.r @@ -0,0 +1,10 @@ +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/sys/src/cmd/diff/test/merge-t8.c b/sys/src/cmd/diff/test/merge-t8.c new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t8.c @@ -0,0 +1 @@ +foo diff --git a/sys/src/cmd/diff/test/merge-t8.expected b/sys/src/cmd/diff/test/merge-t8.expected new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t8.expected diff --git a/sys/src/cmd/diff/test/merge-t8.l b/sys/src/cmd/diff/test/merge-t8.l new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t8.l diff --git a/sys/src/cmd/diff/test/merge-t8.r b/sys/src/cmd/diff/test/merge-t8.r new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t8.r @@ -0,0 +1 @@ +foo diff --git a/sys/src/cmd/diff/test/merge-t9.c b/sys/src/cmd/diff/test/merge-t9.c new file mode 100644 index 000000000..1191247b6 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t9.c @@ -0,0 +1,2 @@ +1 +2 diff --git a/sys/src/cmd/diff/test/merge-t9.expected b/sys/src/cmd/diff/test/merge-t9.expected new file mode 100644 index 000000000..422c2b7ab --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t9.expected @@ -0,0 +1,2 @@ +a +b diff --git a/sys/src/cmd/diff/test/merge-t9.l b/sys/src/cmd/diff/test/merge-t9.l new file mode 100644 index 000000000..e9395445a --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t9.l @@ -0,0 +1,2 @@ +a +2 diff --git a/sys/src/cmd/diff/test/merge-t9.r b/sys/src/cmd/diff/test/merge-t9.r new file mode 100644 index 000000000..2f1f45816 --- /dev/null +++ b/sys/src/cmd/diff/test/merge-t9.r @@ -0,0 +1,2 @@ +1 +b diff --git a/sys/src/cmd/diff/test/merge.rc b/sys/src/cmd/diff/test/merge.rc new file mode 100755 index 000000000..b10f1afbe --- /dev/null +++ b/sys/src/cmd/diff/test/merge.rc @@ -0,0 +1,8 @@ +#!/bin/rc + +tests=`{seq 9} +for(t in $tests){ + echo ../$O.merge3 merge-t$t.l merge-t$t.c merge-t$t.r + ../$O.merge3 merge-t$t.l merge-t$t.c merge-t$t.r > merge-t$t.out + cmp merge-t$t.out merge-t$t.expected #|| {echo 'failed '$t; exit failed} +} diff --git a/sys/src/cmd/diff/test/merge5.expected b/sys/src/cmd/diff/test/merge5.expected new file mode 100644 index 000000000..fb4e99e00 --- /dev/null +++ b/sys/src/cmd/diff/test/merge5.expected @@ -0,0 +1,29 @@ +lc: (2,1)::(2,4) +rc: (4,7)::(4,7) +1 +a +b +c +lc: (5,5)::(8,8) +rc: (4,7)::(4,7) +2 +3 +<<<<<<<<<< test/merge-t5.l +4 +x +6 +7 +========== original +4 +5 +6 +7 +========== test/merge-t5.r +w +x +y +z +>>>>>>>>>> +8 +9 +10 diff --git a/sys/src/cmd/diff/test/mkfile b/sys/src/cmd/diff/test/mkfile new file mode 100644 index 000000000..25135f1f2 --- /dev/null +++ b/sys/src/cmd/diff/test/mkfile @@ -0,0 +1,7 @@ +</$objtype/mkfile + +TEST=\ + diff\ + merge + +</sys/src/cmd/mktest |