diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/disk |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/disk')
72 files changed, 23331 insertions, 0 deletions
diff --git a/sys/src/cmd/disk/9660/boot.c b/sys/src/cmd/disk/9660/boot.c new file mode 100755 index 000000000..ef3f7c297 --- /dev/null +++ b/sys/src/cmd/disk/9660/boot.c @@ -0,0 +1,210 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> + +#include "iso9660.h" + +/* FreeBSD 4.5 installation CD for reference +g% cdsector 17 | xd -b +1+0 records in +1+0 records out +0000000 00 - volume descriptor type (0) + 43 44 30 30 31 - "CD001" + 01 - volume descriptor version (1) + 45 4c 20 54 4f 52 49 54 4f +0000010 20 53 50 45 43 49 46 49 43 41 54 49 4f 4e 00 00 +0000020 00 00 00 00 00 00 00 - 7-39 boot system identifier +"EL TORITO SPECIFICATION" + + 00 00 00 00 00 00 00 00 00 +0000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0000040 00 00 00 00 00 00 00 - 39-71 boot identifier + +boot system use: + +absolute pointer to the boot catalog?? + + 4d 0c 00 00 00 00 00 00 00 +0000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0000580 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0000590 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00005a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +g% cdsector 3149 | xd -b # 0x0c4d +1+0 records in +1+0 records out +0000000 01 - header (1) + 00 - platform id (0 = 0x86) + 00 00 - reserved 0 + 00 00 00 00 00 00 00 00 00 00 00 00 +0000010 00 00 00 00 00 00 00 00 00 00 00 00 - id string + aa 55 - checksum + 55 aa - magic + +0000020 88 - 88 = bootable + 03 - 3 = 2.88MB diskette + 00 00 - load segment 0 means default 0x7C0 + 00 - system type (byte 5 of boot image) + 00 - unused (0) + 01 00 - 512-byte sector count for initial load + 4e 0c 00 00 - ptr to virtual disk + 00 00 00 00 +0000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +0000060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +g% cdsector `{h2d 0c4e} | xd -b +1+0 records in +1+0 records out +0000000 eb 3c 00 00 00 00 00 00 00 00 00 00 02 00 00 00 +0000010 00 00 00 00 00 00 00 00 12 00 02 00 00 00 00 00 +0000020 00 00 00 00 00 16 1f 66 6a 00 51 50 06 53 + 31 c0 + +FREEBSD +0000000 eb 3c 00 00 00 00 00 00 00 00 00 00 02 00 00 00 +0000010 00 00 00 00 00 00 00 00 12 00 02 00 00 00 00 00 +0000020 00 00 00 00 00 16 1f 66 6a 00 51 50 06 53 + 31 c0 + +DOS 5 +0000000 eb 3c 90 4d 53 44 4f 53 35 2e 30 00 02 01 01 00 +0000010 02 e0 00 40 0b f0 09 00 12 00 02 00 00 00 00 00 +0000020 00 00 00 00 00 00 29 6a 2c e0 16 4e 4f 20 4e 41 +0000030 4d 45 20 20 20 20 46 41 54 31 32 20 20 20 fa 33 +0000040 c0 8e d0 bc 00 7c 16 07 bb 78 00 36 c5 37 1e 56 +0000050 16 53 bf 3e 7c b9 0b 00 fc f3 a4 06 1f c6 45 fe +0000060 0f 8b 0e 18 7c 88 4d f9 89 47 02 c7 07 3e 7c fb +0000070 cd 13 72 79 33 c0 39 06 13 7c 74 08 8b 0e 13 7c +0000080 89 0e 20 7c a0 10 7c f7 26 16 7c 03 06 1c 7c 13 +0000090 16 1e 7c 03 06 0e 7c 83 d2 00 a3 50 7c 89 16 52 +00000a0 7c a3 49 7c 89 16 4b 7c b8 20 00 f7 26 11 7c 8b + +NDISK +0000000 eb 3c 90 50 6c 61 6e 39 2e 30 30 00 02 01 01 00 +0000010 02 e0 00 40 0b f0 09 00 12 00 02 00 00 00 00 00 +0000020 40 0b 00 00 00 00 29 13 00 00 00 43 59 4c 49 4e +0000030 44 52 49 43 41 4c 46 41 54 31 32 20 20 20 fa 31 +0000040 c0 8e d0 8e d8 8e c0 bc ec 7b 89 e5 88 56 12 fb +0000050 be ea 7d bf 90 7d ff d7 bf 82 7d ff d7 8b 06 27 +0000060 7c 8b 16 29 7c bb 00 7e bf 2c 7d ff d7 bb 10 00 + +PBSDISK +0000000 eb 3c 90 50 6c 61 6e 39 2e 30 30 00 02 01 01 00 +0000010 02 e0 00 40 0b f0 09 00 12 00 02 00 00 00 00 00 +0000020 40 0b 00 00 00 00 29 13 00 00 00 43 59 4c 49 4e +0000030 44 52 49 43 41 4c 46 41 54 31 32 20 20 20 fa 31 +0000040 c0 8e d0 8e d8 8e c0 bc f8 7b 89 e5 88 56 00 fb +0000050 be f8 7d bf 00 7d ff d7 bf df 7c ff d7 8b 06 27 +0000060 7c 8b 16 29 7c bb 00 7e bf 89 7c ff d7 bf fb 7c +*/ + +void +Cputbootvol(Cdimg *cd) +{ + Cputc(cd, 0x00); + Cputs(cd, "CD001", 5); + Cputc(cd, 0x01); + Cputs(cd, "EL TORITO SPECIFICATION", 2+1+6+1+13); + Crepeat(cd, 0, 2+16+16+7); + cd->bootcatptr = Cwoffset(cd); + Cpadblock(cd); +} + +void +Cupdatebootvol(Cdimg *cd) +{ + uvlong o; + + o = Cwoffset(cd); + Cwseek(cd, cd->bootcatptr); + Cputnl(cd, cd->bootcatblock, 4); + Cwseek(cd, o); +} + +void +Cputbootcat(Cdimg *cd) +{ + cd->bootcatblock = Cwoffset(cd) / Blocksize; + Cputc(cd, 0x01); + Cputc(cd, 0x00); + Cputc(cd, 0x00); + Cputc(cd, 0x00); + Crepeat(cd, 0, 12+12); + + /* + * either the checksum doesn't include the header word + * or it just doesn't matter. + */ + Cputc(cd, 0xAA); + Cputc(cd, 0x55); + Cputc(cd, 0x55); + Cputc(cd, 0xAA); + + cd->bootimageptr = Cwoffset(cd); + Cpadblock(cd); +} + +enum { + Emusectsz = 512, /* bytes per emulated sector */ +}; + +void +Cupdatebootcat(Cdimg *cd) +{ + uvlong o; + int n; + + if(cd->bootdirec == nil) + return; + + o = Cwoffset(cd); + Cwseek(cd, cd->bootimageptr); + Cputc(cd, 0x88); + + if(cd->flags & CDbootnoemu) + Cputc(cd, 0); /* no disk emulation */ + else + switch(cd->bootdirec->length){ + default: + fprint(2, "warning: boot image is not 1.44MB or 2.88MB; " + "pretending 1.44MB\n"); + /* fall through */ + case 1440*1024: + Cputc(cd, 0x02); /* 1.44MB disk */ + break; + case 2880*1024: + Cputc(cd, 0x03); /* 2.88MB disk */ + break; + } + Cputnl(cd, 0, 2); /* load segment */ + Cputc(cd, 0); /* system type */ + Cputc(cd, 0); /* unused */ + + n = 1; + if(cd->flags & CDbootnoemu){ + n = (cd->bootdirec->length + Emusectsz - 1) / Emusectsz; + if(n > 4){ + fprint(2, "warning: boot image too big; " + "will only load the first 2K\n"); + n = 4; + } + } + Cputnl(cd, n, 2); /* Emusectsz-byte sector count for load */ + Cputnl(cd, cd->bootdirec->block, 4); /* ptr to disk image */ + Cwseek(cd, o); +} + +void +findbootimage(Cdimg *cd, Direc *root) +{ + Direc *d; + + d = walkdirec(root, cd->bootimage); + if(d == nil){ + fprint(2, "warning: did not encounter boot image\n"); + return; + } + + cd->bootdirec = d; +} diff --git a/sys/src/cmd/disk/9660/cdrdwr.c b/sys/src/cmd/disk/9660/cdrdwr.c new file mode 100755 index 000000000..21c9c047a --- /dev/null +++ b/sys/src/cmd/disk/9660/cdrdwr.c @@ -0,0 +1,636 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> + +#include "iso9660.h" + +static int readisodesc(Cdimg*, Voldesc*); +static int readjolietdesc(Cdimg*, Voldesc*); + +/* + * It's not strictly conforming; instead it's enough to + * get us up and running; presumably the real CD writing + * will take care of being conforming. + * + * Things not conforming include: + * - no path table + * - root directories are of length zero + */ +Cdimg* +createcd(char *file, Cdinfo info) +{ + int fd, xfd; + Cdimg *cd; + + if(access(file, AEXIST) == 0){ + werrstr("file already exists"); + return nil; + } + + if((fd = create(file, ORDWR, 0666)) < 0) + return nil; + + cd = emalloc(sizeof *cd); + cd->file = atom(file); + + Binit(&cd->brd, fd, OREAD); + + if((xfd = open(file, ORDWR)) < 0) + sysfatal("can't open file again: %r"); + Binit(&cd->bwr, xfd, OWRITE); + + Crepeat(cd, 0, 16*Blocksize); + Cputisopvd(cd, info); + if(info.flags & CDbootable){ + cd->bootimage = info.bootimage; + cd->flags |= info.flags & (CDbootable|CDbootnoemu); + Cputbootvol(cd); + } + + if(readisodesc(cd, &cd->iso) < 0) + assert(0); + if(info.flags & CDplan9) + cd->flags |= CDplan9; + else if(info.flags & CDrockridge) + cd->flags |= CDrockridge; + if(info.flags & CDjoliet) { + Cputjolietsvd(cd, info); + if(readjolietdesc(cd, &cd->joliet) < 0) + assert(0); + cd->flags |= CDjoliet; + } + Cputendvd(cd); + + if(info.flags & CDdump){ + cd->nulldump = Cputdumpblock(cd); + cd->flags |= CDdump; + } + if(cd->flags & CDbootable){ + Cputbootcat(cd); + Cupdatebootvol(cd); + } + + if(info.flags & CDconform) + cd->flags |= CDconform; + + cd->flags |= CDnew; + cd->nextblock = Cwoffset(cd) / Blocksize; + assert(cd->nextblock != 0); + + return cd; +} + +Cdimg* +opencd(char *file, Cdinfo info) +{ + int fd, xfd; + Cdimg *cd; + Dir *d; + + if((fd = open(file, ORDWR)) < 0) { + if(access(file, AEXIST) == 0) + return nil; + return createcd(file, info); + } + + if((d = dirfstat(fd)) == nil) { + close(fd); + return nil; + } + if(d->length == 0 || d->length % Blocksize) { + werrstr("bad length %lld", d->length); + close(fd); + free(d); + return nil; + } + + cd = emalloc(sizeof *cd); + cd->file = atom(file); + cd->nextblock = d->length / Blocksize; + assert(cd->nextblock != 0); + free(d); + + Binit(&cd->brd, fd, OREAD); + + if((xfd = open(file, ORDWR)) < 0) + sysfatal("can't open file again: %r"); + Binit(&cd->bwr, xfd, OWRITE); + + if(readisodesc(cd, &cd->iso) < 0) { + free(cd); + close(fd); + close(xfd); + return nil; + } + + /* lowercase because of isostring */ + if(strstr(cd->iso.systemid, "iso9660") == nil + && strstr(cd->iso.systemid, "utf8") == nil) { + werrstr("unknown systemid %s", cd->iso.systemid); + free(cd); + close(fd); + close(xfd); + return nil; + } + + if(strstr(cd->iso.systemid, "plan 9")) + cd->flags |= CDplan9; + if(strstr(cd->iso.systemid, "iso9660")) + cd->flags |= CDconform; + if(strstr(cd->iso.systemid, "rrip")) + cd->flags |= CDrockridge; + if(strstr(cd->iso.systemid, "boot")) + cd->flags |= CDbootable; + if(readjolietdesc(cd, &cd->joliet) == 0) + cd->flags |= CDjoliet; + if(hasdump(cd)) + cd->flags |= CDdump; + + return cd; +} + +ulong +big(void *a, int n) +{ + uchar *p; + ulong v; + int i; + + p = a; + v = 0; + for(i=0; i<n; i++) + v = (v<<8) | *p++; + return v; +} + +ulong +little(void *a, int n) +{ + uchar *p; + ulong v; + int i; + + p = a; + v = 0; + for(i=0; i<n; i++) + v |= (*p++<<(i*8)); + return v; +} + +void +Creadblock(Cdimg *cd, void *buf, ulong block, ulong len) +{ + assert(block != 0); /* nothing useful there */ + + Bflush(&cd->bwr); + if(Bseek(&cd->brd, (vlong)block * Blocksize, 0) != + (vlong)block * Blocksize) + sysfatal("error seeking to block %lud", block); + if(Bread(&cd->brd, buf, len) != len) + sysfatal("error reading %lud bytes at block %lud: %r %lld", + len, block, Bseek(&cd->brd, 0, 2)); +} + +int +parsedir(Cdimg *cd, Direc *d, uchar *buf, int len, char *(*cvtname)(uchar*, int)) +{ + enum { NAMELEN = 28 }; + char name[NAMELEN]; + uchar *p; + Cdir *c; + + memset(d, 0, sizeof *d); + + c = (Cdir*)buf; + + if(c->len > len) { + werrstr("buffer too small"); + return -1; + } + + if(c->namelen == 1 && c->name[0] == '\0') + d->name = atom("."); + else if(c->namelen == 1 && c->name[0] == '\001') + d->name = atom(".."); + else if(cvtname) + d->name = cvtname(c->name, c->namelen); + + d->block = little(c->dloc, 4); + d->length = little(c->dlen, 4); + + if(c->flags & 2) + d->mode |= DMDIR; + +/*BUG: do we really need to parse the plan 9 fields? */ + /* plan 9 use fields */ + if((cd->flags & CDplan9) && cvtname == isostring + && (c->namelen != 1 || c->name[0] > 1)) { + p = buf+33+c->namelen; + if((p-buf)&1) + p++; + assert(p < buf+c->len); + assert(*p < NAMELEN); + if(*p != 0) { + memmove(name, p+1, *p); + name[*p] = '\0'; + d->confname = d->name; + d->name = atom(name); + } + p += *p+1; + assert(*p < NAMELEN); + memmove(name, p+1, *p); + name[*p] = '\0'; + d->uid = atom(name); + p += *p+1; + assert(*p < NAMELEN); + memmove(name, p+1, *p); + name[*p] = '\0'; + d->gid = atom(name); + p += *p+1; + if((p-buf)&1) + p++; + d->mode = little(p, 4); + } + + // BUG: rock ridge extensions + return 0; +} + +void +setroot(Cdimg *cd, ulong block, ulong dloc, ulong dlen) +{ + assert(block != 0); + + Cwseek(cd, (vlong)block * Blocksize + offsetof(Cvoldesc, rootdir[0]) + + offsetof(Cdir, dloc[0])); + Cputn(cd, dloc, 4); + Cputn(cd, dlen, 4); +} + +void +setvolsize(Cdimg *cd, ulong block, ulong size) +{ + assert(block != 0); + + Cwseek(cd, (vlong)block * Blocksize + offsetof(Cvoldesc, volsize[0])); + Cputn(cd, size, 4); +} + +void +setpathtable(Cdimg *cd, ulong block, ulong sz, ulong lloc, ulong bloc) +{ + assert(block != 0); + + Cwseek(cd, (vlong)block * Blocksize + offsetof(Cvoldesc, pathsize[0])); + Cputn(cd, sz, 4); + Cputnl(cd, lloc, 4); + Cputnl(cd, 0, 4); + Cputnm(cd, bloc, 4); + Cputnm(cd, 0, 4); + assert(Cwoffset(cd) == (vlong)block * Blocksize + + offsetof(Cvoldesc, rootdir[0])); +} + + +static void +parsedesc(Voldesc *v, Cvoldesc *cv, char *(*string)(uchar*, int)) +{ + v->systemid = string(cv->systemid, sizeof cv->systemid); + + v->pathsize = little(cv->pathsize, 4); + v->lpathloc = little(cv->lpathloc, 4); + v->mpathloc = little(cv->mpathloc, 4); + + v->volumeset = string(cv->volumeset, sizeof cv->volumeset); + v->publisher = string(cv->publisher, sizeof cv->publisher); + v->preparer = string(cv->preparer, sizeof cv->preparer); + v->application = string(cv->application, sizeof cv->application); + + v->abstract = string(cv->abstract, sizeof cv->abstract); + v->biblio = string(cv->biblio, sizeof cv->biblio); + v->notice = string(cv->notice, sizeof cv->notice); +} + +static int +readisodesc(Cdimg *cd, Voldesc *v) +{ + static uchar magic[] = { 0x01, 'C', 'D', '0', '0', '1', 0x01, 0x00 }; + Cvoldesc cv; + + memset(v, 0, sizeof *v); + + Creadblock(cd, &cv, 16, sizeof cv); + if(memcmp(cv.magic, magic, sizeof magic) != 0) { + werrstr("bad pvd magic"); + return -1; + } + + if(little(cv.blocksize, 2) != Blocksize) { + werrstr("block size not %d", Blocksize); + return -1; + } + + cd->iso9660pvd = 16; + parsedesc(v, &cv, isostring); + + return parsedir(cd, &v->root, cv.rootdir, sizeof cv.rootdir, isostring); +} + +static int +readjolietdesc(Cdimg *cd, Voldesc *v) +{ + int i; + static uchar magic[] = { 0x02, 'C', 'D', '0', '0', '1', 0x01, 0x00 }; + Cvoldesc cv; + + memset(v, 0, sizeof *v); + + for(i=16; i<24; i++) { + Creadblock(cd, &cv, i, sizeof cv); + if(memcmp(cv.magic, magic, sizeof magic) != 0) + continue; + if(cv.charset[0] != 0x25 || cv.charset[1] != 0x2F + || (cv.charset[2] != 0x40 && cv.charset[2] != 0x43 && cv.charset[2] != 0x45)) + continue; + break; + } + + if(i==24) { + werrstr("could not find Joliet SVD"); + return -1; + } + + if(little(cv.blocksize, 2) != Blocksize) { + werrstr("block size not %d", Blocksize); + return -1; + } + + cd->jolietsvd = i; + parsedesc(v, &cv, jolietstring); + + return parsedir(cd, &v->root, cv.rootdir, sizeof cv.rootdir, jolietstring); +} + +/* + * CD image buffering routines. + */ +void +Cputc(Cdimg *cd, int c) +{ + assert(Boffset(&cd->bwr) >= 16*Blocksize || c == 0); + +if(Boffset(&cd->bwr) == 0x9962) +if(c >= 256) abort(); + if(Bputc(&cd->bwr, c) < 0) + sysfatal("Bputc: %r"); + Bflush(&cd->brd); +} + +void +Cputnl(Cdimg *cd, ulong val, int size) +{ + switch(size) { + default: + sysfatal("bad size %d in bputnl", size); + case 2: + Cputc(cd, val); + Cputc(cd, val>>8); + break; + case 4: + Cputc(cd, val); + Cputc(cd, val>>8); + Cputc(cd, val>>16); + Cputc(cd, val>>24); + break; + } +} + +void +Cputnm(Cdimg *cd, ulong val, int size) +{ + switch(size) { + default: + sysfatal("bad size %d in bputnl", size); + case 2: + Cputc(cd, val>>8); + Cputc(cd, val); + break; + case 4: + Cputc(cd, val>>24); + Cputc(cd, val>>16); + Cputc(cd, val>>8); + Cputc(cd, val); + break; + } +} + +void +Cputn(Cdimg *cd, long val, int size) +{ + Cputnl(cd, val, size); + Cputnm(cd, val, size); +} + +/* + * ASCII/UTF string writing + */ +void +Crepeat(Cdimg *cd, int c, int n) +{ + while(n-- > 0) + Cputc(cd, c); +} + +void +Cputs(Cdimg *cd, char *s, int size) +{ + int n; + + if(s == nil) { + Crepeat(cd, ' ', size); + return; + } + + for(n=0; n<size && *s; n++) + Cputc(cd, *s++); + if(n<size) + Crepeat(cd, ' ', size-n); +} + +void +Cwrite(Cdimg *cd, void *buf, int n) +{ + assert(Boffset(&cd->bwr) >= 16*Blocksize); + + if(Bwrite(&cd->bwr, buf, n) != n) + sysfatal("Bwrite: %r"); + Bflush(&cd->brd); +} + +void +Cputr(Cdimg *cd, Rune r) +{ + Cputc(cd, r>>8); + Cputc(cd, r); +} + +void +Crepeatr(Cdimg *cd, Rune r, int n) +{ + int i; + + for(i=0; i<n; i++) + Cputr(cd, r); +} + +void +Cputrs(Cdimg *cd, Rune *s, int osize) +{ + int n, size; + + size = osize/2; + if(s == nil) + Crepeatr(cd, (Rune)' ', size); + else { + for(n=0; *s && n<size; n++) + Cputr(cd, *s++); + if(n<size) + Crepeatr(cd, ' ', size-n); + } + if(osize&1) + Cputc(cd, 0); /* what else can we do? */ +} + +void +Cputrscvt(Cdimg *cd, char *s, int size) +{ + Rune r[256]; + + strtorune(r, s); + Cputrs(cd, strtorune(r, s), size); +} + +void +Cpadblock(Cdimg *cd) +{ + int n; + ulong nb; + + n = Blocksize - (Boffset(&cd->bwr) % Blocksize); + if(n != Blocksize) + Crepeat(cd, 0, n); + + nb = Boffset(&cd->bwr)/Blocksize; + assert(nb != 0); + if(nb > cd->nextblock) + cd->nextblock = nb; +} + +void +Cputdate(Cdimg *cd, ulong ust) +{ + Tm *tm; + + if(ust == 0) { + Crepeat(cd, 0, 7); + return; + } + tm = gmtime(ust); + Cputc(cd, tm->year); + Cputc(cd, tm->mon+1); + Cputc(cd, tm->mday); + Cputc(cd, tm->hour); + Cputc(cd, tm->min); + Cputc(cd, tm->sec); + Cputc(cd, 0); +} + +void +Cputdate1(Cdimg *cd, ulong ust) +{ + Tm *tm; + char str[20]; + + if(ust == 0) { + Crepeat(cd, '0', 16); + Cputc(cd, 0); + return; + } + tm = gmtime(ust); + sprint(str, "%.4d%.2d%.2d%.2d%.2d%.4d", + tm->year+1900, + tm->mon+1, + tm->mday, + tm->hour, + tm->min, + tm->sec*100); + Cputs(cd, str, 16); + Cputc(cd, 0); +} + +void +Cwseek(Cdimg *cd, vlong offset) +{ + Bseek(&cd->bwr, offset, 0); +} + +uvlong +Cwoffset(Cdimg *cd) +{ + return Boffset(&cd->bwr); +} + +void +Cwflush(Cdimg *cd) +{ + Bflush(&cd->bwr); +} + +uvlong +Croffset(Cdimg *cd) +{ + return Boffset(&cd->brd); +} + +void +Crseek(Cdimg *cd, vlong offset) +{ + Bseek(&cd->brd, offset, 0); +} + +int +Cgetc(Cdimg *cd) +{ + int c; + + Cwflush(cd); + if((c = Bgetc(&cd->brd)) == Beof) { + fprint(2, "getc at %llud\n", Croffset(cd)); + assert(0); + //sysfatal("Bgetc: %r"); + } + return c; +} + +void +Cread(Cdimg *cd, void *buf, int n) +{ + Cwflush(cd); + if(Bread(&cd->brd, buf, n) != n) + sysfatal("Bread: %r"); +} + +char* +Crdline(Cdimg *cd, int c) +{ + Cwflush(cd); + return Brdline(&cd->brd, c); +} + +int +Clinelen(Cdimg *cd) +{ + return Blinelen(&cd->brd); +} + diff --git a/sys/src/cmd/disk/9660/conform.c b/sys/src/cmd/disk/9660/conform.c new file mode 100755 index 000000000..df1986bfa --- /dev/null +++ b/sys/src/cmd/disk/9660/conform.c @@ -0,0 +1,141 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> +#include <ctype.h> +#include "iso9660.h" + +/* + * We keep an array sorted by bad atom pointer. + * The theory is that since we don't free memory very often, + * the array will be mostly sorted already and insertions will + * usually be near the end, so we won't spend much time + * keeping it sorted. + */ + +/* + * Binary search a Tx list. + * If no entry is found, return a pointer to + * where a new such entry would go. + */ +static Tx* +txsearch(char *atom, Tx *t, int n) +{ + while(n > 0) { + if(atom < t[n/2].bad) + n = n/2; + else if(atom > t[n/2].bad) { + t += n/2+1; + n -= (n/2+1); + } else + return &t[n/2]; + } + return t; +} + +void +addtx(char *b, char *g) +{ + Tx *t; + Conform *c; + + if(map == nil) + map = emalloc(sizeof(*map)); + c = map; + + if(c->nt%32 == 0) + c->t = erealloc(c->t, (c->nt+32)*sizeof(c->t[0])); + t = txsearch(b, c->t, c->nt); + if(t < c->t+c->nt && t->bad == b) { + fprint(2, "warning: duplicate entry for %s in _conform.map\n", b); + return; + } + + if(t != c->t+c->nt) + memmove(t+1, t, (c->t+c->nt - t)*sizeof(Tx)); + t->bad = b; + t->good = g; + c->nt++; +} + +char* +conform(char *s, int isdir) +{ + Tx *t; + char buf[10], *g; + Conform *c; + + c = map; + s = atom(s); + if(c){ + t = txsearch(s, c->t, c->nt); + if(t < c->t+c->nt && t->bad == s) + return t->good; + } + + sprint(buf, "%c%.6d", isdir ? 'D' : 'F', c ? c->nt : 0); + g = atom(buf); + addtx(s, g); + return g; +} + +#ifdef NOTUSED +static int +isalldigit(char *s) +{ + while(*s) + if(!isdigit(*s++)) + return 0; + return 1; +} +#endif + +static int +goodcmp(const void *va, const void *vb) +{ + Tx *a, *b; + + a = (Tx*)va; + b = (Tx*)vb; + return strcmp(a->good, b->good); +} + +static int +badatomcmp(const void *va, const void *vb) +{ + Tx *a, *b; + + a = (Tx*)va; + b = (Tx*)vb; + if(a->good < b->good) + return -1; + if(a->good > b->good) + return 1; + return 0; +} + +void +wrconform(Cdimg *cd, int n, ulong *pblock, uvlong *plength) +{ + char buf[1024]; + int i; + Conform *c; + + c = map; + *pblock = cd->nextblock; + if(c==nil || n==c->nt){ + *plength = 0; + return; + } + + Cwseek(cd, (vlong)cd->nextblock * Blocksize); + qsort(c->t, c->nt, sizeof(c->t[0]), goodcmp); + for(i=n; i<c->nt; i++) { + snprint(buf, sizeof buf, "%s %s\n", c->t[i].good, c->t[i].bad); + Cwrite(cd, buf, strlen(buf)); + } + qsort(c->t, c->nt, sizeof(c->t[0]), badatomcmp); + *plength = Cwoffset(cd) - (vlong)*pblock * Blocksize; + chat("write _conform.map at %lud+%llud\n", *pblock, *plength); + Cpadblock(cd); +} diff --git a/sys/src/cmd/disk/9660/direc.c b/sys/src/cmd/disk/9660/direc.c new file mode 100755 index 000000000..87b56034f --- /dev/null +++ b/sys/src/cmd/disk/9660/direc.c @@ -0,0 +1,222 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> + +#include "iso9660.h" + +void +mkdirec(Direc *direc, XDir *d) +{ + memset(direc, 0, sizeof(Direc)); + direc->name = atom(d->name); + direc->uid = atom(d->uid); + direc->gid = atom(d->gid); + direc->uidno = d->uidno; + direc->gidno = d->gidno; + direc->mode = d->mode; + direc->length = d->length; + direc->mtime = d->mtime; + direc->atime = d->atime; + direc->ctime = d->ctime; + direc->symlink = d->symlink; +} + +static int +strecmp(char *a, char *ea, char *b) +{ + int r; + + if((r = strncmp(a, b, ea-a)) != 0) + return r; + + if(b[ea-a] == '\0') + return 0; + return 1; +} + +/* + * Binary search a list of directories for the + * entry with name name. + * If no entry is found, return a pointer to + * where a new such entry would go. + */ +static Direc* +dbsearch(char *name, int nname, Direc *d, int n) +{ + int i; + + while(n > 0) { + i = strecmp(name, name+nname, d[n/2].name); + if(i < 0) + n = n/2; + else if(i > 0) { + d += n/2+1; + n -= (n/2+1); + } else + return &d[n/2]; + } + return d; +} + +/* + * Walk to name, starting at d. + */ +Direc* +walkdirec(Direc *d, char *name) +{ + char *p, *nextp, *slashp; + Direc *nd; + + for(p=name; p && *p; p=nextp) { + if((slashp = strchr(p, '/')) != nil) + nextp = slashp+1; + else + nextp = slashp = p+strlen(p); + + nd = dbsearch(p, slashp-p, d->child, d->nchild); + if(nd >= d->child+d->nchild || strecmp(p, slashp, nd->name) != 0) + return nil; + d = nd; + } + return d; +} + +/* + * Add the file ``name'' with attributes d to the + * directory ``root''. Name may contain multiple + * elements; all but the last must exist already. + * + * The child lists are kept sorted by utfname. + */ +Direc* +adddirec(Direc *root, char *name, XDir *d) +{ + char *p; + Direc *nd; + int off; + + if(name[0] == '/') + name++; + if((p = strrchr(name, '/')) != nil) { + *p = '\0'; + root = walkdirec(root, name); + if(root == nil) { + sysfatal("error in proto file: no entry for /%s but /%s/%s", name, name, p+1); + return nil; + } + *p = '/'; + p++; + } else + p = name; + + nd = dbsearch(p, strlen(p), root->child, root->nchild); + off = nd - root->child; + if(off < root->nchild && strcmp(nd->name, p) == 0) { + if ((d->mode & DMDIR) == 0) + fprint(2, "warning: proto lists %s twice\n", name); + return nil; + } + + if(root->nchild%Ndirblock == 0) { + root->child = erealloc(root->child, (root->nchild+Ndirblock)*sizeof(Direc)); + nd = root->child + off; + } + + memmove(nd+1, nd, (root->nchild - off)*sizeof(Direc)); + mkdirec(nd, d); + nd->name = atom(p); + root->nchild++; + return nd; +} + +/* + * Copy the tree src into dst. + */ +void +copydirec(Direc *dst, Direc *src) +{ + int i, n; + + *dst = *src; + + if((src->mode & DMDIR) == 0) + return; + + n = (src->nchild + Ndirblock - 1); + n -= n%Ndirblock; + dst->child = emalloc(n*sizeof(Direc)); + + n = dst->nchild; + for(i=0; i<n; i++) + copydirec(&dst->child[i], &src->child[i]); +} + +/* + * Turn the Dbadname flag on for any entries + * that have non-conforming names. + */ +static void +_checknames(Direc *d, int (*isbadname)(char*), int isroot) +{ + int i; + + if(!isroot && isbadname(d->name)) + d->flags |= Dbadname; + + if(strcmp(d->name, "_conform.map") == 0) + d->flags |= Dbadname; + + for(i=0; i<d->nchild; i++) + _checknames(&d->child[i], isbadname, 0); +} + +void +checknames(Direc *d, int (*isbadname)(char*)) +{ + _checknames(d, isbadname, 1); +} + +/* + * Set the names to conform to 8.3 + * by changing them to numbers. + * Plan 9 gets the right names from its + * own directory entry. + * + * We used to write a _conform.map file to translate + * names. Joliet should take care of most of the + * interoperability with other systems now. + */ +void +convertnames(Direc *d, char* (*cvt)(char*, char*)) +{ + int i; + char new[1024]; + + if(d->flags & Dbadname) + cvt(new, conform(d->name, d->mode & DMDIR)); + else + cvt(new, d->name); + d->confname = atom(new); + + for(i=0; i<d->nchild; i++) + convertnames(&d->child[i], cvt); +} + +/* + * Sort a directory with a given comparison function. + * After this is called on a tree, adddirec should not be, + * since the entries may no longer be sorted as adddirec expects. + */ +void +dsort(Direc *d, int (*cmp)(const void*, const void*)) +{ + int i, n; + + n = d->nchild; + qsort(d->child, n, sizeof(d[0]), cmp); + + for(i=0; i<n; i++) + dsort(&d->child[i], cmp); +} + diff --git a/sys/src/cmd/disk/9660/dump.c b/sys/src/cmd/disk/9660/dump.c new file mode 100755 index 000000000..02fd0b246 --- /dev/null +++ b/sys/src/cmd/disk/9660/dump.c @@ -0,0 +1,512 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> +#include <ctype.h> +#include "iso9660.h" + +static void +md5cd(Cdimg *cd, ulong block, ulong length, uchar *digest) +{ + int n; + uchar buf[Blocksize]; + DigestState *s; + + s = md5(nil, 0, nil, nil); + while(length > 0) { + n = length; + if(n > Blocksize) + n = Blocksize; + + Creadblock(cd, buf, block, n); + + md5(buf, n, nil, s); + + block++; + length -= n; + } + md5(nil, 0, digest, s); +} + +static Dumpdir* +mkdumpdir(char *name, uchar *md5, ulong block, ulong length) +{ + Dumpdir *d; + + assert(block != 0); + + d = emalloc(sizeof *d); + d->name = name; + memmove(d->md5, md5, sizeof d->md5); + d->block = block; + d->length = length; + + return d; +} + +static Dumpdir** +ltreewalkmd5(Dumpdir **l, uchar *md5) +{ + int i; + + while(*l) { + i = memcmp(md5, (*l)->md5, MD5dlen); + if(i < 0) + l = &(*l)->md5left; + else if(i == 0) + return l; + else + l = &(*l)->md5right; + } + return l; +} + +static Dumpdir** +ltreewalkblock(Dumpdir **l, ulong block) +{ + while(*l) { + if(block < (*l)->block) + l = &(*l)->blockleft; + else if(block == (*l)->block) + return l; + else + l = &(*l)->blockright; + } + return l; +} + +/* + * Add a particular file to our binary tree. + */ +static void +addfile(Cdimg *cd, Dump *d, char *name, Direc *dir) +{ + uchar md5[MD5dlen]; + Dumpdir **lblock; + + assert((dir->mode & DMDIR) == 0); + + if(dir->length == 0) + return; + + lblock = ltreewalkblock(&d->blockroot, dir->block); + if(*lblock != nil) { + if((*lblock)->length == dir->length) + return; + fprint(2, "block %lud length %lud %s %lud %s\n", dir->block, (*lblock)->length, (*lblock)->name, + dir->length, dir->name); + assert(0); + } + + md5cd(cd, dir->block, dir->length, md5); + if(chatty > 1) + fprint(2, "note file %.16H %lud (%s)\n", md5, dir->length, dir->name); + insertmd5(d, name, md5, dir->block, dir->length); +} + +void +insertmd5(Dump *d, char *name, uchar *md5, ulong block, ulong length) +{ + Dumpdir **lmd5; + Dumpdir **lblock; + + lblock = ltreewalkblock(&d->blockroot, block); + if(*lblock != nil) { + if((*lblock)->length == length) + return; + fprint(2, "block %lud length %lud %lud\n", block, (*lblock)->length, length); + assert(0); + } + + assert(length != 0); + *lblock = mkdumpdir(name, md5, block, length); + + lmd5 = ltreewalkmd5(&d->md5root, md5); + if(*lmd5 != nil) + fprint(2, "warning: data duplicated on CD\n"); + else + *lmd5 = *lblock; +} + +/* + * Fill all the children entries for a particular directory; + * all we care about is block, length, and whether it is a directory. + */ +void +readkids(Cdimg *cd, Direc *dir, char *(*cvt)(uchar*, int)) +{ + char *dot, *dotdot; + int m, n; + uchar buf[Blocksize], *ebuf, *p; + ulong b, nb; + Cdir *c; + Direc dx; + + assert(dir->mode & DMDIR); + + dot = atom("."); + dotdot = atom(".."); + ebuf = buf+Blocksize; + nb = (dir->length+Blocksize-1) / Blocksize; + + n = 0; + for(b=0; b<nb; b++) { + Creadblock(cd, buf, dir->block + b, Blocksize); + p = buf; + while(p < ebuf) { + c = (Cdir*)p; + if(c->len == 0) + break; + if(p+c->len > ebuf) + break; + if(parsedir(cd, &dx, p, ebuf-p, cvt) == 0 && dx.name != dot && dx.name != dotdot) + n++; + p += c->len; + } + } + + m = (n+Ndirblock-1)/Ndirblock * Ndirblock; + dir->child = emalloc(m*sizeof dir->child[0]); + dir->nchild = n; + + n = 0; + for(b=0; b<nb; b++) { + assert(n <= dir->nchild); + Creadblock(cd, buf, dir->block + b, Blocksize); + p = buf; + while(p < ebuf) { + c = (Cdir*)p; + if(c->len == 0) + break; + if(p+c->len > ebuf) + break; + if(parsedir(cd, &dx, p, ebuf-p, cvt) == 0 && dx.name != dot && dx.name != dotdot) { + assert(n < dir->nchild); + dir->child[n++] = dx; + } + p += c->len; + } + } +} + +/* + * Free the children. Make sure their children are free too. + */ +void +freekids(Direc *dir) +{ + int i; + + for(i=0; i<dir->nchild; i++) + assert(dir->child[i].nchild == 0); + + free(dir->child); + dir->child = nil; + dir->nchild = 0; +} + +/* + * Add a whole directory and all its children to our binary tree. + */ +static void +adddir(Cdimg *cd, Dump *d, Direc *dir) +{ + int i; + + readkids(cd, dir, isostring); + for(i=0; i<dir->nchild; i++) { + if(dir->child[i].mode & DMDIR) + adddir(cd, d, &dir->child[i]); + else + addfile(cd, d, atom(dir->name), &dir->child[i]); + } + freekids(dir); +} + +Dumpdir* +lookupmd5(Dump *d, uchar *md5) +{ + return *ltreewalkmd5(&d->md5root, md5); +} + +void +adddirx(Cdimg *cd, Dump *d, Direc *dir, int lev) +{ + int i; + Direc dd; + + if(lev == 2){ + dd = *dir; + adddir(cd, d, &dd); + return; + } + for(i=0; i<dir->nchild; i++) + adddirx(cd, d, &dir->child[i], lev+1); +} + +Dump* +dumpcd(Cdimg *cd, Direc *dir) +{ + Dump *d; + + d = emalloc(sizeof *d); + d->cd = cd; + adddirx(cd, d, dir, 0); + return d; +} + +/* +static ulong +minblock(Direc *root, int lev) +{ + int i; + ulong m, n; + + m = root->block; + for(i=0; i<root->nchild; i++) { + n = minblock(&root->child[i], lev-1); + if(m > n) + m = n; + } + return m; +} +*/ + +void +copybutname(Direc *d, Direc *s) +{ + Direc x; + + x = *d; + *d = *s; + d->name = x.name; + d->confname = x.confname; +} + +Direc* +createdumpdir(Direc *root, XDir *dir, char *utfname) +{ + char *p; + Direc *d; + + if(utfname[0]=='/') + sysfatal("bad dump name '%s'", utfname); + p = strchr(utfname, '/'); + if(p == nil || strchr(p+1, '/')) + sysfatal("bad dump name '%s'", utfname); + *p++ = '\0'; + if((d = walkdirec(root, utfname)) == nil) + d = adddirec(root, utfname, dir); + if(walkdirec(d, p)) + sysfatal("duplicate dump name '%s/%s'", utfname, p); + d = adddirec(d, p, dir); + return d; +} + +static void +rmdirec(Direc *d, Direc *kid) +{ + Direc *ekid; + + ekid = d->child+d->nchild; + assert(d->child <= kid && kid < ekid); + if(ekid != kid+1) + memmove(kid, kid+1, (ekid-(kid+1))*sizeof(*kid)); + d->nchild--; +} + +void +rmdumpdir(Direc *root, char *utfname) +{ + char *p; + Direc *d, *dd; + + if(utfname[0]=='/') + sysfatal("bad dump name '%s'", utfname); + p = strchr(utfname, '/'); + if(p == nil || strchr(p+1, '/')) + sysfatal("bad dump name '%s'", utfname); + *p++ = '\0'; + if((d = walkdirec(root, utfname)) == nil) + sysfatal("cannot remove %s/%s: %s does not exist", utfname, p, utfname); + p[-1] = '/'; + + if((dd = walkdirec(d, p)) == nil) + sysfatal("cannot remove %s: does not exist", utfname); + + rmdirec(d, dd); + if(d->nchild == 0) + rmdirec(root, d); +} + +char* +adddumpdir(Direc *root, ulong now, XDir *dir) +{ + char buf[40], *p; + int n; + Direc *dday, *dyear; + Tm tm; + + tm = *localtime(now); + + sprint(buf, "%d", tm.year+1900); + if((dyear = walkdirec(root, buf)) == nil) { + dyear = adddirec(root, buf, dir); + assert(dyear != nil); + } + + n = 0; + sprint(buf, "%.2d%.2d", tm.mon+1, tm.mday); + p = buf+strlen(buf); + while(walkdirec(dyear, buf)) + sprint(p, "%d", ++n); + + dday = adddirec(dyear, buf, dir); + assert(dday != nil); + + sprint(buf, "%s/%s", dyear->name, dday->name); +assert(walkdirec(root, buf)==dday); + return atom(buf); +} + +/* + * The dump directory tree is inferred from a linked list of special blocks. + * One block is written at the end of each dump. + * The blocks have the form + * + * plan 9 dump cd + * <dump-name> <dump-time> <next-block> <conform-block> <conform-length> \ + * <iroot-block> <iroot-length> <jroot-block> <jroot-length> + * + * If only the first line is present, this is the end of the chain. + */ +static char magic[] = "plan 9 dump cd\n"; +ulong +Cputdumpblock(Cdimg *cd) +{ + uvlong x; + + Cwseek(cd, (vlong)cd->nextblock * Blocksize); + x = Cwoffset(cd); + Cwrite(cd, magic, sizeof(magic)-1); + Cpadblock(cd); + return x/Blocksize; +} + +int +hasdump(Cdimg *cd) +{ + int i; + char buf[128]; + + for(i=16; i<24; i++) { + Creadblock(cd, buf, i, sizeof buf); + if(memcmp(buf, magic, sizeof(magic)-1) == 0) + return i; + } + return 0; +} + +Direc +readdumpdirs(Cdimg *cd, XDir *dir, char *(*cvt)(uchar*, int)) +{ + char buf[Blocksize]; + char *p, *q, *f[16]; + int i, nf; + ulong db, t; + Direc *nr, root; + XDir xd; + + mkdirec(&root, dir); + db = hasdump(cd); + xd = *dir; + for(;;){ + if(db == 0) + sysfatal("error in dump blocks"); + + Creadblock(cd, buf, db, sizeof buf); + if(memcmp(buf, magic, sizeof(magic)-1) != 0) + break; + p = buf+sizeof(magic)-1; + if(p[0] == '\0') + break; + if((q = strchr(p, '\n')) != nil) + *q = '\0'; + + nf = tokenize(p, f, nelem(f)); + i = 5; + if(nf < i || (cvt==jolietstring && nf < i+2)) + sysfatal("error in dump block %lud: nf=%d; p='%s'", db, nf, p); + nr = createdumpdir(&root, &xd, f[0]); + t = strtoul(f[1], 0, 0); + xd.mtime = xd.ctime = xd.atime = t; + db = strtoul(f[2], 0, 0); + if(cvt == jolietstring) + i += 2; + nr->block = strtoul(f[i], 0, 0); + nr->length = strtoul(f[i+1], 0, 0); + } + cd->nulldump = db; + return root; +} + +extern void addtx(char*, char*); + +static int +isalldigit(char *s) +{ + while(*s) + if(!isdigit(*s++)) + return 0; + return 1; +} + +void +readdumpconform(Cdimg *cd) +{ + char buf[Blocksize]; + char *p, *q, *f[10]; + int nf; + ulong cb, nc, db; + uvlong m; + + db = hasdump(cd); + assert(map==nil || map->nt == 0); + + for(;;){ + if(db == 0) + sysfatal("error0 in dump blocks"); + + Creadblock(cd, buf, db, sizeof buf); + if(memcmp(buf, magic, sizeof(magic)-1) != 0) + break; + p = buf+sizeof(magic)-1; + if(p[0] == '\0') + break; + if((q = strchr(p, '\n')) != nil) + *q = '\0'; + + nf = tokenize(p, f, nelem(f)); + if(nf < 5) + sysfatal("error0 in dump block %lud", db); + + db = strtoul(f[2], 0, 0); + cb = strtoul(f[3], 0, 0); + nc = strtoul(f[4], 0, 0); + + Crseek(cd, (vlong)cb * Blocksize); + m = (vlong)cb * Blocksize + nc; + while(Croffset(cd) < m && (p = Crdline(cd, '\n')) != nil){ + p[Clinelen(cd)-1] = '\0'; + if(tokenize(p, f, 2) != 2 || (f[0][0] != 'D' && f[0][0] != 'F') + || strlen(f[0]) != 7 || !isalldigit(f[0]+1)) + break; + + addtx(atom(f[1]), atom(f[0])); + } + } + if(map) + cd->nconform = map->nt; + else + cd->nconform = 0; +} diff --git a/sys/src/cmd/disk/9660/dump9660.c b/sys/src/cmd/disk/9660/dump9660.c new file mode 100755 index 000000000..83ae9807a --- /dev/null +++ b/sys/src/cmd/disk/9660/dump9660.c @@ -0,0 +1,417 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <disk.h> +#include <libsec.h> +#include "iso9660.h" + +ulong now; +int chatty; +int doabort; +int docolon; +int mk9660; +vlong dataoffset; +int blocksize; +Conform *map; + +static void addprotofile(char *new, char *old, Dir *d, void *a); +void usage(void); + +char *argv0; + +void +usage(void) +{ + if(mk9660) + fprint(2, "usage: disk/mk9660 [-D:] [-9cjr] [-b bootfile] [-o offset blocksize] [-p proto] [-s src] cdimage\n"); + else + fprint(2, "usage: disk/dump9660 [-D:] [-9cjr] [-m maxsize] [-n now] [-p proto] [-s src] cdimage\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int fix; + ulong block, newnull, cblock; + vlong maxsize; + uvlong length, clength; + char buf[256], *dumpname, *proto, *s, *src, *status; + Cdimg *cd; + Cdinfo info; + XDir dir; + Direc *iconform, idumproot, iroot, *jconform, jdumproot, jroot, *r; + Dump *dump; + + fix = 0; + status = nil; + memset(&info, 0, sizeof info); + proto = "/sys/lib/sysconfig/proto/allproto"; + src = "./"; + + info.volumename = atom("9CD"); + info.volumeset = atom("9VolumeSet"); + info.publisher = atom("9Publisher"); + info.preparer = atom("dump9660"); + info.application = atom("dump9660"); + info.flags = CDdump; + maxsize = 0; + mk9660 = 0; + fmtinstall('H', encodefmt); + + ARGBEGIN{ + case 'D': + chatty++; + break; + case 'M': + mk9660 = 1; + argv0 = "disk/mk9660"; + info.flags &= ~CDdump; + break; + case '9': + info.flags |= CDplan9; + break; + case ':': + docolon = 1; + break; + case 'a': + doabort = 1; + break; + case 'B': + info.flags |= CDbootnoemu; + /* fall through */ + case 'b': + if(!mk9660) + usage(); + info.flags |= CDbootable; + info.bootimage = EARGF(usage()); + break; + case 'c': + info.flags |= CDconform; + break; + case 'f': + fix = 1; + break; + case 'j': + info.flags |= CDjoliet; + break; + case 'n': + now = atoi(EARGF(usage())); + break; + case 'm': + maxsize = strtoull(EARGF(usage()), 0, 0); + break; + case 'o': + dataoffset = atoll(EARGF(usage())); + blocksize = atoi(EARGF(usage())); + if(blocksize%Blocksize) + sysfatal("bad block size %d -- must be multiple of 2048", blocksize); + blocksize /= Blocksize; + break; + case 'p': + proto = EARGF(usage()); + break; + case 'r': + info.flags |= CDrockridge; + break; + case 's': + src = EARGF(usage()); + break; + case 'v': + info.volumename = atom(EARGF(usage())); + break; + default: + usage(); + }ARGEND + + if(mk9660 && (fix || now || maxsize)) + usage(); + + if(argc != 1) + usage(); + + if(now == 0) + now = (ulong)time(0); + if(mk9660){ + if((cd = createcd(argv[0], info)) == nil) + sysfatal("cannot create '%s': %r", argv[0]); + }else{ + if((cd = opencd(argv[0], info)) == nil) + sysfatal("cannot open '%s': %r", argv[0]); + if(!(cd->flags & CDdump)) + sysfatal("not a dump cd"); + } + + /* create ISO9660/Plan 9 tree in memory */ + memset(&dir, 0, sizeof dir); + dir.name = atom(""); + dir.uid = atom("sys"); + dir.gid = atom("sys"); + dir.uidno = 0; + dir.gidno = 0; + dir.mode = DMDIR | 0755; + dir.mtime = now; + dir.atime = now; + dir.ctime = now; + + mkdirec(&iroot, &dir); + iroot.srcfile = src; + + /* + * Read new files into memory + */ + if(rdproto(proto, src, addprotofile, nil, &iroot) < 0) + sysfatal("rdproto: %r"); + + if(mk9660){ + dump = emalloc(sizeof *dump); + dumpname = nil; + }else{ + /* + * Read current dump tree and _conform.map. + */ + idumproot = readdumpdirs(cd, &dir, isostring); + readdumpconform(cd); + if(cd->flags & CDjoliet) + jdumproot = readdumpdirs(cd, &dir, jolietstring); + + if(fix){ + dumpname = nil; + cd->nextblock = cd->nulldump+1; + cd->nulldump = 0; + Cwseek(cd, (vlong)cd->nextblock * Blocksize); + goto Dofix; + } + + dumpname = adddumpdir(&idumproot, now, &dir); + /* note that we assume all names are conforming and thus sorted */ + if(cd->flags & CDjoliet) { + s = adddumpdir(&jdumproot, now, &dir); + if(s != dumpname) + sysfatal("dumpnames don't match %s %s", dumpname, s); + } + dump = dumpcd(cd, &idumproot); + cd->nextblock = cd->nulldump+1; + } + + /* + * Write new files, starting where the dump tree was. + * Must be done before creation of the Joliet tree so that + * blocks and lengths are correct. + */ + if(dataoffset > (vlong)cd->nextblock * Blocksize) + cd->nextblock = (dataoffset+Blocksize-1)/Blocksize; + Cwseek(cd, (vlong)cd->nextblock * Blocksize); + writefiles(dump, cd, &iroot); + + if(cd->bootimage){ + findbootimage(cd, &iroot); + Cupdatebootcat(cd); + } + + /* create Joliet tree */ + if(cd->flags & CDjoliet) + copydirec(&jroot, &iroot); + + if(info.flags & CDconform) { + checknames(&iroot, isbadiso9660); + convertnames(&iroot, struprcpy); + } else + convertnames(&iroot, (void *) strcpy); + +// isoabstract = findconform(&iroot, abstract); +// isobiblio = findconform(&iroot, biblio); +// isonotice = findconform(&iroot, notice); + + dsort(&iroot, isocmp); + + if(cd->flags & CDjoliet) { + // jabstract = findconform(&jroot, abstract); + // jbiblio = findconform(&jroot, biblio); + // jnotice = findconform(&jroot, notice); + + checknames(&jroot, isbadjoliet); + convertnames(&jroot, (void *) strcpy); + dsort(&jroot, jolietcmp); + } + + /* + * Write directories. + */ + writedirs(cd, &iroot, Cputisodir); + if(cd->flags & CDjoliet) + writedirs(cd, &jroot, Cputjolietdir); + + if(mk9660){ + cblock = 0; + clength = 0; + newnull = 0; + }else{ + /* + * Write incremental _conform.map block. + */ + wrconform(cd, cd->nconform, &cblock, &clength); + + /* jump here if we're just fixing up the cd */ +Dofix: + /* + * Write null dump header block; everything after this will be + * overwritten at the next dump. Because of this, it needs to be + * reconstructable. We reconstruct the _conform.map and dump trees + * from the header blocks in dump.c, and we reconstruct the path + * tables by walking the cd. + */ + newnull = Cputdumpblock(cd); + } + + /* + * Write _conform.map. + */ + dir.mode = 0444; + if(cd->flags & (CDconform|CDjoliet)) { + if(!mk9660 && cd->nconform == 0){ + block = cblock; + length = clength; + }else + wrconform(cd, 0, &block, &length); + + if(mk9660) +{ + idumproot = iroot; + jdumproot = jroot; + } + if(length) { + /* The ISO9660 name will get turned into uppercase when written. */ + if((iconform = walkdirec(&idumproot, "_conform.map")) == nil) + iconform = adddirec(&idumproot, "_conform.map", &dir); + jconform = nil; + if(cd->flags & CDjoliet) { + if((jconform = walkdirec(&jdumproot, "_conform.map")) == nil) + jconform = adddirec(&jdumproot, "_conform.map", &dir); + } + iconform->block = block; + iconform->length = length; + if(cd->flags & CDjoliet) { + jconform->block = block; + jconform->length = length; + } + } + if(mk9660) { + iroot = idumproot; + jroot = jdumproot; + } + } + + if(mk9660){ + /* + * Patch in root directories. + */ + setroot(cd, cd->iso9660pvd, iroot.block, iroot.length); + setvolsize(cd, cd->iso9660pvd, (vlong)cd->nextblock * Blocksize); + if(cd->flags & CDjoliet){ + setroot(cd, cd->jolietsvd, jroot.block, jroot.length); + setvolsize(cd, cd->jolietsvd, + (vlong)cd->nextblock * Blocksize); + } + }else{ + /* + * Write dump tree at end. We assume the name characters + * are all conforming, so everything is already sorted properly. + */ + convertnames(&idumproot, (info.flags & CDconform) ? (void *) struprcpy : (void *) strcpy); + if(cd->nulldump) { + r = walkdirec(&idumproot, dumpname); + assert(r != nil); + copybutname(r, &iroot); + } + if(cd->flags & CDjoliet) { + convertnames(&jdumproot, (void *) strcpy); + if(cd->nulldump) { + r = walkdirec(&jdumproot, dumpname); + assert(r != nil); + copybutname(r, &jroot); + } + } + + writedumpdirs(cd, &idumproot, Cputisodir); + if(cd->flags & CDjoliet) + writedumpdirs(cd, &jdumproot, Cputjolietdir); + + /* + * Patch in new root directory entry. + */ + setroot(cd, cd->iso9660pvd, idumproot.block, idumproot.length); + setvolsize(cd, cd->iso9660pvd, (vlong)cd->nextblock * Blocksize); + if(cd->flags & CDjoliet){ + setroot(cd, cd->jolietsvd, jdumproot.block, jdumproot.length); + setvolsize(cd, cd->jolietsvd, + (vlong)cd->nextblock * Blocksize); + } + } + writepathtables(cd); + + if(!mk9660){ + /* + * If we've gotten too big, truncate back to what we started with, + * fix up the cd, and exit with a non-zero status. + */ + Cwflush(cd); + if(cd->nulldump && maxsize && Cwoffset(cd) > maxsize){ + fprint(2, "too big; writing old tree back\n"); + status = "cd too big; aborted"; + + rmdumpdir(&idumproot, dumpname); + rmdumpdir(&jdumproot, dumpname); + + cd->nextblock = cd->nulldump+1; + cd->nulldump = 0; + Cwseek(cd, (vlong)cd->nextblock * Blocksize); + goto Dofix; + } + + /* + * Write old null header block; this commits all our changes. + */ + if(cd->nulldump){ + Cwseek(cd, (vlong)cd->nulldump * Blocksize); + sprint(buf, "plan 9 dump cd\n"); + sprint(buf+strlen(buf), "%s %lud %lud %lud %llud %lud %lud", + dumpname, now, newnull, cblock, clength, + iroot.block, iroot.length); + if(cd->flags & CDjoliet) + sprint(buf+strlen(buf), " %lud %lud", + jroot.block, jroot.length); + strcat(buf, "\n"); + Cwrite(cd, buf, strlen(buf)); + Cpadblock(cd); + Cwflush(cd); + } + } + fdtruncate(cd->fd, (vlong)cd->nextblock * Blocksize); + exits(status); +} + +static void +addprotofile(char *new, char *old, Dir *d, void *a) +{ + char *name, *p; + Direc *direc; + XDir xd; + + dirtoxdir(&xd, d); + name = nil; + if(docolon && strchr(new, ':')) { + name = emalloc(strlen(new)+1); + strcpy(name, new); + while((p=strchr(name, ':'))) + *p=' '; + new = name; + } + if((direc = adddirec((Direc*)a, new, &xd))) { + direc->srcfile = atom(old); + + // BUG: abstract, biblio, notice + } + if(name) + free(name); +} diff --git a/sys/src/cmd/disk/9660/ichar.c b/sys/src/cmd/disk/9660/ichar.c new file mode 100755 index 000000000..35b0c0564 --- /dev/null +++ b/sys/src/cmd/disk/9660/ichar.c @@ -0,0 +1,206 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> +#include <ctype.h> + +#include "iso9660.h" + +/* + * ISO 9660 file names must be uppercase, digits, or underscore. + * We use lowercase, digits, and underscore, translating lower to upper + * in mkisostring, and upper to lower in isostring. + * Files with uppercase letters in their names are thus nonconforming. + * Conforming files also must have a basename + * at most 8 letters and at most one suffix of at most 3 letters. + */ +char* +isostring(uchar *buf, int len) +{ + char *p, *q; + + p = emalloc(len+1); + memmove(p, buf, len); + p[len] = '\0'; + while(len > 0 && p[len-1] == ' ') + p[--len] = '\0'; + for(q=p; *q; q++) + *q = tolower(*q); + + q = atom(p); + free(p); + return q; +} + +int +isisofrog(char c) +{ + if(c >= '0' && c <= '9') + return 0; + if(c >= 'a' && c <= 'z') + return 0; + if(c == '_') + return 0; + + return 1; +} + +int +isbadiso9660(char *s) +{ + char *p, *q; + int i; + + if((p = strchr(s, '.')) != nil) { + if(p-s > 8) + return 1; + for(q=s; q<p; q++) + if(isisofrog(*q)) + return 1; + if(strlen(p+1) > 3) + return 1; + for(q=p+1; *q; q++) + if(isisofrog(*q)) + return 1; + } else { + if(strlen(s) > 8) + return 1; + for(q=s; *q; q++) + if(isisofrog(*q)) + return 1; + + /* + * we rename files of the form [FD]dddddd + * so they don't interfere with us. + */ + if(strlen(s) == 7 && (s[0] == 'D' || s[0] == 'F')) { + for(i=1; i<7; i++) + if(s[i] < '0' || s[i] > '9') + break; + if(i == 7) + return 1; + } + } + return 0; +} + +/* + * ISO9660 name comparison + * + * The standard algorithm is as follows: + * Take the filenames without extensions, pad the shorter with 0x20s (spaces), + * and do strcmp. If they are equal, go on. + * Take the extensions, pad the shorter with 0x20s (spaces), + * and do strcmp. If they are equal, go on. + * Compare the version numbers. + * + * Since Plan 9 names are not allowed to contain characters 0x00-0x1F, + * the padded comparisons are equivalent to using strcmp directly. + * We still need to handle the base and extension differently, + * so that .foo sorts before !foo.foo. + */ +int +isocmp(const void *va, const void *vb) +{ + int i; + char s1[32], s2[32], *b1, *b2, *e1, *e2; + const Direc *a, *b; + + a = va; + b = vb; + + strecpy(s1, s1+sizeof s1, a->confname); + b1 = s1; + strecpy(s2, s2+sizeof s2, b->confname); + b2 = s2; + if((e1 = strchr(b1, '.')) != nil) + *e1++ = '\0'; + else + e1 = ""; + if((e2 = strchr(b2, '.')) != nil) + *e2++ = '\0'; + else + e2 = ""; + + if((i = strcmp(b1, b2)) != 0) + return i; + + return strcmp(e1, e2); +} + +static char* +mkisostring(char *isobuf, int n, char *s) +{ + char *p, *q, *eq; + + eq = isobuf+n; + for(p=s, q=isobuf; *p && q < eq; p++) + if('a' <= *p && *p <= 'z') + *q++ = *p+'A'-'a'; + else + *q++ = *p; + + while(q < eq) + *q++ = ' '; + + return isobuf; +} + +void +Cputisopvd(Cdimg *cd, Cdinfo info) +{ + char buf[130]; + + Cputc(cd, 1); /* primary volume descriptor */ + Cputs(cd, "CD001", 5); /* standard identifier */ + Cputc(cd, 1); /* volume descriptor version */ + Cputc(cd, 0); /* unused */ + + assert(~info.flags & (CDplan9|CDrockridge)); + + /* system identifier */ + strcpy(buf, ""); + if(info.flags & CDplan9) + strcat(buf, "plan 9 "); + if(info.flags & CDrockridge) + strcat(buf, "rrip "); + if(info.flags & CDbootable) + strcat(buf, "boot "); + if(info.flags & CDconform) + strcat(buf, "iso9660"); + else + strcat(buf, "utf8"); + + struprcpy(buf, buf); + Cputs(cd, buf, 32); + + Cputs(cd, mkisostring(buf, 32, info.volumename), 32); /* volume identifier */ + + Crepeat(cd, 0, 8); /* unused */ + Cputn(cd, 0, 4); /* volume space size */ + Crepeat(cd, 0, 32); /* unused */ + Cputn(cd, 1, 2); /* volume set size */ + Cputn(cd, 1, 2); /* volume sequence number */ + Cputn(cd, Blocksize, 2); /* logical block size */ + Cputn(cd, 0, 4); /* path table size */ + Cputnl(cd, 0, 4); /* location of Lpath */ + Cputnl(cd, 0, 4); /* location of optional Lpath */ + Cputnm(cd, 0, 4); /* location of Mpath */ + Cputnm(cd, 0, 4); /* location of optional Mpath */ + Cputisodir(cd, nil, DTroot, 1, Cwoffset(cd)); /* root directory */ + + Cputs(cd, mkisostring(buf, 128, info.volumeset), 128); /* volume set identifier */ + Cputs(cd, mkisostring(buf, 128, info.publisher), 128); /* publisher identifier */ + Cputs(cd, mkisostring(buf, 128, info.preparer), 128); /* data preparer identifier */ + Cputs(cd, mkisostring(buf, 128, info.application), 128); /* application identifier */ + + Cputs(cd, "", 37); /* copyright notice */ + Cputs(cd, "", 37); /* abstract */ + Cputs(cd, "", 37); /* bibliographic file */ + Cputdate1(cd, now); /* volume creation date */ + Cputdate1(cd, now); /* volume modification date */ + Cputdate1(cd, 0); /* volume expiration date */ + Cputdate1(cd, 0); /* volume effective date */ + Cputc(cd, 1); /* file structure version */ + Cpadblock(cd); +} diff --git a/sys/src/cmd/disk/9660/iso9660.h b/sys/src/cmd/disk/9660/iso9660.h new file mode 100755 index 000000000..edd0c7df2 --- /dev/null +++ b/sys/src/cmd/disk/9660/iso9660.h @@ -0,0 +1,426 @@ +/* + * iso9660.h + * + * Routines and data structures to support reading and writing ISO 9660 CD images. + * See the ISO 9660 or ECMA 119 standards. + * + * Also supports Rock Ridge extensions for long file names and Unix stuff. + * Also supports Microsoft's Joliet extensions for Unicode and long file names. + * Also supports El Torito bootable CD spec. + */ + +typedef struct Cdimg Cdimg; +typedef struct Cdinfo Cdinfo; +typedef struct Conform Conform; +typedef struct Direc Direc; +typedef struct Dumproot Dumproot; +typedef struct Voldesc Voldesc; +typedef struct XDir XDir; + +#ifndef CHLINK +#define CHLINK 0 +#endif + +struct XDir { + char *name; + char *uid; + char *gid; + char *symlink; + ulong uidno; /* Numeric uid */ + ulong gidno; /* Numeric gid */ + + ulong mode; + ulong atime; + ulong mtime; + ulong ctime; + + vlong length; +}; + +/* + * A directory entry in a ISO9660 tree. + * The extra data (uid, etc.) here is put into the system use areas. + */ +struct Direc { + char *name; /* real name */ + char *confname; /* conformant name */ + char *srcfile; /* file to copy onto the image */ + + ulong block; + ulong length; + int flags; + + char *uid; + char *gid; + char *symlink; + ulong mode; + long atime; + long ctime; + long mtime; + + ulong uidno; + ulong gidno; + + Direc *child; + int nchild; +}; +enum { /* Direc flags */ + Dbadname = 1<<0, /* Non-conformant name */ +}; + +/* + * Data found in a volume descriptor. + */ +struct Voldesc { + char *systemid; + char *volumeset; + char *publisher; + char *preparer; + char *application; + + /* file names for various parameters */ + char *abstract; + char *biblio; + char *notice; + + /* path table */ + ulong pathsize; + ulong lpathloc; + ulong mpathloc; + + /* root of file tree */ + Direc root; +}; + +/* + * An ISO9660 CD image. Various parameters are kept in memory but the + * real image file is opened for reading and writing on fd. + * + * The bio buffers brd and bwr moderate reading and writing to the image. + * The routines we use are careful to flush one before or after using the other, + * as necessary. + */ +struct Cdimg { + char *file; + int fd; + ulong dumpblock; + ulong nextblock; + ulong iso9660pvd; + ulong jolietsvd; + ulong pathblock; + uvlong rrcontin; /* rock ridge continuation offset */ + ulong nulldump; /* next dump block */ + ulong nconform; /* number of conform entries written already */ + uvlong bootcatptr; + ulong bootcatblock; + uvlong bootimageptr; + Direc *bootdirec; + char *bootimage; + + Biobuf brd; + Biobuf bwr; + + int flags; + + Voldesc iso; + Voldesc joliet; +}; +enum { /* Cdimg->flags, Cdinfo->flags */ + CDjoliet = 1<<0, + CDplan9 = 1<<1, + CDconform = 1<<2, + CDrockridge = 1<<3, + CDnew = 1<<4, + CDdump = 1<<5, + CDbootable = 1<<6, + CDbootnoemu = 1<<7, +}; + +typedef struct Tx Tx; +struct Tx { + char *bad; /* atoms */ + char *good; +}; + +struct Conform { + Tx *t; + int nt; /* delta = 32 */ +}; + +struct Cdinfo { + int flags; + + char *volumename; + + char *volumeset; + char *publisher; + char *preparer; + char *application; + char *bootimage; +}; + +//enum { +// Blocklen = 2048, /* unused */ +//}; + +/* + * This is a doubly binary tree. + * We have a tree keyed on the MD5 values + * as well as a tree keyed on the block numbers. + */ +typedef struct Dump Dump; +typedef struct Dumpdir Dumpdir; + +struct Dump { + Cdimg *cd; + Dumpdir *md5root; + Dumpdir *blockroot; +}; + +struct Dumpdir { + char *name; + uchar md5[MD5dlen]; + ulong block; + ulong length; + Dumpdir *md5left; + Dumpdir *md5right; + Dumpdir *blockleft; + Dumpdir *blockright; +}; + +struct Dumproot { + char *name; + int nkid; + Dumproot *kid; + Direc root; + Direc jroot; +}; + +/* + * ISO9660 on-CD structures. + */ +typedef struct Cdir Cdir; +typedef struct Cpath Cpath; +typedef struct Cvoldesc Cvoldesc; + +/* a volume descriptor block */ +struct Cvoldesc { + uchar magic[8]; /* 0x01, "CD001", 0x01, 0x00 */ + uchar systemid[32]; /* system identifier */ + uchar volumeid[32]; /* volume identifier */ + uchar unused[8]; /* character set in secondary desc */ + uchar volsize[8]; /* volume size */ + uchar charset[32]; + uchar volsetsize[4]; /* volume set size = 1 */ + uchar volseqnum[4]; /* volume sequence number = 1 */ + uchar blocksize[4]; /* logical block size */ + uchar pathsize[8]; /* path table size */ + uchar lpathloc[4]; /* Lpath */ + uchar olpathloc[4]; /* optional Lpath */ + uchar mpathloc[4]; /* Mpath */ + uchar ompathloc[4]; /* optional Mpath */ + uchar rootdir[34]; /* directory entry for root */ + uchar volumeset[128]; /* volume set identifier */ + uchar publisher[128]; + uchar preparer[128]; /* data preparer identifier */ + uchar application[128]; /* application identifier */ + uchar notice[37]; /* copyright notice file */ + uchar abstract[37]; /* abstract file */ + uchar biblio[37]; /* bibliographic file */ + uchar cdate[17]; /* creation date */ + uchar mdate[17]; /* modification date */ + uchar xdate[17]; /* expiration date */ + uchar edate[17]; /* effective date */ + uchar fsvers; /* file system version = 1 */ +}; + +/* a directory entry */ +struct Cdir { + uchar len; + uchar xlen; + uchar dloc[8]; + uchar dlen[8]; + uchar date[7]; + uchar flags; + uchar unitsize; + uchar gapsize; + uchar volseqnum[4]; + uchar namelen; + uchar name[1]; /* chumminess */ +}; + +/* a path table entry */ +struct Cpath { + uchar namelen; + uchar xlen; + uchar dloc[4]; + uchar parent[2]; + uchar name[1]; /* chumminess */ +}; + +enum { /* Rockridge flags */ + RR_PX = 1<<0, + RR_PN = 1<<1, + RR_SL = 1<<2, + RR_NM = 1<<3, + RR_CL = 1<<4, + RR_PL = 1<<5, + RR_RE = 1<<6, + RR_TF = 1<<7, +}; + +enum { /* CputrripTF type argument */ + TFcreation = 1<<0, + TFmodify = 1<<1, + TFaccess = 1<<2, + TFattributes = 1<<3, + TFbackup = 1<<4, + TFexpiration = 1<<5, + TFeffective = 1<<6, + TFlongform = 1<<7, +}; + +enum { /* CputrripNM flag types */ + NMcontinue = 1<<0, + NMcurrent = 1<<1, + NMparent = 1<<2, + NMroot = 1<<3, + NMvolroot = 1<<4, + NMhost = 1<<5, +}; + +/* boot.c */ +void Cputbootvol(Cdimg*); +void Cputbootcat(Cdimg*); +void Cupdatebootvol(Cdimg*); +void Cupdatebootcat(Cdimg*); +void findbootimage(Cdimg*, Direc*); + +/* cdrdwr.c */ +Cdimg *createcd(char*, Cdinfo); +Cdimg *opencd(char*, Cdinfo); +void Creadblock(Cdimg*, void*, ulong, ulong); +ulong big(void*, int); +ulong little(void*, int); +int parsedir(Cdimg*, Direc*, uchar*, int, char *(*)(uchar*, int)); +void setroot(Cdimg*, ulong, ulong, ulong); +void setvolsize(Cdimg*, ulong, ulong); +void setpathtable(Cdimg*, ulong, ulong, ulong, ulong); +void Cputc(Cdimg*, int); +void Cputnl(Cdimg*, ulong, int); +void Cputnm(Cdimg*, ulong, int); +void Cputn(Cdimg*, long, int); +void Crepeat(Cdimg*, int, int); +void Cputs(Cdimg*, char*, int); +void Cwrite(Cdimg*, void*, int); +void Cputr(Cdimg*, Rune); +void Crepeatr(Cdimg*, Rune, int); +void Cputrs(Cdimg*, Rune*, int); +void Cputrscvt(Cdimg*, char*, int); +void Cpadblock(Cdimg*); +void Cputdate(Cdimg*, ulong); +void Cputdate1(Cdimg*, ulong); +void Cread(Cdimg*, void*, int); +void Cwflush(Cdimg*); +void Cwseek(Cdimg*, vlong); +uvlong Cwoffset(Cdimg*); +uvlong Croffset(Cdimg*); +int Cgetc(Cdimg*); +void Crseek(Cdimg*, vlong); +char *Crdline(Cdimg*, int); +int Clinelen(Cdimg*); + +/* conform.c */ +void rdconform(Cdimg*); +char *conform(char*, int); +void wrconform(Cdimg*, int, ulong*, uvlong*); + +/* direc.c */ +void mkdirec(Direc*, XDir*); +Direc *walkdirec(Direc*, char*); +Direc *adddirec(Direc*, char*, XDir*); +void copydirec(Direc*, Direc*); +void checknames(Direc*, int (*)(char*)); +void convertnames(Direc*, char* (*)(char*, char*)); +void dsort(Direc*, int (*)(const void*, const void*)); +void setparents(Direc*); + +/* dump.c */ +ulong Cputdumpblock(Cdimg*); +int hasdump(Cdimg*); +Dump *dumpcd(Cdimg*, Direc*); +Dumpdir *lookupmd5(Dump*, uchar*); +void insertmd5(Dump*, char*, uchar*, ulong, ulong); + +Direc readdumpdirs(Cdimg*, XDir*, char*(*)(uchar*,int)); +char *adddumpdir(Direc*, ulong, XDir*); +void copybutname(Direc*, Direc*); + +void readkids(Cdimg*, Direc*, char*(*)(uchar*,int)); +void freekids(Direc*); +void readdumpconform(Cdimg*); +void rmdumpdir(Direc*, char*); + +/* ichar.c */ +char *isostring(uchar*, int); +int isbadiso9660(char*); +int isocmp(const void*, const void*); +int isisofrog(char); +void Cputisopvd(Cdimg*, Cdinfo); + +/* jchar.c */ +char *jolietstring(uchar*, int); +int isbadjoliet(char*); +int jolietcmp(const void*, const void*); +int isjolietfrog(Rune); +void Cputjolietsvd(Cdimg*, Cdinfo); + +/* path.c */ +void writepathtables(Cdimg*); + +/* util.c */ +void *emalloc(ulong); +void *erealloc(void*, ulong); +char *atom(char*); +char *struprcpy(char*, char*); +int chat(char*, ...); + +/* unix.c, plan9.c */ +void dirtoxdir(XDir*, Dir*); +void fdtruncate(int, ulong); +long uidno(char*); +long gidno(char*); + +/* rune.c */ +Rune *strtorune(Rune*, char*); +Rune *runechr(Rune*, Rune); +int runecmp(Rune*, Rune*); + +/* sysuse.c */ +int Cputsysuse(Cdimg*, Direc*, int, int, int); + +/* write.c */ +void writefiles(Dump*, Cdimg*, Direc*); +void writedirs(Cdimg*, Direc*, int(*)(Cdimg*, Direc*, int, int, int)); +void writedumpdirs(Cdimg*, Direc*, int(*)(Cdimg*, Direc*, int, int, int)); +int Cputisodir(Cdimg*, Direc*, int, int, int); +int Cputjolietdir(Cdimg*, Direc*, int, int, int); +void Cputendvd(Cdimg*); + +enum { + Blocksize = 2048, + Ndirblock = 16, /* directory blocks allocated at once */ + + DTdot = 0, + DTdotdot, + DTiden, + DTroot, + DTrootdot, +}; + +extern ulong now; +extern Conform *map; +extern int chatty; +extern int docolon; +extern int mk9660; +extern int blocksize; diff --git a/sys/src/cmd/disk/9660/jchar.c b/sys/src/cmd/disk/9660/jchar.c new file mode 100755 index 000000000..c49da6351 --- /dev/null +++ b/sys/src/cmd/disk/9660/jchar.c @@ -0,0 +1,138 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> + +#include "iso9660.h" + +char* +jolietstring(uchar *buf, int len) +{ + char *p, *q; + int i; + Rune *rp; + + rp = emalloc(sizeof(Rune)*(len/2+1)); + p = emalloc(UTFmax*(len/2+1)); + + for(i=0; i<len/2; i++) + rp[i] = (buf[2*i]<<8) | buf[2*i+1]; + rp[i] = (Rune)'\0'; + + snprint(p, UTFmax*(len/2+1), "%S", rp); + q = atom(p); + free(p); + return q; +} + +/* + * Joliet name validity check + * + * Joliet names have length at most 128 bytes (64 runes), + * and cannot contain '*', '/', ':', ';', '?', or '\'. + */ +int +isjolietfrog(Rune r) +{ + return r==L'*' || r==L'/' || r==L':' + || r==';' || r=='?' || r=='\\'; +} + +int +isbadjoliet(char *s) +{ + Rune r[256], *p; + + if(utflen(s) > 64) + return 1; + strtorune(r, s); + for(p=r; *p; p++) + if(isjolietfrog(*p)) + return 1; + return 0; +} + +/* + * Joliet name comparison + * + * The standard algorithm is the ISO9660 algorithm but + * on the encoded Runes. Runes are encoded in big endian + * format, so we can just use runecmp. + * + * Padding is with zeros, but that still doesn't affect us. + */ + +static Rune emptystring[] = { (Rune)0 }; +int +jolietcmp(const void *va, const void *vb) +{ + int i; + Rune s1[256], s2[256], *b1, *b2, *e1, *e2; /*BUG*/ + const Direc *a, *b; + + a = va; + b = vb; + + b1 = strtorune(s1, a->confname); + b2 = strtorune(s2, b->confname); + if((e1 = runechr(b1, (Rune)'.')) != nil) + *e1++ = '\0'; + else + e1 = emptystring; + + if((e2 = runechr(b2, (Rune)'.')) != nil) + *e2++ = '\0'; + else + e2 = emptystring; + + if((i = runecmp(b1, b2)) != 0) + return i; + + return runecmp(e1, e2); +} + +/* + * Write a Joliet secondary volume descriptor. + */ +void +Cputjolietsvd(Cdimg *cd, Cdinfo info) +{ + Cputc(cd, 2); /* secondary volume descriptor */ + Cputs(cd, "CD001", 5); /* standard identifier */ + Cputc(cd, 1); /* volume descriptor version */ + Cputc(cd, 0); /* unused */ + + Cputrscvt(cd, "Joliet Plan 9", 32); /* system identifier */ + Cputrscvt(cd, info.volumename, 32); /* volume identifier */ + + Crepeat(cd, 0, 8); /* unused */ + Cputn(cd, 0, 4); /* volume space size */ + Cputc(cd, 0x25); /* escape sequences: UCS-2 Level 2 */ + Cputc(cd, 0x2F); + Cputc(cd, 0x43); + + Crepeat(cd, 0, 29); + Cputn(cd, 1, 2); /* volume set size */ + Cputn(cd, 1, 2); /* volume sequence number */ + Cputn(cd, Blocksize, 2); /* logical block size */ + Cputn(cd, 0, 4); /* path table size */ + Cputnl(cd, 0, 4); /* location of Lpath */ + Cputnl(cd, 0, 4); /* location of optional Lpath */ + Cputnm(cd, 0, 4); /* location of Mpath */ + Cputnm(cd, 0, 4); /* location of optional Mpath */ + Cputjolietdir(cd, nil, DTroot, 1, Cwoffset(cd)); /* root directory */ + Cputrscvt(cd, info.volumeset, 128); /* volume set identifier */ + Cputrscvt(cd, info.publisher, 128); /* publisher identifier */ + Cputrscvt(cd, info.preparer, 128); /* data preparer identifier */ + Cputrscvt(cd, info.application, 128); /* application identifier */ + Cputrscvt(cd, "", 37); /* copyright notice */ + Cputrscvt(cd, "", 37); /* abstract */ + Cputrscvt(cd, "", 37); /* bibliographic file */ + Cputdate1(cd, now); /* volume creation date */ + Cputdate1(cd, now); /* volume modification date */ + Cputdate1(cd, 0); /* volume expiration date */ + Cputdate1(cd, 0); /* volume effective date */ + Cputc(cd, 1); /* file structure version */ + Cpadblock(cd); +} + diff --git a/sys/src/cmd/disk/9660/mk9660.rc b/sys/src/cmd/disk/9660/mk9660.rc new file mode 100755 index 000000000..1824f8e74 --- /dev/null +++ b/sys/src/cmd/disk/9660/mk9660.rc @@ -0,0 +1,5 @@ +#!/bin/rc + +# the master copy of this file is /sys/src/cmd/disk/9660/mk9660.rc +# do NOT edit the copies in /*/bin/disk. +exec disk/dump9660 -M $* diff --git a/sys/src/cmd/disk/9660/mkfile b/sys/src/cmd/disk/9660/mkfile new file mode 100755 index 000000000..e7c11fecd --- /dev/null +++ b/sys/src/cmd/disk/9660/mkfile @@ -0,0 +1,43 @@ +</$objtype/mkfile + +TARG=dump9660 mk9660 + +OFILES= + +DFILES=\ + boot.$O\ + cdrdwr.$O\ + conform.$O\ + direc.$O\ + dump.$O\ + dump9660.$O\ + ichar.$O\ + jchar.$O\ + path.$O\ + plan9.$O\ + rune.$O\ + sysuse.$O\ + util.$O\ + write.$O\ + +HFILES=iso9660.h + +UPDATE=\ + mkfile\ + $HFILES\ + ${OFILES:%.$O=%.c}\ + ${DFILES:%.$O=%.c}\ + dump9660.c\ + mk9660.rc\ + ${TARG:%=/386/bin/disk/%}\ + +BIN=/$objtype/bin/disk +</sys/src/cmd/mkmany + +$O.dump9660: $DFILES + +mk9660.$O:V: + # nothing + +$O.mk9660: mk9660.rc + cp mk9660.rc $target diff --git a/sys/src/cmd/disk/9660/path.c b/sys/src/cmd/disk/9660/path.c new file mode 100755 index 000000000..5523afd7a --- /dev/null +++ b/sys/src/cmd/disk/9660/path.c @@ -0,0 +1,159 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> + +#include "iso9660.h" + +/* + * Add the requisite path tables to the CD image. + * They get put on the end once everything else is done. + * We use the path table itself as a queue in the breadth-first + * traversal of the tree. + * + * The only problem with this is that the path table does not + * store the lengths of the directories. So we keep an explicit + * map in an array in memory. + */ + +enum { + Big, + Little +}; + +static void +Crdpath(Cdimg *cd, Cpath *p) +{ + p->namelen = Cgetc(cd); + if(p->namelen == 0) { + Crseek(cd, (Croffset(cd)+Blocksize-1)/Blocksize * Blocksize); + p->namelen = Cgetc(cd); + assert(p->namelen != 0); + } + + p->xlen = Cgetc(cd); + assert(p->xlen == 0); /* sanity, might not be true if we start using the extended fields */ + + Cread(cd, p->dloc, 4); + Cread(cd, p->parent, 2); + p->name[0] = '\0'; + Crseek(cd, Croffset(cd)+p->namelen+p->xlen+(p->namelen&1)); /* skip name, ext data */ +} + +static void +writepath(Cdimg *cd, Cdir *c, int parent, int size) +{ +/* + DO NOT UNCOMMENT THIS CODE. + This commented-out code is here only so that no one comes + along and adds it later. + + The ISO 9660 spec is silent about whether path table entries + need to be padded so that they never cross block boundaries. + It would be reasonable to assume that they are like every other + data structure in the bloody spec; this code pads them out. + + Empirically, though, they're NOT padded. Windows NT and + derivatives are the only known current operating systems + that actually read these things. + + int l; + + l = 1+1+4+2+c->namelen; + if(Cwoffset(cd)/Blocksize != (Cwoffset(cd)+l)/Blocksize) + Cpadblock(cd); +*/ + Cputc(cd, c->namelen); + Cputc(cd, 0); + Cwrite(cd, c->dloc + (size==Little ? 0 : 4), 4); + (size==Little ? Cputnl : Cputnm)(cd, parent, 2); + Cwrite(cd, c->name, c->namelen); + if(c->namelen & 1) + Cputc(cd, 0); +} + +static ulong* +addlength(ulong *a, ulong x, int n) +{ + if(n%128==0) + a = erealloc(a, (n+128)*sizeof a[0]); + a[n] = x; + return a; +} + +static ulong +writepathtable(Cdimg *cd, ulong vdblock, int size) +{ + int rp, wp; + uchar buf[Blocksize]; + ulong bk, i, *len, n; + uvlong start, end, rdoff; + Cdir *c; + Cpath p; + + Creadblock(cd, buf, vdblock, Blocksize); + c = (Cdir*)(buf + offsetof(Cvoldesc, rootdir[0])); + + rp = 0; + wp = 0; + len = nil; + start = (vlong)cd->nextblock * Blocksize; + Cwseek(cd, start); + Crseek(cd, start); + writepath(cd, c, 1, size); + len = addlength(len, little(c->dlen, 4), wp); + wp++; + + while(rp < wp) { + Crdpath(cd, &p); + n = (len[rp]+Blocksize-1)/Blocksize; + rp++; + bk = (size==Big ? big : little)(p.dloc, 4); + rdoff = Croffset(cd); + for(i=0; i<n; i++) { + Creadblock(cd, buf, bk+i, Blocksize); + c = (Cdir*)buf; + if(i != 0 && c->namelen == 1 && c->name[0] == '\0') + break; /* hit another directory; stop */ + while(c->len && c->namelen && + (uchar*)c + c->len < buf + Blocksize) { + if(c->flags & 0x02 && + (c->namelen > 1 || c->name[0] > '\001')) { + /* directory */ + writepath(cd, c, rp, size); + len = addlength(len, little(c->dlen, 4), wp); + wp++; + } + c = (Cdir*)((uchar*)c+c->len); + } + } + Crseek(cd, rdoff); + } + end = Cwoffset(cd); + Cpadblock(cd); + return end-start; +} + + +static void +writepathtablepair(Cdimg *cd, ulong vdblock) +{ + ulong bloc, lloc, sz, sz2; + + lloc = cd->nextblock; + sz = writepathtable(cd, vdblock, Little); + bloc = cd->nextblock; + sz2 = writepathtable(cd, vdblock, Big); + assert(sz == sz2); + setpathtable(cd, vdblock, sz, lloc, bloc); +} + +void +writepathtables(Cdimg *cd) +{ + cd->pathblock = cd->nextblock; + + writepathtablepair(cd, cd->iso9660pvd); + if(cd->flags & CDjoliet) + writepathtablepair(cd, cd->jolietsvd); +} diff --git a/sys/src/cmd/disk/9660/plan9.c b/sys/src/cmd/disk/9660/plan9.c new file mode 100755 index 000000000..25f04e921 --- /dev/null +++ b/sys/src/cmd/disk/9660/plan9.c @@ -0,0 +1,27 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> +#include <disk.h> +#include "iso9660.h" + +void +dirtoxdir(XDir *xd, Dir *d) +{ + xd->name = atom(d->name); + xd->uid = atom(d->uid); + xd->gid = atom(d->gid); + xd->uidno = 0; + xd->gidno = 0; + xd->mode = d->mode; + xd->atime = d->atime; + xd->mtime = d->mtime; + xd->ctime = 0; + xd->length = d->length; +}; + +void +fdtruncate(int fd, ulong size) +{ + USED(fd, size); +} diff --git a/sys/src/cmd/disk/9660/rune.c b/sys/src/cmd/disk/9660/rune.c new file mode 100755 index 000000000..3a436f4a3 --- /dev/null +++ b/sys/src/cmd/disk/9660/rune.c @@ -0,0 +1,39 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> + +#include "iso9660.h" + +Rune* +strtorune(Rune *r, char *s) +{ + Rune *or; + + if(s == nil) + return nil; + + or = r; + while(*s) + s += chartorune(r++, s); + *r = L'\0'; + return or; +} + +Rune* +runechr(Rune *s, Rune c) +{ + for(; *s; s++) + if(*s == c) + return s; + return nil; +} + +int +runecmp(Rune *s, Rune *t) +{ + while(*s && *t && *s == *t) + s++, t++; + return *s - *t; +} + diff --git a/sys/src/cmd/disk/9660/sysuse.c b/sys/src/cmd/disk/9660/sysuse.c new file mode 100755 index 000000000..b7732b3a6 --- /dev/null +++ b/sys/src/cmd/disk/9660/sysuse.c @@ -0,0 +1,615 @@ +/* + * To understand this code, see Rock Ridge Interchange Protocol + * standard 1.12 and System Use Sharing Protocol version 1.12 + * (search for rrip112.ps and susp112.ps on the web). + * + * Even better, go read something else. + */ + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> +#include "iso9660.h" + +static long mode(Direc*, int); +static long nlink(Direc*); +static ulong suspdirflags(Direc*, int); +static ulong CputsuspCE(Cdimg *cd, vlong offset); +static int CputsuspER(Cdimg*, int); +static int CputsuspRR(Cdimg*, int, int); +static int CputsuspSP(Cdimg*, int); +//static int CputsuspST(Cdimg*, int); +static int Cputrripname(Cdimg*, char*, int, char*, int); +static int CputrripSL(Cdimg*, int, int, char*, int); +static int CputrripPX(Cdimg*, Direc*, int, int); +static int CputrripTF(Cdimg*, Direc*, int, int); + +/* + * Patch the length field in a CE record. + */ +static void +setcelen(Cdimg *cd, vlong woffset, ulong len) +{ + vlong o; + + o = Cwoffset(cd); + Cwseek(cd, woffset); + Cputn(cd, len, 4); + Cwseek(cd, o); +} + +/* + * Rock Ridge data is put into little blockettes, which can be + * at most 256 bytes including a one-byte length. Some number + * of blockettes get packed together into a normal 2048-byte block. + * Blockettes cannot cross block boundaries. + * + * A Cbuf is a blockette buffer. Len contains + * the length of the buffer written so far, and we can + * write up to 254-28. + * + * We only have one active Cbuf at a time; cdimg.rrcontin is the byte + * offset of the beginning of that Cbuf. + * + * The blockette can be at most 255 bytes. The last 28 + * will be (in the worst case) a CE record pointing at + * a new blockette. If we do write 255 bytes though, + * we'll try to pad it out to be even, and overflow. + * So the maximum is 254-28. + * + * Ceoffset contains the offset to be used with setcelen + * to patch the CE pointing at the Cbuf once we know how + * long the Cbuf is. + */ +typedef struct Cbuf Cbuf; +struct Cbuf { + int len; /* written so far, of 254-28 */ + uvlong ceoffset; +}; + +static int +freespace(Cbuf *cp) +{ + return (254-28) - cp->len; +} + +static Cbuf* +ensurespace(Cdimg *cd, int n, Cbuf *co, Cbuf *cn, int dowrite) +{ + uvlong end; + + if(co->len+n <= 254-28) { + co->len += n; + return co; + } + + co->len += 28; + assert(co->len <= 254); + + if(dowrite == 0) { + cn->len = n; + return cn; + } + + /* + * the current blockette is full; update cd->rrcontin and then + * write a CE record to finish it. Unfortunately we need to + * figure out which block will be next before we write the CE. + */ + end = Cwoffset(cd)+28; + + /* + * if we're in a continuation blockette, update rrcontin. + * also, write our length into the field of the CE record + * that points at us. + */ + if(cd->rrcontin+co->len == end) { + assert(cd->rrcontin != 0); + assert(co == cn); + cd->rrcontin += co->len; + setcelen(cd, co->ceoffset, co->len); + } else + assert(co != cn); + + /* + * if the current continuation block can't fit another + * blockette, then start a new continuation block. + * rrcontin = 0 (mod Blocksize) means we just finished + * one, not that we've just started one. + */ + if(cd->rrcontin%Blocksize == 0 + || cd->rrcontin/Blocksize != (cd->rrcontin+256)/Blocksize) { + cd->rrcontin = (vlong)cd->nextblock * Blocksize; + cd->nextblock++; + } + + cn->ceoffset = CputsuspCE(cd, cd->rrcontin); + + assert(Cwoffset(cd) == end); + + cn->len = n; + Cwseek(cd, cd->rrcontin); + assert(cd->rrcontin != 0); + + return cn; +} + +/* + * Put down the name, but we might need to break it + * into chunks so that each chunk fits in 254-28-5 bytes. + * What a crock. + * + * The new Plan 9 format uses strings of this form too, + * since they're already there. + */ +Cbuf* +Cputstring(Cdimg *cd, Cbuf *cp, Cbuf *cn, char *nm, char *p, int flags, int dowrite) +{ + char buf[256], *q; + int free; + + for(; p[0] != '\0'; p = q) { + cp = ensurespace(cd, 5+1, cp, cn, dowrite); + cp->len -= 5+1; + free = freespace(cp); + assert(5+1 <= free && free < 256); + + strncpy(buf, p, free-5); + buf[free-5] = '\0'; + q = p+strlen(buf); + p = buf; + + ensurespace(cd, 5+strlen(p), cp, nil, dowrite); /* nil: better not use this. */ + Cputrripname(cd, nm, flags | (q[0] ? NMcontinue : 0), p, dowrite); + } + return cp; +} + +/* + * Write a Rock Ridge SUSP set of records for a directory entry. + */ +int +Cputsysuse(Cdimg *cd, Direc *d, int dot, int dowrite, int initlen) +{ + char buf[256], buf0[256], *nextpath, *p, *path, *q; + int flags, free, m, what; + uvlong o; + Cbuf cn, co, *cp; + + assert(cd != nil); + assert((initlen&1) == 0); + + if(dot == DTroot) + return 0; + + co.len = initlen; + + o = Cwoffset(cd); + + assert(dowrite==0 || Cwoffset(cd) == o+co.len-initlen); + cp = &co; + + if (dot == DTrootdot) { + m = CputsuspSP(cd, 0); + cp = ensurespace(cd, m, cp, &cn, dowrite); + CputsuspSP(cd, dowrite); + + m = CputsuspER(cd, 0); + cp = ensurespace(cd, m, cp, &cn, dowrite); + CputsuspER(cd, dowrite); + } + + /* + * In a perfect world, we'd be able to omit the NM + * entries when our name was all lowercase and conformant, + * but OpenBSD insists on uppercasing (really, not lowercasing) + * the ISO9660 names. + */ + what = RR_PX | RR_TF | RR_NM; + if(d != nil && (d->mode & CHLINK)) + what |= RR_SL; + + m = CputsuspRR(cd, what, 0); + cp = ensurespace(cd, m, cp, &cn, dowrite); + CputsuspRR(cd, what, dowrite); + + if(what & RR_PX) { + m = CputrripPX(cd, d, dot, 0); + cp = ensurespace(cd, m, cp, &cn, dowrite); + CputrripPX(cd, d, dot, dowrite); + } + + if(what & RR_NM) { + if(dot == DTiden) + p = d->name; + else if(dot == DTdotdot) + p = ".."; + else + p = "."; + + flags = suspdirflags(d, dot); + assert(dowrite==0 || cp != &co || Cwoffset(cd) == o+co.len-initlen); + cp = Cputstring(cd, cp, &cn, "NM", p, flags, dowrite); + } + + /* + * Put down the symbolic link. This is even more of a crock. + * Not only are the individual elements potentially split, + * but the whole path itself can be split across SL blocks. + * To keep the code simple as possible (really), we write + * only one element per SL block, wasting 6 bytes per element. + */ + if(what & RR_SL) { + for(path=d->symlink; path[0] != '\0'; path=nextpath) { + /* break off one component */ + if((nextpath = strchr(path, '/')) == nil) + nextpath = path+strlen(path); + strncpy(buf0, path, nextpath-path); + buf0[nextpath-path] = '\0'; + if(nextpath[0] == '/') + nextpath++; + p = buf0; + + /* write the name, perhaps broken into pieces */ + if(strcmp(p, "") == 0) + flags = NMroot; + else if(strcmp(p, ".") == 0) + flags = NMcurrent; + else if(strcmp(p, "..") == 0) + flags = NMparent; + else + flags = 0; + + /* the do-while handles the empty string properly */ + do { + /* must have room for at least 1 byte of name */ + cp = ensurespace(cd, 7+1, cp, &cn, dowrite); + cp->len -= 7+1; + free = freespace(cp); + assert(7+1 <= free && free < 256); + + strncpy(buf, p, free-7); + buf[free-7] = '\0'; + q = p+strlen(buf); + p = buf; + + /* nil: better not need to expand */ + assert(7+strlen(p) <= free); + ensurespace(cd, 7+strlen(p), cp, nil, dowrite); + CputrripSL(cd, nextpath[0], flags | (q[0] ? NMcontinue : 0), p, dowrite); + p = q; + } while(p[0] != '\0'); + } + } + + assert(dowrite==0 || cp != &co || Cwoffset(cd) == o+co.len-initlen); + + if(what & RR_TF) { + m = CputrripTF(cd, d, TFcreation|TFmodify|TFaccess|TFattributes, 0); + cp = ensurespace(cd, m, cp, &cn, dowrite); + CputrripTF(cd, d, TFcreation|TFmodify|TFaccess|TFattributes, dowrite); + } + assert(dowrite==0 || cp != &co || Cwoffset(cd) == o+co.len-initlen); + + if(cp == &cn && dowrite) { + /* seek out of continuation, but mark our place */ + cd->rrcontin = Cwoffset(cd); + setcelen(cd, cn.ceoffset, cn.len); + Cwseek(cd, o+co.len-initlen); + } + + if(co.len & 1) { + co.len++; + if(dowrite) + Cputc(cd, 0); + } + + if(dowrite) { + if(Cwoffset(cd) != o+co.len-initlen) + fprint(2, "offset %llud o+co.len-initlen %llud\n", + Cwoffset(cd), o+co.len-initlen); + assert(Cwoffset(cd) == o+co.len-initlen); + } else + assert(Cwoffset(cd) == o); + + assert(co.len <= 255); + return co.len - initlen; +} + +static char SUSPrrip[10] = "RRIP_1991A"; +static char SUSPdesc[84] = "RRIP <more garbage here>"; +static char SUSPsrc[135] = "RRIP <more garbage here>"; + +static ulong +CputsuspCE(Cdimg *cd, vlong offset) +{ + vlong o, x; + + chat("writing SUSP CE record pointing to %ld, %ld\n", + offset/Blocksize, offset%Blocksize); + o = Cwoffset(cd); + Cputc(cd, 'C'); + Cputc(cd, 'E'); + Cputc(cd, 28); + Cputc(cd, 1); + Cputn(cd, offset/Blocksize, 4); + Cputn(cd, offset%Blocksize, 4); + x = Cwoffset(cd); + Cputn(cd, 0, 4); + assert(Cwoffset(cd) == o+28); + + return x; +} + +static int +CputsuspER(Cdimg *cd, int dowrite) +{ + assert(cd != nil); + + if(dowrite) { + chat("writing SUSP ER record\n"); + Cputc(cd, 'E'); /* ER field marker */ + Cputc(cd, 'R'); + Cputc(cd, 26); /* Length */ + Cputc(cd, 1); /* Version */ + Cputc(cd, 10); /* LEN_ID */ + Cputc(cd, 4); /* LEN_DESC */ + Cputc(cd, 4); /* LEN_SRC */ + Cputc(cd, 1); /* EXT_VER */ + Cputs(cd, SUSPrrip, 10); /* EXT_ID */ + Cputs(cd, SUSPdesc, 4); /* EXT_DESC */ + Cputs(cd, SUSPsrc, 4); /* EXT_SRC */ + } + return 8+10+4+4; +} + +static int +CputsuspRR(Cdimg *cd, int what, int dowrite) +{ + assert(cd != nil); + + if(dowrite) { + Cputc(cd, 'R'); /* RR field marker */ + Cputc(cd, 'R'); + Cputc(cd, 5); /* Length */ + Cputc(cd, 1); /* Version number */ + Cputc(cd, what); /* Flags */ + } + return 5; +} + +static int +CputsuspSP(Cdimg *cd, int dowrite) +{ + assert(cd!=0); + + if(dowrite) { +chat("writing SUSP SP record\n"); + Cputc(cd, 'S'); /* SP field marker */ + Cputc(cd, 'P'); + Cputc(cd, 7); /* Length */ + Cputc(cd, 1); /* Version */ + Cputc(cd, 0xBE); /* Magic */ + Cputc(cd, 0xEF); + Cputc(cd, 0); + } + + return 7; +} + +#ifdef NOTUSED +static int +CputsuspST(Cdimg *cd, int dowrite) +{ + assert(cd!=0); + + if(dowrite) { + Cputc(cd, 'S'); /* ST field marker */ + Cputc(cd, 'T'); + Cputc(cd, 4); /* Length */ + Cputc(cd, 1); /* Version */ + } + return 4; +} +#endif + +static ulong +suspdirflags(Direc *d, int dot) +{ + uchar flags; + + USED(d); + flags = 0; + switch(dot) { + default: + assert(0); + case DTdot: + case DTrootdot: + flags |= NMcurrent; + break; + case DTdotdot: + flags |= NMparent; + break; + case DTroot: + flags |= NMvolroot; + break; + case DTiden: + break; + } + return flags; +} + +static int +Cputrripname(Cdimg *cd, char *nm, int flags, char *name, int dowrite) +{ + int l; + + l = strlen(name); + if(dowrite) { + Cputc(cd, nm[0]); /* NM field marker */ + Cputc(cd, nm[1]); + Cputc(cd, l+5); /* Length */ + Cputc(cd, 1); /* Version */ + Cputc(cd, flags); /* Flags */ + Cputs(cd, name, l); /* Alternate name */ + } + return 5+l; +} + +static int +CputrripSL(Cdimg *cd, int contin, int flags, char *name, int dowrite) +{ + int l; + + l = strlen(name); + if(dowrite) { + Cputc(cd, 'S'); + Cputc(cd, 'L'); + Cputc(cd, l+7); + Cputc(cd, 1); + Cputc(cd, contin ? 1 : 0); + Cputc(cd, flags); + Cputc(cd, l); + Cputs(cd, name, l); + } + return 7+l; +} + +static int +CputrripPX(Cdimg *cd, Direc *d, int dot, int dowrite) +{ + assert(cd!=0); + + if(dowrite) { + Cputc(cd, 'P'); /* PX field marker */ + Cputc(cd, 'X'); + Cputc(cd, 36); /* Length */ + Cputc(cd, 1); /* Version */ + + Cputn(cd, mode(d, dot), 4); /* POSIX File mode */ + Cputn(cd, nlink(d), 4); /* POSIX st_nlink */ + Cputn(cd, d?d->uidno:0, 4); /* POSIX st_uid */ + Cputn(cd, d?d->gidno:0, 4); /* POSIX st_gid */ + } + + return 36; +} + +static int +CputrripTF(Cdimg *cd, Direc *d, int type, int dowrite) +{ + int i, length; + + assert(cd!=0); + assert(!(type & TFlongform)); + + length = 0; + for(i=0; i<7; i++) + if (type & (1<<i)) + length++; + assert(length == 4); + + if(dowrite) { + Cputc(cd, 'T'); /* TF field marker */ + Cputc(cd, 'F'); + Cputc(cd, 5+7*length); /* Length */ + Cputc(cd, 1); /* Version */ + Cputc(cd, type); /* Flags (types) */ + + if (type & TFcreation) + Cputdate(cd, d?d->ctime:0); + if (type & TFmodify) + Cputdate(cd, d?d->mtime:0); + if (type & TFaccess) + Cputdate(cd, d?d->atime:0); + if (type & TFattributes) + Cputdate(cd, d?d->ctime:0); + + // if (type & TFbackup) + // Cputdate(cd, 0); + // if (type & TFexpiration) + // Cputdate(cd, 0); + // if (type & TFeffective) + // Cputdate(cd, 0); + } + return 5+7*length; +} + + +#define NONPXMODES (DMDIR | DMAPPEND | DMEXCL | DMMOUNT) +#define POSIXMODEMASK (0177777) +#ifndef S_IFMT +#define S_IFMT (0170000) +#endif +#ifndef S_IFDIR +#define S_IFDIR (0040000) +#endif +#ifndef S_IFREG +#define S_IFREG (0100000) +#endif +#ifndef S_IFLNK +#define S_IFLNK (0120000) +#endif +#undef ISTYPE +#define ISTYPE(mode, mask) (((mode) & S_IFMT) == (mask)) +#ifndef S_ISDIR +#define S_ISDIR(mode) ISTYPE(mode, S_IFDIR) +#endif +#ifndef S_ISREG +#define S_ISREG(mode) ISTYPE(mode, S_IREG) +#endif +#ifndef S_ISLNK +#define S_ISLNK(mode) ISTYPE(mode, S_ILNK) +#endif + + +static long +mode(Direc *d, int dot) +{ + long mode; + + if (!d) + return 0; + + if ((dot != DTroot) && (dot != DTrootdot)) { + mode = (d->mode & ~(NONPXMODES)); + if (d->mode & DMDIR) + mode |= S_IFDIR; + else if (d->mode & CHLINK) + mode |= S_IFLNK; + else + mode |= S_IFREG; + } else + mode = S_IFDIR | (0755); + + mode &= POSIXMODEMASK; + + /* Botch: not all POSIX types supported yet */ + assert(mode & (S_IFDIR|S_IFREG)); + +chat("writing PX record mode field %ulo with dot %d and name \"%s\"\n", mode, dot, d->name); + + return mode; +} + +static long +nlink(Direc *d) /* Trump up the nlink field for POSIX compliance */ +{ + int i; + long n; + + if (!d) + return 0; + + n = 1; + if (d->mode & DMDIR) /* One for "." and one more for ".." */ + n++; + + for(i=0; i<d->nchild; i++) + if (d->child[i].mode & DMDIR) + n++; + + return n; +} + diff --git a/sys/src/cmd/disk/9660/uid.c b/sys/src/cmd/disk/9660/uid.c new file mode 100755 index 000000000..a63484249 --- /dev/null +++ b/sys/src/cmd/disk/9660/uid.c @@ -0,0 +1,41 @@ +#include <u.h> +#include <libc.h> + +/* + * /adm/users is + * id:user/group:head member:other members + * + * /etc/{passwd,group} + * name:x:nn:other stuff + */ + +static int isnumber(char *s); + +sniff(Biobuf *b) +{ + read first line of file into p; + + nf = getfields(p, f, nelem(f), ":"); + if(nf < 4) + return nil; + + if(isnumber(f[0]) && !isnumber(f[2])) + return _plan9; + + if(!isnumber(f[0]) && isnumber(f[2])) + return _unix; + + return nil; +} + + +int +isnumber(char *s) +{ + char *q; + + strtol(s, &q, 10); + return *q == '\0'; +} + +/* EOF */ diff --git a/sys/src/cmd/disk/9660/unix.c b/sys/src/cmd/disk/9660/unix.c new file mode 100755 index 000000000..942dac85d --- /dev/null +++ b/sys/src/cmd/disk/9660/unix.c @@ -0,0 +1,83 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> +#include <libproto.h> +#include <ctype.h> + +#include "iso9660.h" + +#include <grp.h> +#include <pwd.h> + +typedef struct Xarg Xarg; +struct Xarg { + void (*enm)(char*,char*,XDir*,void*); + void (*warn)(char*,void*); + void *arg; +}; + +static long numericuid(char *user); +static long numericgid(char *gp); + +void +dirtoxdir(XDir *xd, Dir *d) +{ + // char buf[NAMELEN+1]; + memset(xd, 0, sizeof *xd); + + xd->name = atom(d->name); + xd->uid = atom(d->uid); + xd->gid = atom(d->gid); + xd->uidno = numericuid(d->uid); + xd->gidno = numericgid(d->gid); + xd->mode = d->mode; + xd->atime = d->atime; + xd->mtime = d->mtime; + xd->ctime = 0; + xd->length = d->length; + if(xd->mode & CHLINK) { + xd->mode |= 0777; + xd->symlink = atom(d->symlink); + } +}; + +void +fdtruncate(int fd, ulong size) +{ + ftruncate(fd, size); + + return; +} + +static long +numericuid(char *user) +{ + struct passwd *pass; + static int warned = 0; + + if (! (pass = getpwnam(user))) { + if (!warned) + fprint(2, "Warning: getpwnam(3) failed for \"%s\"\n", user); + warned = 1; + return 0; + } + + return pass->pw_uid; +} + +static long +numericgid(char *gp) +{ + struct group *gr; + static int warned = 0; + + if (! (gr = getgrnam(gp))) { + if (!warned) + fprint(2, "Warning: getgrnam(3) failed for \"%s\"\n", gp); + warned = 1; + return 0; + } + + return gr->gr_gid; +} diff --git a/sys/src/cmd/disk/9660/util.c b/sys/src/cmd/disk/9660/util.c new file mode 100755 index 000000000..16fb21f3d --- /dev/null +++ b/sys/src/cmd/disk/9660/util.c @@ -0,0 +1,98 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> +#include <ctype.h> + +#include "iso9660.h" + +typedef struct Stringtab Stringtab; +struct Stringtab { + Stringtab *link; + char *str; +}; + +static Stringtab *stab[1024]; + +static uint +hash(char *s) +{ + uint h; + uchar *p; + + h = 0; + for(p=(uchar*)s; *p; p++) + h = h*37 + *p; + return h; +} + +static char* +estrdup(char *s) +{ + if((s = strdup(s)) == nil) + sysfatal("strdup(%.10s): out of memory", s); + return s; +} + +char* +atom(char *str) +{ + uint h; + Stringtab *tab; + + h = hash(str) % nelem(stab); + for(tab=stab[h]; tab; tab=tab->link) + if(strcmp(str, tab->str) == 0) + return tab->str; + + tab = emalloc(sizeof *tab); + tab->str = estrdup(str); + tab->link = stab[h]; + stab[h] = tab; + return tab->str; +} + +void* +emalloc(ulong n) +{ + void *p; + + if((p = malloc(n)) == nil) + sysfatal("malloc(%lud): out of memory", n); + memset(p, 0, n); + return p; +} + +void* +erealloc(void *v, ulong n) +{ + if((v = realloc(v, n)) == nil) + sysfatal("realloc(%p, %lud): out of memory", v, n); + return v; +} + +char* +struprcpy(char *p, char *s) +{ + char *op; + + op = p; + for(; *s; s++) + *p++ = toupper(*s); + *p = '\0'; + + return op; +} + +int +chat(char *fmt, ...) +{ + va_list arg; + + if(!chatty) + return 0; + va_start(arg, fmt); + vfprint(2, fmt, arg); + va_end(arg); + return 1; +} diff --git a/sys/src/cmd/disk/9660/write.c b/sys/src/cmd/disk/9660/write.c new file mode 100755 index 000000000..e45f21c19 --- /dev/null +++ b/sys/src/cmd/disk/9660/write.c @@ -0,0 +1,411 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <libsec.h> + +#include "iso9660.h" + +static void +writelittlebig4(uchar *buf, ulong x) +{ + buf[0] = buf[7] = x; + buf[1] = buf[6] = x>>8; + buf[2] = buf[5] = x>>16; + buf[3] = buf[4] = x>>24; +} + +void +rewritedot(Cdimg *cd, Direc *d) +{ + uchar buf[Blocksize]; + Cdir *c; + + Creadblock(cd, buf, d->block, Blocksize); + c = (Cdir*)buf; + assert(c->len != 0); + assert(c->namelen == 1 && c->name[0] == '\0'); /* dot */ + writelittlebig4(c->dloc, d->block); + writelittlebig4(c->dlen, d->length); + + Cwseek(cd, (vlong)d->block * Blocksize); + Cwrite(cd, buf, Blocksize); +} + +void +rewritedotdot(Cdimg *cd, Direc *d, Direc *dparent) +{ + uchar buf[Blocksize]; + Cdir *c; + + Creadblock(cd, buf, d->block, Blocksize); + c = (Cdir*)buf; + assert(c->len != 0); + assert(c->namelen == 1 && c->name[0] == '\0'); /* dot */ + + c = (Cdir*)(buf+c->len); + assert(c->len != 0); + assert(c->namelen == 1 && c->name[0] == '\001'); /* dotdot*/ + + writelittlebig4(c->dloc, dparent->block); + writelittlebig4(c->dlen, dparent->length); + + Cwseek(cd, (vlong)d->block * Blocksize); + Cwrite(cd, buf, Blocksize); +} + +/* + * Write each non-directory file. We copy the file to + * the cd image, and then if it turns out that we've + * seen this stream of bits before, we push the next block + * pointer back. This ensures consistency between the MD5s + * and the data on the CD image. MD5 summing on one pass + * and copying on another would not ensure this. + */ +void +writefiles(Dump *d, Cdimg *cd, Direc *direc) +{ + int i; + uchar buf[8192], digest[MD5dlen]; + ulong length, n, start; + Biobuf *b; + DigestState *s; + Dumpdir *dd; + + if(direc->mode & DMDIR) { + for(i=0; i<direc->nchild; i++) + writefiles(d, cd, &direc->child[i]); + return; + } + + assert(direc->block == 0); + + if((b = Bopen(direc->srcfile, OREAD)) == nil){ + fprint(2, "warning: cannot open '%s': %r\n", direc->srcfile); + direc->block = 0; + direc->length = 0; + return; + } + + start = cd->nextblock; + assert(start != 0); + if(blocksize && start%blocksize) + start += blocksize-start%blocksize; + + Cwseek(cd, (vlong)start * Blocksize); + + s = md5(nil, 0, nil, nil); + length = 0; + while((n = Bread(b, buf, sizeof buf)) > 0) { + md5(buf, n, nil, s); + Cwrite(cd, buf, n); + length += n; + } + md5(nil, 0, digest, s); + Bterm(b); + Cpadblock(cd); + + if(length != direc->length) { + fprint(2, "warning: %s changed size underfoot\n", direc->srcfile); + direc->length = length; + } + + if(length == 0) + direc->block = 0; + else if((dd = lookupmd5(d, digest))) { + assert(dd->length == length); + assert(dd->block != 0); + direc->block = dd->block; + cd->nextblock = start; + } else { + direc->block = start; + if(chatty > 1) + fprint(2, "lookup %.16H %lud (%s) failed\n", digest, length, direc->name); + insertmd5(d, atom(direc->name), digest, start, length); + } +} + +/* + * Write a directory tree. We work from the leaves, + * and patch the dotdot pointers afterward. + */ +static void +_writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level) +{ + int i, l, ll; + ulong start, next; + + if((d->mode & DMDIR) == 0) + return; + + if(chatty) + fprint(2, "%*s%s\n", 4*level, "", d->name); + + for(i=0; i<d->nchild; i++) + _writedirs(cd, &d->child[i], put, level+1); + + l = 0; + l += put(cd, d, (level == 0) ? DTrootdot : DTdot, 0, l); + l += put(cd, nil, DTdotdot, 0, l); + for(i=0; i<d->nchild; i++) + l += put(cd, &d->child[i], DTiden, 0, l); + + start = cd->nextblock; + cd->nextblock += (l+Blocksize-1)/Blocksize; + next = cd->nextblock; + + Cwseek(cd, (vlong)start * Blocksize); + ll = 0; + ll += put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, ll); + ll += put(cd, nil, DTdotdot, 1, ll); + for(i=0; i<d->nchild; i++) + ll += put(cd, &d->child[i], DTiden, 1, ll); + assert(ll == l); + Cpadblock(cd); + assert(Cwoffset(cd) == (vlong)next * Blocksize); + + d->block = start; + d->length = (vlong)(next - start) * Blocksize; + rewritedot(cd, d); + rewritedotdot(cd, d, d); + + for(i=0; i<d->nchild; i++) + if(d->child[i].mode & DMDIR) + rewritedotdot(cd, &d->child[i], d); +} + +void +writedirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int)) +{ + /* + * If we're writing a mk9660 image, then the root really + * is the root, so start at level 0. If we're writing a dump image, + * then the "root" is really going to be two levels down once + * we patch in the dump hierarchy above it, so start at level non-zero. + */ + if(chatty) + fprint(2, ">>> writedirs\n"); + _writedirs(cd, d, put, mk9660 ? 0 : 1); +} + + +/* + * Write the dump tree. This is like writedirs but once we get to + * the roots of the individual days we just patch the parent dotdot blocks. + */ +static void +_writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int), int level) +{ + int i; + ulong start; + + switch(level) { + case 0: + /* write root, list of years, also conform.map */ + for(i=0; i<d->nchild; i++) + if(d->child[i].mode & DMDIR) + _writedumpdirs(cd, &d->child[i], put, level+1); + chat("write dump root dir at %lud\n", cd->nextblock); + goto Writedir; + + case 1: /* write year, list of days */ + for(i=0; i<d->nchild; i++) + _writedumpdirs(cd, &d->child[i], put, level+1); + chat("write dump %s dir at %lud\n", d->name, cd->nextblock); + goto Writedir; + + Writedir: + start = cd->nextblock; + Cwseek(cd, (vlong)start * Blocksize); + + put(cd, d, (level == 0) ? DTrootdot : DTdot, 1, Cwoffset(cd)); + put(cd, nil, DTdotdot, 1, Cwoffset(cd)); + for(i=0; i<d->nchild; i++) + put(cd, &d->child[i], DTiden, 1, Cwoffset(cd)); + Cpadblock(cd); + + d->block = start; + d->length = (vlong)(cd->nextblock - start) * Blocksize; + + rewritedot(cd, d); + rewritedotdot(cd, d, d); + + for(i=0; i<d->nchild; i++) + if(d->child[i].mode & DMDIR) + rewritedotdot(cd, &d->child[i], d); + break; + + case 2: /* write day: already written, do nothing */ + break; + + default: + assert(0); + } +} + +void +writedumpdirs(Cdimg *cd, Direc *d, int (*put)(Cdimg*, Direc*, int, int, int)) +{ + _writedumpdirs(cd, d, put, 0); +} + +static int +Cputplan9(Cdimg *cd, Direc *d, int dot, int dowrite) +{ + int l, n; + + if(dot != DTiden) + return 0; + + l = 0; + if(d->flags & Dbadname) { + n = strlen(d->name); + l += 1+n; + if(dowrite) { + Cputc(cd, n); + Cputs(cd, d->name, n); + } + } else { + l++; + if(dowrite) + Cputc(cd, 0); + } + + n = strlen(d->uid); + l += 1+n; + if(dowrite) { + Cputc(cd, n); + Cputs(cd, d->uid, n); + } + + n = strlen(d->gid); + l += 1+n; + if(dowrite) { + Cputc(cd, n); + Cputs(cd, d->gid, n); + } + + if(l & 1) { + l++; + if(dowrite) + Cputc(cd, 0); + } + l += 8; + if(dowrite) + Cputn(cd, d->mode, 4); + + return l; +} + +/* + * Write a directory entry. + */ +static int +genputdir(Cdimg *cd, Direc *d, int dot, int joliet, int dowrite, int offset) +{ + int f, n, l, lp; + vlong o; + + f = 0; + if(dot != DTiden || (d->mode & DMDIR)) + f |= 2; + + n = 1; + if(dot == DTiden) { + if(joliet) + n = 2*utflen(d->confname); + else + n = strlen(d->confname); + } + + l = 33+n; + if(l & 1) + l++; + assert(l <= 255); + + if(joliet == 0) { + if(cd->flags & CDplan9) + l += Cputplan9(cd, d, dot, 0); + else if(cd->flags & CDrockridge) + l += Cputsysuse(cd, d, dot, 0, l); + assert(l <= 255); + } + + if(dowrite == 0) { + if(Blocksize - offset%Blocksize < l) + l += Blocksize - offset%Blocksize; + return l; + } + + assert(offset%Blocksize == Cwoffset(cd)%Blocksize); + + o = Cwoffset(cd); + lp = 0; + if(Blocksize - Cwoffset(cd)%Blocksize < l) { + lp = Blocksize - Cwoffset(cd)%Blocksize; + Cpadblock(cd); + } + + Cputc(cd, l); /* length of directory record */ + Cputc(cd, 0); /* extended attribute record length */ + if(d) { + if((d->mode & DMDIR) == 0) + assert(d->length == 0 || d->block >= 18); + + Cputn(cd, d->block, 4); /* location of extent */ + Cputn(cd, d->length, 4); /* data length */ + } else { + Cputn(cd, 0, 4); + Cputn(cd, 0, 4); + } + Cputdate(cd, d ? d->mtime : now); /* recorded date */ + Cputc(cd, f); /* file flags */ + Cputc(cd, 0); /* file unit size */ + Cputc(cd, 0); /* interleave gap size */ + Cputn(cd, 1, 2); /* volume sequence number */ + Cputc(cd, n); /* length of file identifier */ + + if(dot == DTiden) { /* identifier */ + if(joliet) + Cputrscvt(cd, d->confname, n); + else + Cputs(cd, d->confname, n); + }else + if(dot == DTdotdot) + Cputc(cd, 1); + else + Cputc(cd, 0); + + if(Cwoffset(cd) & 1) /* pad */ + Cputc(cd, 0); + + if(joliet == 0) { + if(cd->flags & CDplan9) + Cputplan9(cd, d, dot, 1); + else if(cd->flags & CDrockridge) + Cputsysuse(cd, d, dot, 1, Cwoffset(cd)-(o+lp)); + } + + assert(o+lp+l == Cwoffset(cd)); + return lp+l; +} + +int +Cputisodir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset) +{ + return genputdir(cd, d, dot, 0, dowrite, offset); +} + +int +Cputjolietdir(Cdimg *cd, Direc *d, int dot, int dowrite, int offset) +{ + return genputdir(cd, d, dot, 1, dowrite, offset); +} + +void +Cputendvd(Cdimg *cd) +{ + Cputc(cd, 255); /* volume descriptor set terminator */ + Cputs(cd, "CD001", 5); /* standard identifier */ + Cputc(cd, 1); /* volume descriptor version */ + Cpadblock(cd); +} diff --git a/sys/src/cmd/disk/exsort.c b/sys/src/cmd/disk/exsort.c new file mode 100755 index 000000000..4a04c845a --- /dev/null +++ b/sys/src/cmd/disk/exsort.c @@ -0,0 +1,136 @@ +#include <u.h> +#include <libc.h> + +int ulcmp(void*, void*); +void swapem(ulong*, long); + +enum +{ + Wormsize = 157933, +}; +int wflag; + +void +main(int argc, char *argv[]) +{ + long i, l, x, lobits, hibits, tot; + int f, j; + char *file; + ulong *b, a, lo, hi; + + ARGBEGIN { + default: + print("usage: disk/exsort [-w] [file]\n"); + exits("usage"); + case 'w': + wflag++; + break; + } ARGEND; + + file = "/adm/cache"; + if(argc > 0) + file = argv[0]; + + if(wflag) + f = open(file, ORDWR); + else + f = open(file, OREAD); + if(f < 0) { + print("cant open %s: %r\n", file); + exits("open"); + } + l = seek(f, 0, 2) / sizeof(long); + + b = malloc(l*sizeof(long)); + if(b == 0) { + print("cant malloc %s: %r\n", file); + exits("malloc"); + } + seek(f, 0, 0); + if(read(f, b, l*sizeof(long)) != l*sizeof(long)) { + print("short read %s: %r\n", file); + exits("read"); + } + + lobits = 0; + hibits = 0; + for(i=0; i<l; i++) { + a = b[i]; + if(a & (1L<<7)) + lobits++; + if(a & (1L<<31)) + hibits++; + } + + print("lobits = %6ld\n", lobits); + print("hibits = %6ld\n", hibits); + + if(hibits > lobits) { + print("swapping\n"); + swapem(b, l); + } + + qsort(b, l, sizeof(ulong), ulcmp); + + tot = 0; + for(j=0; j<100; j++) { + lo = j*Wormsize; + hi = lo + Wormsize; + + x = 0; + for(i=0; i<l; i++) { + a = b[i]; + if(a >= lo && a < hi) + x++; + } + if(x) { + print("disk %2d %6ld blocks\n", j, x); + tot += x; + } + } + print("total %6ld blocks\n", tot); + + + if(wflag) { + if(hibits > lobits) + swapem(b, l); + seek(f, 0, 0); + if(write(f, b, l*sizeof(long)) != l*sizeof(long)) { + print("short write %s\n", file); + exits("write"); + } + } + + exits(0); +} + +int +ulcmp(void *va, void *vb) +{ + ulong *a, *b; + + a = va; + b = vb; + + if(*a > *b) + return 1; + if(*a < *b) + return -1; + return 0; +} + +void +swapem(ulong *b, long l) +{ + long i; + ulong x, a; + + for(i=0; i<l; i++, b++) { + a = *b; + x = (((a>>0) & 0xff) << 24) | + (((a>>8) & 0xff) << 16) | + (((a>>16) & 0xff) << 8) | + (((a>>24) & 0xff) << 0); + *b = x; + } +} diff --git a/sys/src/cmd/disk/format.c b/sys/src/cmd/disk/format.c new file mode 100755 index 000000000..2ada3abfa --- /dev/null +++ b/sys/src/cmd/disk/format.c @@ -0,0 +1,799 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> +#include <disk.h> + +/* + * disk types (all MFM encoding) + */ +typedef struct Type Type; +struct Type +{ + char *name; + int bytes; /* bytes/sector */ + int sectors; /* sectors/track */ + int heads; /* number of heads */ + int tracks; /* tracks/disk */ + int media; /* media descriptor byte */ + int cluster; /* default cluster size */ +}; +Type floppytype[] = +{ + { "3½HD", 512, 18, 2, 80, 0xf0, 1, }, + { "3½DD", 512, 9, 2, 80, 0xf9, 2, }, + { "3½QD", 512, 36, 2, 80, 0xf9, 2, }, /* invented */ + { "5¼HD", 512, 15, 2, 80, 0xf9, 1, }, + { "5¼DD", 512, 9, 2, 40, 0xfd, 2, }, + { "hard", 512, 0, 0, 0, 0xf8, 4, }, +}; + +#define NTYPES (sizeof(floppytype)/sizeof(Type)) + +typedef struct Dosboot Dosboot; +struct Dosboot{ + uchar magic[3]; /* really an x86 JMP instruction */ + uchar version[8]; + uchar sectsize[2]; + uchar clustsize; + uchar nresrv[2]; + uchar nfats; + uchar rootsize[2]; + uchar volsize[2]; + uchar mediadesc; + uchar fatsize[2]; + uchar trksize[2]; + uchar nheads[2]; + uchar nhidden[4]; + uchar bigvolsize[4]; + uchar driveno; + uchar reserved0; + uchar bootsig; + uchar volid[4]; + uchar label[11]; + uchar type[8]; +}; +#define PUTSHORT(p, v) { (p)[1] = (v)>>8; (p)[0] = (v); } +#define PUTLONG(p, v) { PUTSHORT((p), (v)); PUTSHORT((p)+2, (v)>>16); } +#define GETSHORT(p) (((p)[1]<<8)|(p)[0]) +#define GETLONG(p) (((ulong)GETSHORT(p+2)<<16)|(ulong)GETSHORT(p)) + +typedef struct Dosdir Dosdir; +struct Dosdir +{ + uchar name[8]; + uchar ext[3]; + uchar attr; + uchar reserved[10]; + uchar time[2]; + uchar date[2]; + uchar start[2]; + uchar length[4]; +}; + +#define DRONLY 0x01 +#define DHIDDEN 0x02 +#define DSYSTEM 0x04 +#define DVLABEL 0x08 +#define DDIR 0x10 +#define DARCH 0x20 + +/* + * the boot program for the boot sector. + */ +int nbootprog = 188; /* no. of bytes of boot program, including the first 0x3E */ +uchar bootprog[512] = +{ +[0x000] 0xEB, 0x3C, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +[0x03E] 0xFA, 0xFC, 0x8C, 0xC8, 0x8E, 0xD8, 0x8E, 0xD0, + 0xBC, 0x00, 0x7C, 0xBE, 0x77, 0x7C, 0xE8, 0x19, + 0x00, 0x33, 0xC0, 0xCD, 0x16, 0xBB, 0x40, 0x00, + 0x8E, 0xC3, 0xBB, 0x72, 0x00, 0xB8, 0x34, 0x12, + 0x26, 0x89, 0x07, 0xEA, 0x00, 0x00, 0xFF, 0xFF, + 0xEB, 0xD6, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4, + 0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2, + 0xC3, 'N', 'o', 't', ' ', 'a', ' ', 'b', + 'o', 'o', 't', 'a', 'b', 'l', 'e', ' ', + 'd', 'i', 's', 'c', ' ', 'o', 'r', ' ', + 'd', 'i', 's', 'c', ' ', 'e', 'r', 'r', + 'o', 'r', '\r', '\n', 'P', 'r', 'e', 's', + 's', ' ', 'a', 'l', 'm', 'o', 's', 't', + ' ', 'a', 'n', 'y', ' ', 'k', 'e', 'y', + ' ', 't', 'o', ' ', 'r', 'e', 'b', 'o', + 'o', 't', '.', '.', '.', 0x00, 0x00, 0x00, +[0x1F0] 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA, +}; + +char *dev; +int clustersize; +uchar *fat; /* the fat */ +int fatbits; +int fatsecs; +int fatlast; /* last cluster allocated */ +int clusters; +int fatsecs; +vlong volsecs; +uchar *root; /* first block of root */ +int rootsecs; +int rootfiles; +int rootnext; +int nresrv = 1; +int chatty; +vlong length; +Type *t; +int fflag; +int hflag; +int xflag; +char *file; +char *pbs; +char *type; +char *bootfile; +int dos; + +enum +{ + Sof = 1, /* start of file */ + Eof = 2, /* end of file */ +}; + +void dosfs(int, int, Disk*, char*, int, char*[], int); +ulong clustalloc(int); +void addrname(uchar*, Dir*, char*, ulong); +void sanitycheck(Disk*); + +void +usage(void) +{ + fprint(2, "usage: disk/format [-df] [-b bootblock] [-c csize] " + "[-l label] [-r nresrv] [-t type] disk [files ...]\n"); + exits("usage"); +} + +void +fatal(char *fmt, ...) +{ + char err[128]; + va_list arg; + + va_start(arg, fmt); + vsnprint(err, sizeof(err), fmt, arg); + va_end(arg); + fprint(2, "format: %s\n", err); + if(fflag && file) + remove(file); + exits(err); +} + +void +main(int argc, char **argv) +{ + int fd, n, writepbs; + char buf[512], label[11]; + char *a; + Disk *disk; + + dos = 0; + type = nil; + clustersize = 0; + writepbs = 0; + memmove(label, "CYLINDRICAL", sizeof(label)); + ARGBEGIN { + case 'b': + pbs = EARGF(usage()); + writepbs = 1; + break; + case 'c': + clustersize = atoi(EARGF(usage())); + break; + case 'd': + dos = 1; + writepbs = 1; + break; + case 'f': + fflag = 1; + break; + case 'l': + a = EARGF(usage()); + n = strlen(a); + if(n > sizeof(label)) + n = sizeof(label); + memmove(label, a, n); + while(n < sizeof(label)) + label[n++] = ' '; + break; + case 'r': + nresrv = atoi(EARGF(usage())); + break; + case 't': + type = EARGF(usage()); + break; + case 'v': + chatty++; + break; + case 'x': + xflag = 1; + break; + default: + usage(); + } ARGEND + + if(argc < 1) + usage(); + + disk = opendisk(argv[0], 0, 0); + if(disk == nil) { + if(fflag) { + if((fd = create(argv[0], ORDWR, 0666)) >= 0) { + file = argv[0]; + close(fd); + disk = opendisk(argv[0], 0, 0); + } + } + } + if(disk == nil) + fatal("opendisk: %r"); + + if(disk->type == Tfile) + fflag = 1; + + if(type == nil) { + switch(disk->type){ + case Tfile: + type = "3½HD"; + break; + case Tfloppy: + seek(disk->ctlfd, 0, 0); + n = read(disk->ctlfd, buf, 10); + if(n <= 0 || n >= 10) + fatal("reading floppy type"); + buf[n] = 0; + type = strdup(buf); + if(type == nil) + fatal("out of memory"); + break; + case Tsd: + type = "hard"; + break; + default: + type = "unknown"; + break; + } + } + + if(!fflag && disk->type == Tfloppy) + if(fprint(disk->ctlfd, "format %s", type) < 0) + fatal("formatting floppy as %s: %r", type); + + if(disk->type != Tfloppy) + sanitycheck(disk); + + /* check that everything will succeed */ + dosfs(dos, writepbs, disk, label, argc-1, argv+1, 0); + + /* commit */ + dosfs(dos, writepbs, disk, label, argc-1, argv+1, 1); + + print("used %lld bytes\n", fatlast*clustersize*disk->secsize); + exits(0); +} + +/* + * Look for a partition table on sector 1, as would be the + * case if we were erroneously formatting 9fat without -r 2. + * If it's there and nresrv is not big enough, complain and exit. + * I've blown away my partition table too many times. + */ +void +sanitycheck(Disk *disk) +{ + char buf[512]; + int bad; + + if(xflag) + return; + + bad = 0; + if(dos && nresrv < 2 && seek(disk->fd, disk->secsize, 0) == disk->secsize + && read(disk->fd, buf, sizeof(buf)) >= 5 && strncmp(buf, "part ", 5) == 0) { + fprint(2, + "there's a plan9 partition on the disk\n" + "and you didn't specify -r 2 (or greater).\n" + "either specify -r 2 or -x to disable this check.\n"); + bad = 1; + } + + if(disk->type == Tsd && disk->offset == 0LL) { + fprint(2, + "you're attempting to format your disk (/dev/sdXX/data)\n" + "rather than a partition like /dev/sdXX/9fat;\n" + "this is likely a mistake. specify -x to disable this check.\n"); + bad = 1; + } + + if(bad) + exits("failed disk sanity check"); +} + +/* + * Return the BIOS drive number for the disk. + * 0x80 is the first fixed disk, 0x81 the next, etc. + * We map sdC0=0x80, sdC1=0x81, sdD0=0x82, sdD1=0x83 + */ +int +getdriveno(Disk *disk) +{ + char buf[64], *p; + + if(disk->type != Tsd) + return 0x80; /* first hard disk */ + + if(fd2path(disk->fd, buf, sizeof(buf)) < 0) + return 0x80; + + /* + * The name is of the format #SsdC0/foo + * or /dev/sdC0/foo. + * So that we can just look for /sdC0, turn + * #SsdC0/foo into #/sdC0/foo. + */ + if(buf[0] == '#' && buf[1] == 'S') + buf[1] = '/'; + + for(p=buf; *p; p++) + if(p[0] == 's' && p[1] == 'd' && (p[2]=='C' || p[2]=='D') && + (p[3]=='0' || p[3]=='1')) + return 0x80 + (p[2]-'C')*2 + (p[3]-'0'); + + return 0x80; +} + +long +writen(int fd, void *buf, long n) +{ + long m, tot; + + /* write 8k at a time, to be nice to the disk subsystem */ + for(tot=0; tot<n; tot+=m){ + m = n - tot; + if(m > 8192) + m = 8192; + if(write(fd, (uchar*)buf+tot, m) != m) + break; + } + return tot; +} + +void +dosfs(int dofat, int dopbs, Disk *disk, char *label, int argc, char *argv[], int commit) +{ + char r[16]; + Dosboot *b; + uchar *buf, *pbsbuf, *p; + Dir *d; + int i, data, newclusters, npbs, n, sysfd; + ulong x; + vlong length, secsize; + + if(dofat == 0 && dopbs == 0) + return; + + for(t = floppytype; t < &floppytype[NTYPES]; t++) + if(strcmp(type, t->name) == 0) + break; + if(t == &floppytype[NTYPES]) + fatal("unknown floppy type %s", type); + + if(t->sectors == 0 && strcmp(type, "hard") == 0) { + t->sectors = disk->s; + t->heads = disk->h; + t->tracks = disk->c; + } + + if(t->sectors == 0 && dofat) + fatal("cannot format fat with type %s: geometry unknown\n", type); + + if(fflag){ + disk->size = t->bytes*t->sectors*t->heads*t->tracks; + disk->secsize = t->bytes; + disk->secs = disk->size / disk->secsize; + } + + secsize = disk->secsize; + length = disk->size; + + buf = malloc(secsize); + if(buf == 0) + fatal("out of memory"); + + /* + * Make disk full size if a file. + */ + if(fflag && disk->type == Tfile){ + if((d = dirfstat(disk->wfd)) == nil) + fatal("fstat disk: %r"); + if(commit && d->length < disk->size) { + if(seek(disk->wfd, disk->size-1, 0) < 0) + fatal("seek to 9: %r"); + if(write(disk->wfd, "9", 1) < 0) + fatal("writing 9: @%lld %r", seek(disk->wfd, 0LL, 1)); + } + free(d); + } + + /* + * Start with initial sector from disk + */ + if(seek(disk->fd, 0, 0) < 0) + fatal("seek to boot sector: %r\n"); + if(commit && read(disk->fd, buf, secsize) != secsize) + fatal("reading boot sector: %r"); + + if(dofat) + memset(buf, 0, sizeof(Dosboot)); + + /* + * Jump instruction and OEM name. + */ + b = (Dosboot*)buf; + b->magic[0] = 0xEB; + b->magic[1] = 0x3C; + b->magic[2] = 0x90; + memmove(b->version, "Plan9.00", sizeof(b->version)); + + /* + * Add bootstrapping code; assume it starts + * at 0x3E (the destination of the jump we just + * wrote to b->magic). + */ + if(dopbs) { + pbsbuf = malloc(secsize); + if(pbsbuf == 0) + fatal("out of memory"); + + if(pbs){ + if((sysfd = open(pbs, OREAD)) < 0) + fatal("open %s: %r", pbs); + if((npbs = read(sysfd, pbsbuf, secsize)) < 0) + fatal("read %s: %r", pbs); + + if(npbs > secsize-2) + fatal("boot block too large"); + + close(sysfd); + } + else { + memmove(pbsbuf, bootprog, sizeof(bootprog)); + npbs = nbootprog; + } + if(npbs <= 0x3E) + fprint(2, "warning: pbs too small\n"); + else + memmove(buf+0x3E, pbsbuf+0x3E, npbs-0x3E); + + free(pbsbuf); + } + + /* + * Add FAT BIOS parameter block. + */ + if(dofat) { + if(commit) { + print("Initializing FAT file system\n"); + print("type %s, %d tracks, %d heads, %d sectors/track, %lld bytes/sec\n", + t->name, t->tracks, t->heads, t->sectors, secsize); + } + + if(clustersize == 0) + clustersize = t->cluster; + /* + * the number of fat bits depends on how much disk is left + * over after you subtract out the space taken up by the fat tables. + * try both. what a crock. + */ + fatbits = 12; +Tryagain: + volsecs = length/secsize; + /* + * here's a crock inside a crock. even having fixed fatbits, + * the number of fat sectors depends on the number of clusters, + * but of course we don't know yet. maybe iterating will get us there. + * or maybe it will cycle. + */ + clusters = 0; + for(i=0;; i++){ + fatsecs = (fatbits*clusters + 8*secsize - 1)/(8*secsize); + rootsecs = volsecs/200; + rootfiles = rootsecs * (secsize/sizeof(Dosdir)); + if(rootfiles > 512){ + rootfiles = 512; + rootsecs = rootfiles/(secsize/sizeof(Dosdir)); + } + data = nresrv + 2*fatsecs + (rootfiles*sizeof(Dosdir) + secsize-1)/secsize; + newclusters = 2 + (volsecs - data)/clustersize; + if(newclusters == clusters) + break; + clusters = newclusters; + if(i > 10) + fatal("can't decide how many clusters to use (%d? %d?)", clusters, newclusters); +if(chatty) print("clusters %d\n", clusters); + } + +if(chatty) print("try %d fatbits => %d clusters of %d\n", fatbits, clusters, clustersize); + switch(fatbits){ + case 12: + if(clusters >= 4087){ + fatbits = 16; + goto Tryagain; + } + break; + case 16: + if(clusters >= 65527) + fatal("disk too big; implement fat32"); + break; + } + PUTSHORT(b->sectsize, secsize); + b->clustsize = clustersize; + PUTSHORT(b->nresrv, nresrv); + b->nfats = 2; + PUTSHORT(b->rootsize, rootfiles); + if(volsecs < (1<<16)) + PUTSHORT(b->volsize, volsecs); + b->mediadesc = t->media; + PUTSHORT(b->fatsize, fatsecs); + PUTSHORT(b->trksize, t->sectors); + PUTSHORT(b->nheads, t->heads); + PUTLONG(b->nhidden, disk->offset); + PUTLONG(b->bigvolsize, volsecs); + + /* + * Extended BIOS Parameter Block. + */ + if(t->media == 0xF8) + b->driveno = getdriveno(disk); + else + b->driveno = 0; +if(chatty) print("driveno = %ux\n", b->driveno); + + b->bootsig = 0x29; + x = disk->offset + b->nfats*fatsecs + nresrv; + PUTLONG(b->volid, x); +if(chatty) print("volid = %lux %lux\n", x, GETLONG(b->volid)); + memmove(b->label, label, sizeof(b->label)); + sprint(r, "FAT%d ", fatbits); + memmove(b->type, r, sizeof(b->type)); + } + + buf[secsize-2] = 0x55; + buf[secsize-1] = 0xAA; + + if(commit) { + if(seek(disk->wfd, 0, 0) < 0) + fatal("seek to boot sector: %r\n"); + if(write(disk->wfd, buf, secsize) != secsize) + fatal("writing boot sector: %r"); + } + + free(buf); + + /* + * If we were only called to write the PBS, leave now. + */ + if(dofat == 0) + return; + + /* + * allocate an in memory fat + */ + if(seek(disk->wfd, nresrv*secsize, 0) < 0) + fatal("seek to fat: %r\n"); +if(chatty) print("fat @%lluX\n", seek(disk->wfd, 0, 1)); + fat = malloc(fatsecs*secsize); + if(fat == 0) + fatal("out of memory"); + memset(fat, 0, fatsecs*secsize); + fat[0] = t->media; + fat[1] = 0xff; + fat[2] = 0xff; + if(fatbits == 16) + fat[3] = 0xff; + fatlast = 1; + if(seek(disk->wfd, 2*fatsecs*secsize, 1) < 0) /* 2 fats */ + fatal("seek to root: %r"); +if(chatty) print("root @%lluX\n", seek(disk->wfd, 0LL, 1)); + + /* + * allocate an in memory root + */ + root = malloc(rootsecs*secsize); + if(root == 0) + fatal("out of memory"); + memset(root, 0, rootsecs*secsize); + if(seek(disk->wfd, rootsecs*secsize, 1) < 0) /* rootsecs */ + fatal("seek to files: %r"); +if(chatty) print("files @%lluX\n", seek(disk->wfd, 0LL, 1)); + + /* + * Now positioned at the Files Area. + * If we have any arguments, process + * them and write out. + */ + for(p = root; argc > 0; argc--, argv++, p += sizeof(Dosdir)){ + if(p >= (root+(rootsecs*secsize))) + fatal("too many files in root"); + /* + * Open the file and get its length. + */ + if((sysfd = open(*argv, OREAD)) < 0) + fatal("open %s: %r", *argv); + if((d = dirfstat(sysfd)) == nil) + fatal("stat %s: %r", *argv); + if(d->length > 0xFFFFFFFFU) + fatal("file %s too big\n", *argv, d->length); + if(commit) + print("Adding file %s, length %lld\n", *argv, d->length); + + length = d->length; + if(length){ + /* + * Allocate a buffer to read the entire file into. + * This must be rounded up to a cluster boundary. + * + * Read the file and write it out to the Files Area. + */ + length += secsize*clustersize - 1; + length /= secsize*clustersize; + length *= secsize*clustersize; + if((buf = malloc(length)) == 0) + fatal("out of memory"); + + if(readn(sysfd, buf, d->length) != d->length) + fatal("read %s: %r", *argv); + memset(buf+d->length, 0, length-d->length); +if(chatty) print("%s @%lluX\n", d->name, seek(disk->wfd, 0LL, 1)); + if(commit && writen(disk->wfd, buf, length) != length) + fatal("write %s: %r", *argv); + free(buf); + + close(sysfd); + + /* + * Allocate the FAT clusters. + * We're assuming here that where we + * wrote the file is in sync with + * the cluster allocation. + * Save the starting cluster. + */ + length /= secsize*clustersize; + x = clustalloc(Sof); + for(n = 0; n < length-1; n++) + clustalloc(0); + clustalloc(Eof); + } + else + x = 0; + + /* + * Add the filename to the root. + */ +fprint(2, "add %s at clust %lux\n", d->name, x); + addrname(p, d, *argv, x); + free(d); + } + + /* + * write the fats and root + */ + if(commit) { + if(seek(disk->wfd, nresrv*secsize, 0) < 0) + fatal("seek to fat #1: %r"); + if(write(disk->wfd, fat, fatsecs*secsize) < 0) + fatal("writing fat #1: %r"); + if(write(disk->wfd, fat, fatsecs*secsize) < 0) + fatal("writing fat #2: %r"); + if(write(disk->wfd, root, rootsecs*secsize) < 0) + fatal("writing root: %r"); + } + + free(fat); + free(root); +} + +/* + * allocate a cluster + */ +ulong +clustalloc(int flag) +{ + ulong o, x; + + if(flag != Sof){ + x = (flag == Eof) ? 0xffff : (fatlast+1); + if(fatbits == 12){ + x &= 0xfff; + o = (3*fatlast)/2; + if(fatlast & 1){ + fat[o] = (fat[o]&0x0f) | (x<<4); + fat[o+1] = (x>>4); + } else { + fat[o] = x; + fat[o+1] = (fat[o+1]&0xf0) | ((x>>8) & 0x0F); + } + } else { + o = 2*fatlast; + fat[o] = x; + fat[o+1] = x>>8; + } + } + + if(flag == Eof) + return 0; + else{ + ++fatlast; + if(fatlast >= clusters) + sysfatal("data does not fit on disk (%d %d)", fatlast, clusters); + return fatlast; + } +} + +void +putname(char *p, Dosdir *d) +{ + int i; + + memset(d->name, ' ', sizeof d->name+sizeof d->ext); + for(i = 0; i< sizeof(d->name); i++){ + if(*p == 0 || *p == '.') + break; + d->name[i] = toupper(*p++); + } + p = strrchr(p, '.'); + if(p){ + for(i = 0; i < sizeof d->ext; i++){ + if(*++p == 0) + break; + d->ext[i] = toupper(*p); + } + } +} + +void +puttime(Dosdir *d) +{ + Tm *t = localtime(time(0)); + ushort x; + + x = (t->hour<<11) | (t->min<<5) | (t->sec>>1); + d->time[0] = x; + d->time[1] = x>>8; + x = ((t->year-80)<<9) | ((t->mon+1)<<5) | t->mday; + d->date[0] = x; + d->date[1] = x>>8; +} + +void +addrname(uchar *entry, Dir *dir, char *name, ulong start) +{ + char *s; + Dosdir *d; + + s = strrchr(name, '/'); + if(s) + s++; + else + s = name; + + d = (Dosdir*)entry; + putname(s, d); + if(strcmp(s, "9load") == 0) + d->attr = DSYSTEM; + else + d->attr = 0; + puttime(d); + d->start[0] = start; + d->start[1] = start>>8; + d->length[0] = dir->length; + d->length[1] = dir->length>>8; + d->length[2] = dir->length>>16; + d->length[3] = dir->length>>24; +} diff --git a/sys/src/cmd/disk/kfs/9p1.c b/sys/src/cmd/disk/kfs/9p1.c new file mode 100755 index 000000000..2a3b5e9c8 --- /dev/null +++ b/sys/src/cmd/disk/kfs/9p1.c @@ -0,0 +1,1444 @@ +#include "all.h" +#include "9p1.h" + +/* + * buggery to give false qid for + * the top 2 levels of the dump fs + */ +void +mkqid(Qid* qid, Dentry *d, int buggery) +{ + int c; + + if(buggery && d->qid.path == QPROOT && (d->qid.path & QPDIR)){ + c = d->name[0]; + if(c >= '0' && c <= '9'){ + qid->path = 3; + qid->vers = d->qid.version; + qid->type = QTDIR; + + c = (c-'0')*10 + (d->name[1]-'0'); + if(c >= 1 && c <= 12) + qid->path = 4; + return; + } + } + + mkqid9p2(qid, &d->qid, d->mode); +} + +int +mkqidcmp(Qid* qid, Dentry *d) +{ + Qid tmp; + + mkqid(&tmp, d, 1); + if(qid->path==tmp.path && (qid->type&QTDIR)==(tmp.type&QTDIR)) + return 0; + return Eqid; +} + +void +f_nop(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + + USED(in); + USED(ou); + if(CHAT(cp)) + print("c_nop %d\n", cp->chan); +} + +void +f_flush(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + + USED(in); + USED(ou); + if(CHAT(cp)) + print("c_flush %d\n", cp->chan); + runlock(&cp->reflock); + wlock(&cp->reflock); + wunlock(&cp->reflock); + rlock(&cp->reflock); +} + +void +f_session(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + if(CHAT(cp)) + print("c_session %d\n", cp->chan); + + memmove(cp->rchal, in->chal, sizeof(cp->rchal)); + if(wstatallow || cp == cons.srvchan){ + memset(ou->chal, 0, sizeof(ou->chal)); + memset(ou->authid, 0, sizeof(ou->authid)); + }else{ + mkchallenge(cp); + memmove(ou->chal, cp->chal, sizeof(ou->chal)); + memmove(ou->authid, nvr.authid, sizeof(ou->authid)); + } + sprint(ou->authdom, "%s.%s", service, nvr.authdom); + fileinit(cp); +} + +void +f_attach(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + Iobuf *p; + Dentry *d; + File *f; + int u; + Filsys *fs; + long raddr; + + if(CHAT(cp)) { + print("c_attach %d\n", cp->chan); + print(" fid = %d\n", in->fid); + print(" uid = %s\n", in->uname); + print(" arg = %s\n", in->aname); + } + + ou->qid = QID9P1(0,0); + ou->fid = in->fid; + if(!in->aname[0]) /* default */ + strncpy(in->aname, filesys[0].name, sizeof(in->aname)); + p = 0; + f = filep(cp, in->fid, 1); + if(!f) { + ou->err = Efid; + goto out; + } + u = -1; + if(cp != cons.chan){ + if(authorize(cp, in, ou) == 0 || strcmp(in->uname, "adm") == 0){ + ou->err = Eauth; + goto out; + } + u = strtouid(in->uname); + if(u < 0){ + ou->err = Ebadu; + goto out; + } + } + + fs = fsstr(in->aname); + if(fs == 0) { + ou->err = Ebadspc; + goto out; + } + raddr = getraddr(fs->dev); + p = getbuf(fs->dev, raddr, Bread); + d = getdir(p, 0); + if(!d || checktag(p, Tdir, QPROOT) || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + f->uid = u; + if(iaccess(f, d, DREAD)) { + ou->err = Eaccess; + goto out; + } + accessdir(p, d, FREAD); + mkqid(&f->qid, d, 1); + f->fs = fs; + f->addr = raddr; + f->slot = 0; + f->open = 0; + freewp(f->wpath); + f->wpath = 0; + + mkqid9p1(&ou->qid, &f->qid); + +out: + if(p) + putbuf(p); + if(f) { + qunlock(f); + if(ou->err) + freefp(f); + } +} + +void +f_clone(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + File *f1, *f2; + int fid, fid1; + + if(CHAT(cp)) { + print("c_clone %d\n", cp->chan); + print(" old fid = %d\n", in->fid); + print(" new fid = %d\n", in->newfid); + } + + fid = in->fid; + fid1 = in->newfid; + + f1 = 0; + f2 = 0; + if(fid < fid1) { + f1 = filep(cp, fid, 0); + f2 = filep(cp, fid1, 1); + } else + if(fid1 < fid) { + f2 = filep(cp, fid1, 1); + f1 = filep(cp, fid, 0); + } + if(!f1 || !f2) { + ou->err = Efid; + goto out; + } + + + f2->fs = f1->fs; + f2->addr = f1->addr; + f2->open = f1->open & ~FREMOV; + f2->uid = f1->uid; + f2->slot = f1->slot; + f2->qid = f1->qid; + + freewp(f2->wpath); + f2->wpath = getwp(f1->wpath); + +out: + ou->fid = fid; + if(f1) + qunlock(f1); + if(f2) + qunlock(f2); +} + +void +f_walk(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + Iobuf *p, *p1; + Dentry *d, *d1; + File *f; + Wpath *w, *ow; + int slot; + long addr; + + if(CHAT(cp)) { + print("c_walk %d\n", cp->chan); + print(" fid = %d\n", in->fid); + print(" name = %s\n", in->name); + } + + ou->fid = in->fid; + ou->qid = QID9P1(0,0); + p = 0; + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(!(d->mode & DDIR)) { + ou->err = Edir1; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + if(cp != cons.chan && iaccess(f, d, DEXEC)) { + ou->err = Eaccess; + goto out; + } + accessdir(p, d, FREAD); + if(strcmp(in->name, ".") == 0) + goto setdot; + if(strcmp(in->name, "..") == 0) { + if(f->wpath == 0) + goto setdot; + putbuf(p); + p = 0; + addr = f->wpath->addr; + slot = f->wpath->slot; + p1 = getbuf(f->fs->dev, addr, Bread); + d1 = getdir(p1, slot); + if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) { + if(p1) + putbuf(p1); + ou->err = Ephase; + goto out; + } + ow = f->wpath; + f->wpath = ow->up; + putwp(ow); + goto found; + } + for(addr=0;; addr++) { + p1 = dnodebuf(p, d, addr, 0); + if(!p1 || checktag(p1, Tdir, d->qid.path) ) { + if(p1) + putbuf(p1); + ou->err = Eentry; + goto out; + } + for(slot=0; slot<DIRPERBUF; slot++) { + d1 = getdir(p1, slot); + if(!(d1->mode & DALLOC)) + continue; + if(strncmp(in->name, d1->name, sizeof(in->name))) + continue; + /* + * update walk path + */ + w = newwp(); + if(!w) { + ou->err = Ewalk; + putbuf(p1); + goto out; + } + w->addr = f->addr; + w->slot = f->slot; + w->up = f->wpath; + f->wpath = w; + slot += DIRPERBUF*addr; + goto found; + } + putbuf(p1); + } + +found: + f->addr = p1->addr; + mkqid(&f->qid, d1, 1); + putbuf(p1); + f->slot = slot; + +setdot: + mkqid9p1(&ou->qid, &f->qid); + f->open = 0; + +out: + if(p) + putbuf(p); + if(f) + qunlock(f); +} + +void +f_clunk(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + File *f; + Tlock *t; + long tim; + + if(CHAT(cp)) { + print("c_clunk %d\n", cp->chan); + print(" fid = %d\n", in->fid); + } + + f = filep(cp, in->fid, 0); + if(!f) { + print("%p\n", f); + ou->err = Efid; + goto out; + } + if(t = f->tlock) { + tim = time(0); + if(t->time < tim || t->file != f) + ou->err = Ebroken; + t->time = 0; /* free the lock */ + f->tlock = 0; + } + if(f->open & FREMOV) + ou->err = doremove(f, 0); + f->open = 0; + freewp(f->wpath); + freefp(f); + +out: + if(f) + qunlock(f); + ou->fid = in->fid; +} + +void +f_clwalk(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + int er, fid; + + if(CHAT(cp)) + print("c_clwalk macro\n"); + + f_clone(cp, in, ou); /* sets tag, fid */ + if(ou->err) + return; + fid = in->fid; + in->fid = in->newfid; + f_walk(cp, in, ou); /* sets tag, fid, qid */ + er = ou->err; + if(er == Eentry) { + /* + * if error is "no entry" + * return non error and fid + */ + ou->err = 0; + f_clunk(cp, in, ou); /* sets tag, fid */ + ou->err = 0; + ou->fid = fid; + if(CHAT(cp)) + print(" error: %s\n", errstring[er]); + return; + } + if(er) { + /* + * if any other error + * return an error + */ + ou->err = 0; + f_clunk(cp, in, ou); /* sets tag, fid */ + ou->err = er; + return; + } + /* + * non error + * return newfid + */ +} + +void +f_open(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + Iobuf *p; + Dentry *d; + File *f; + Tlock *t; + Qid qid; + int ro, fmod; + + if(CHAT(cp)) { + print("c_open %d\n", cp->chan); + print(" fid = %d\n", in->fid); + print(" mode = %o\n", in->mode); + } + + p = 0; + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + + /* + * if remove on close, check access here + */ + ro = isro(f->fs->dev) || (cp != cons.chan && writegroup && !ingroup(f->uid, writegroup)); + if(in->mode & MRCLOSE) { + if(ro) { + ou->err = Eronly; + goto out; + } + /* + * check on parent directory of file to be deleted + */ + if(f->wpath == 0 || f->wpath->addr == f->addr) { + ou->err = Ephase; + goto out; + } + p = getbuf(f->fs->dev, f->wpath->addr, Bread); + d = getdir(p, f->wpath->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ephase; + goto out; + } + if(iaccess(f, d, DWRITE)) { + ou->err = Eaccess; + goto out; + } + putbuf(p); + } + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + mkqid(&qid, d, 1); + switch(in->mode & 7) { + + case MREAD: + if(iaccess(f, d, DREAD) && !writeallow) + goto badaccess; + fmod = FREAD; + break; + + case MWRITE: + if((d->mode & DDIR) || + (iaccess(f, d, DWRITE) && !writeallow)) + goto badaccess; + if(ro) { + ou->err = Eronly; + goto out; + } + fmod = FWRITE; + break; + + case MBOTH: + if((d->mode & DDIR) || + (iaccess(f, d, DREAD) && !writeallow) || + (iaccess(f, d, DWRITE) && !writeallow)) + goto badaccess; + if(ro) { + ou->err = Eronly; + goto out; + } + fmod = FREAD+FWRITE; + break; + + case MEXEC: + if((d->mode & DDIR) || + iaccess(f, d, DEXEC)) + goto badaccess; + fmod = FREAD; + break; + + default: + ou->err = Emode; + goto out; + } + if(in->mode & MTRUNC) { + if((d->mode & DDIR) || + (iaccess(f, d, DWRITE) && !writeallow)) + goto badaccess; + if(ro) { + ou->err = Eronly; + goto out; + } + } + t = 0; + if(d->mode & DLOCK) { + t = tlocked(p, d); + if(t == 0) { + ou->err = Elocked; + goto out; + } + t->file = f; + } + if(in->mode & MRCLOSE) + fmod |= FREMOV; + f->open = fmod; + if(in->mode & MTRUNC) + if(!(d->mode & DAPND)) + dtrunc(p, d); + f->tlock = t; + f->lastra = 0; + mkqid9p1(&ou->qid, &qid); + goto out; + +badaccess: + ou->err = Eaccess; + f->open = 0; + +out: + if(p) + putbuf(p); + if(f) + qunlock(f); + ou->fid = in->fid; +} + +void +f_create(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + Iobuf *p, *p1; + Dentry *d, *d1; + File *f; + int slot, slot1, fmod; + long addr, addr1, path; + Qid qid; + Tlock *t; + Wpath *w; + + if(CHAT(cp)) { + print("c_create %d\n", cp->chan); + print(" fid = %d\n", in->fid); + print(" name = %s\n", in->name); + print(" perm = %lx+%lo\n", (in->perm>>28)&0xf, + in->perm&0777); + print(" mode = %d\n", in->mode); + } + + p = 0; + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + if(isro(f->fs->dev) || (cp != cons.chan && writegroup && !ingroup(f->uid, writegroup))) { + ou->err = Eronly; + goto out; + } + + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + if(!(d->mode & DDIR)) { + ou->err = Edir2; + goto out; + } + if(cp != cons.chan && iaccess(f, d, DWRITE) && !writeallow) { + ou->err = Eaccess; + goto out; + } + accessdir(p, d, FREAD); + if(!strncmp(in->name, ".", sizeof(in->name)) || + !strncmp(in->name, "..", sizeof(in->name))) { + ou->err = Edot; + goto out; + } + if(checkname(in->name)) { + ou->err = Ename; + goto out; + } + addr1 = 0; + slot1 = 0; /* set */ + for(addr=0;; addr++) { + p1 = dnodebuf(p, d, addr, 0); + if(!p1) { + if(addr1) + break; + p1 = dnodebuf(p, d, addr, Tdir); + } + if(p1 == 0) { + ou->err = Efull; + goto out; + } + if(checktag(p1, Tdir, d->qid.path)) { + putbuf(p1); + goto phase; + } + for(slot=0; slot<DIRPERBUF; slot++) { + d1 = getdir(p1, slot); + if(!(d1->mode & DALLOC)) { + if(!addr1) { + addr1 = p1->addr; + slot1 = slot + addr*DIRPERBUF; + } + continue; + } + if(!strncmp(in->name, d1->name, sizeof(in->name))) { + putbuf(p1); + ou->err = Eexist; + goto out; + } + } + putbuf(p1); + } + switch(in->mode & 7) { + case MEXEC: + case MREAD: /* seems only useful to make directories */ + fmod = FREAD; + break; + + case MWRITE: + fmod = FWRITE; + break; + + case MBOTH: + fmod = FREAD+FWRITE; + break; + + default: + ou->err = Emode; + goto out; + } + if(in->perm & PDIR) + if((in->mode & MTRUNC) || (in->perm & PAPND) || (fmod & FWRITE)) + goto badaccess; + /* + * do it + */ + path = qidpathgen(&f->fs->dev); + p1 = getbuf(f->fs->dev, addr1, Bread|Bimm|Bmod); + d1 = getdir(p1, slot1); + if(!d1 || checktag(p1, Tdir, d->qid.path)) { + if(p1) + putbuf(p1); + goto phase; + } + if(d1->mode & DALLOC) { + putbuf(p1); + goto phase; + } + + strncpy(d1->name, in->name, sizeof(in->name)); + /* + * bogus argument passing -- see console.c + */ + if(cp == cons.chan) { + d1->uid = cons.uid; + d1->gid = cons.gid; + } else { + d1->uid = f->uid; + d1->gid = d->gid; + in->perm &= d->mode | ~0666; + if(in->perm & PDIR) + in->perm &= d->mode | ~0777; + } + d1->qid.path = path; + d1->qid.version = 0; + d1->mode = DALLOC | (in->perm & 0777); + if(in->perm & PDIR) { + d1->mode |= DDIR; + d1->qid.path |= QPDIR; + } + if(in->perm & PAPND) + d1->mode |= DAPND; + t = 0; + if(in->perm & PLOCK) { + d1->mode |= DLOCK; + t = tlocked(p1, d1); + } + accessdir(p1, d1, FWRITE); + mkqid(&qid, d1, 0); + putbuf(p1); + accessdir(p, d, FWRITE); + + /* + * do a walk to new directory entry + */ + w = newwp(); + if(!w) { + ou->err = Ewalk; + goto out; + } + w->addr = f->addr; + w->slot = f->slot; + w->up = f->wpath; + f->wpath = w; + f->qid = qid; + f->tlock = t; + f->lastra = 0; + if(in->mode & MRCLOSE) + fmod |= FREMOV; + f->open = fmod; + f->addr = addr1; + f->slot = slot1; + if(t) + t->file = f; + mkqid9p1(&ou->qid, &qid); + goto out; + +badaccess: + ou->err = Eaccess; + goto out; + +phase: + ou->err = Ephase; + +out: + if(p) + putbuf(p); + if(f) + qunlock(f); + ou->fid = in->fid; +} + +void +f_read(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + Iobuf *p, *p1; + File *f; + Dentry *d, *d1; + Tlock *t; + long addr, offset, tim; + int nread, count, n, o, slot; + + if(CHAT(cp)) { + print("c_read %d\n", cp->chan); + print(" fid = %d\n", in->fid); + print(" offset = %ld\n", in->offset); + print(" count = %ld\n", in->count); + } + + p = 0; + count = in->count; + offset = in->offset; + nread = 0; + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + if(!(f->open & FREAD)) { + ou->err = Eopen; + goto out; + } + if(count < 0 || count > MAXDAT) { + ou->err = Ecount; + goto out; + } + if(offset < 0) { + ou->err = Eoffset; + goto out; + } + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + if(t = f->tlock) { + tim = time(0); + if(t->time < tim || t->file != f) { + ou->err = Ebroken; + goto out; + } + /* renew the lock */ + t->time = tim + TLOCK; + } + accessdir(p, d, FREAD); + if(d->mode & DDIR) { + addr = 0; + goto dread; + } + if(offset+count > d->size) + count = d->size - offset; + while(count > 0) { + addr = offset / BUFSIZE; + if(addr == f->lastra+1) + dbufread(p, d, addr+1); + f->lastra = addr; + o = offset % BUFSIZE; + n = BUFSIZE - o; + if(n > count) + n = count; + p1 = dnodebuf(p, d, addr, 0); + if(p1) { + if(checktag(p1, Tfile, QPNONE)) { + ou->err = Ephase; + putbuf(p1); + goto out; + } + memmove(ou->data+nread, p1->iobuf+o, n); + putbuf(p1); + } else + memset(ou->data+nread, 0, n); + count -= n; + nread += n; + offset += n; + } + goto out; + +dread: + p1 = dnodebuf(p, d, addr, 0); + if(!p1) + goto out; + if(checktag(p1, Tdir, QPNONE)) { + ou->err = Ephase; + putbuf(p1); + goto out; + } + n = DIRREC; + for(slot=0; slot<DIRPERBUF; slot++) { + d1 = getdir(p1, slot); + if(!(d1->mode & DALLOC)) + continue; + if(offset >= n) { + offset -= n; + continue; + } + if(count < n) { + putbuf(p1); + goto out; + } + if(convD2M9p1(d1, ou->data+nread) != n) + print("dirread convD2M\n"); + nread += n; + count -= n; + } + putbuf(p1); + addr++; + goto dread; + +out: + count = in->count - nread; + if(count > 0) + memset(ou->data+nread, 0, count); + if(p) + putbuf(p); + if(f) + qunlock(f); + ou->fid = in->fid; + ou->count = nread; + if(CHAT(cp)) + print(" nread = %d\n", nread); +} + +void +f_write(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + Iobuf *p, *p1; + Dentry *d; + File *f; + Tlock *t; + long offset, addr, tim; + int count, nwrite, o, n; + + if(CHAT(cp)) { + print("c_write %d\n", cp->chan); + print(" fid = %d\n", in->fid); + print(" offset = %ld\n", in->offset); + print(" count = %ld\n", in->count); + } + + offset = in->offset; + count = in->count; + nwrite = 0; + p = 0; + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + if(!(f->open & FWRITE)) { + ou->err = Eopen; + goto out; + } + if(isro(f->fs->dev) || (cp != cons.chan && writegroup && !ingroup(f->uid, writegroup))) { + ou->err = Eronly; + goto out; + } + if(count < 0 || count > MAXDAT) { + ou->err = Ecount; + goto out; + } + if(offset < 0) { + ou->err = Eoffset; + goto out; + } + p = getbuf(f->fs->dev, f->addr, Bread|Bmod); + d = getdir(p, f->slot); + if(!d || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + if(t = f->tlock) { + tim = time(0); + if(t->time < tim || t->file != f) { + ou->err = Ebroken; + goto out; + } + /* renew the lock */ + t->time = tim + TLOCK; + } + accessdir(p, d, FWRITE); + if(d->mode & DAPND) + offset = d->size; + if(offset+count > d->size) + d->size = offset+count; + while(count > 0) { + addr = offset / BUFSIZE; + o = offset % BUFSIZE; + n = BUFSIZE - o; + if(n > count) + n = count; + p1 = dnodebuf(p, d, addr, Tfile); + if(p1 == 0) { + ou->err = Efull; + goto out; + } + if(checktag(p1, Tfile, d->qid.path)) { + putbuf(p1); + ou->err = Ephase; + goto out; + } + memmove(p1->iobuf+o, in->data+nwrite, n); + p1->flags |= Bmod; + putbuf(p1); + count -= n; + nwrite += n; + offset += n; + } + if(CHAT(cp)) + print(" nwrite = %d\n", nwrite); + +out: + if(p) + putbuf(p); + if(f) + qunlock(f); + ou->fid = in->fid; + ou->count = nwrite; +} + +int +doremove(File *f, int iscon) +{ + Iobuf *p, *p1; + Dentry *d, *d1; + long addr; + int slot, err; + + p = 0; + p1 = 0; + if(isro(f->fs->dev) || (f->cp != cons.chan && writegroup && !ingroup(f->uid, writegroup))) { + err = Eronly; + goto out; + } + /* + * check on parent directory of file to be deleted + */ + if(f->wpath == 0 || f->wpath->addr == f->addr) { + err = Ephase; + goto out; + } + p1 = getbuf(f->fs->dev, f->wpath->addr, Bread); + d1 = getdir(p1, f->wpath->slot); + if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) { + err = Ephase; + goto out; + } + if(!iscon && iaccess(f, d1, DWRITE)) { + err = Eaccess; + goto out; + } + accessdir(p1, d1, FWRITE); + putbuf(p1); + p1 = 0; + + /* + * check on file to be deleted + */ + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + err = Ealloc; + goto out; + } + if(err = mkqidcmp(&f->qid, d)) + goto out; + + /* + * if deleting a directory, make sure it is empty + */ + if((d->mode & DDIR)) + for(addr=0;; addr++) { + p1 = dnodebuf(p, d, addr, 0); + if(!p1) + break; + if(checktag(p1, Tdir, d->qid.path)) { + err = Ephase; + goto out; + } + for(slot=0; slot<DIRPERBUF; slot++) { + d1 = getdir(p1, slot); + if(!(d1->mode & DALLOC)) + continue; + err = Eempty; + goto out; + } + putbuf(p1); + } + + /* + * do it + */ + dtrunc(p, d); + memset(d, 0, sizeof(Dentry)); + settag(p, Tdir, QPNONE); + +out: + if(p1) + putbuf(p1); + if(p) + putbuf(p); + return err; +} + +void +f_remove(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + File *f; + + if(CHAT(cp)) { + print("c_remove %d\n", cp->chan); + print(" fid = %d\n", in->fid); + } + + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + ou->err = doremove(f, cp==cons.chan); + +out: + ou->fid = in->fid; + if(f) + qunlock(f); +} + +void +f_stat(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + Iobuf *p; + Dentry *d; + File *f; + + if(CHAT(cp)) { + print("c_stat %d\n", cp->chan); + print(" fid = %d\n", in->fid); + } + + p = 0; + memset(ou->stat, 0, sizeof(ou->stat)); + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + if(d->qid.path == QPROOT) /* stat of root gives time */ + d->atime = time(0); + if(convD2M9p1(d, ou->stat) != DIRREC) + print("stat convD2M\n"); + +out: + if(p) + putbuf(p); + if(f) + qunlock(f); + ou->fid = in->fid; +} + +void +f_wstat(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + Iobuf *p, *p1; + Dentry *d, *d1, xd; + File *f; + int slot; + long addr; + + if(CHAT(cp)) { + print("c_wstat %d\n", cp->chan); + print(" fid = %d\n", in->fid); + } + + p = 0; + p1 = 0; + d1 = 0; + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + if(isro(f->fs->dev) || (cp != cons.chan && writegroup && !ingroup(f->uid, writegroup))) { + ou->err = Eronly; + goto out; + } + + /* + * first get parent + */ + if(f->wpath) { + p1 = getbuf(f->fs->dev, f->wpath->addr, Bread); + d1 = getdir(p1, f->wpath->slot); + if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) { + ou->err = Ephase; + goto out; + } + } + + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ealloc; + goto out; + } + if(ou->err = mkqidcmp(&f->qid, d)) + goto out; + + convM2D9p1(in->stat, &xd); + if(CHAT(cp)) { + print(" d.name = %s\n", xd.name); + print(" d.uid = %d\n", xd.uid); + print(" d.gid = %d\n", xd.gid); + print(" d.mode = %.4x\n", xd.mode); + } + + /* + * if chown, + * must be god + */ + while(xd.uid != d->uid) { + if(wstatallow) /* set to allow chown during boot */ + break; + ou->err = Enotu; + goto out; + } + + /* + * if chgroup, + * must be either + * a) owner and in new group + * b) leader of both groups + */ + while(xd.gid != d->gid) { + if(wstatallow || writeallow) /* set to allow chgrp during boot */ + break; + if(d->uid == f->uid && ingroup(f->uid, xd.gid)) + break; + if(leadgroup(f->uid, xd.gid)) + if(leadgroup(f->uid, d->gid)) + break; + ou->err = Enotg; + goto out; + } + + /* + * if rename, + * must have write permission in parent + */ + if(xd.name[0] == 0) + strncpy(xd.name, d->name, sizeof(xd.name)); + while(strncmp(d->name, xd.name, sizeof(d->name)) != 0) { + if(checkname(xd.name)) { + ou->err = Ename; + goto out; + } + + if(strcmp(xd.name, ".") == 0 || strcmp(xd.name, "..") == 0) { + ou->err = Ename; + goto out; + } + + /* + * drop entry to prevent lock, then + * check that destination name is unique, + */ + putbuf(p); + for(addr=0;; addr++) { + p = dnodebuf(p1, d1, addr, 0); + if(!p) + break; + if(checktag(p, Tdir, d1->qid.path)) { + putbuf(p); + continue; + } + for(slot=0; slot<DIRPERBUF; slot++) { + d = getdir(p, slot); + if(!(d->mode & DALLOC)) + continue; + if(!strncmp(xd.name, d->name, sizeof(xd.name))) { + ou->err = Eexist; + goto out; + } + } + putbuf(p); + } + + /* + * reacquire entry + */ + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + if(!d || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + ou->err = Ephase; + goto out; + } + + if(wstatallow || writeallow) /* set to allow rename during boot */ + break; + if(!d1 || iaccess(f, d1, DWRITE)) { + ou->err = Eaccess; + goto out; + } + break; + } + + /* + * if mode/time, either + * a) owner + * b) leader of either group + */ + while(d->mtime != xd.mtime || + ((d->mode^xd.mode) & (DAPND|DLOCK|0777))) { + if(wstatallow) /* set to allow chmod during boot */ + break; + if(d->uid == f->uid) + break; + if(leadgroup(f->uid, xd.gid)) + break; + if(leadgroup(f->uid, d->gid)) + break; + ou->err = Enotu; + goto out; + } + d->mtime = xd.mtime; + d->uid = xd.uid; + d->gid = xd.gid; + d->mode = (xd.mode & (DAPND|DLOCK|0777)) | (d->mode & (DALLOC|DDIR)); + + strncpy(d->name, xd.name, sizeof(d->name)); + if(wstatallow) { + p->flags |= Bmod; + if(xd.atime) + d->atime = xd.atime; + if(xd.mtime) + d->mtime = xd.mtime; + } else + accessdir(p, d, FWSTAT); + +out: + if(p) + putbuf(p); + if(p1) + putbuf(p1); + if(f) + qunlock(f); + ou->fid = in->fid; +} + +void +(*call9p1[MAXSYSCALL])(Chan*, Oldfcall*, Oldfcall*) = +{ + [Tnop9p1] f_nop, + [Tosession9p1] f_session, + [Tsession9p1] f_session, + [Tflush9p1] f_flush, + [Toattach9p1] f_attach, + [Tattach9p1] f_attach, + [Tclone9p1] f_clone, + [Twalk9p1] f_walk, + [Topen9p1] f_open, + [Tcreate9p1] f_create, + [Tread9p1] f_read, + [Twrite9p1] f_write, + [Tclunk9p1] f_clunk, + [Tremove9p1] f_remove, + [Tstat9p1] f_stat, + [Twstat9p1] f_wstat, + [Tclwalk9p1] f_clwalk, +}; + +static void +send(Chan *c, uchar *buf, int n) +{ + int fd, m; + + fd = c->chan; + m = write(fd, buf, n); + if(m == n) + return; + panic("write failed"); +} + +void +error9p1(Chan *c, uchar *buf) +{ + buf[0] = Rnop9p1; + buf[1] = ~0; + buf[2] = ~0; + + send(c, buf, 3); +} + +void +serve9p1(Chan *chan, uchar *ib, int nib) +{ + int n, t; + uchar inbuf[MAXMSG+MAXDAT], outbuf[MAXMSG+MAXDAT]; + Oldfcall fi, fo; + + for(;;){ + if(nib){ + memmove(inbuf, ib, nib); + n = nib; + nib = 0; + }else + n = read(chan->chan, inbuf, sizeof inbuf); + if(chat) + print("read msg %d\n", n); + if(n == 0 && (chan == cons.srvchan || chan == cons.chan)) + continue; + if(n <= 0) + return; + if(convM2S9p1(inbuf, &fi, n) != n){ + error9p1(chan, outbuf); + continue; + } + + t = fi.type; + if(t < 0 || t >= MAXSYSCALL || (t&1) || !call9p1[t]) { + print("9p1: bad message type\n"); + error9p1(chan, outbuf); + continue; + } + + if(CHAT(chan)) + print("9p1: fi %O\n", &fi); + + /* + * set up reply message + */ + fo.err = 0; + if(t == Tread9p1) + fo.data = (char*)outbuf + 8; + + /* + * call the file system + */ + cons.work.count++; + cons.rate.count += n; + + /* + * call the file system + */ + rlock(&mainlock); + rlock(&chan->reflock); + + (*call9p1[t])(chan, &fi, &fo); + + runlock(&chan->reflock); + runlock(&mainlock); + + fo.type = t+1; + fo.tag = fi.tag; + + if(chat) + print("9p1: fo %O\n", &fo); + + if(fo.err) { + strcpy(fo.ename, errstring[fo.err]); + if(CHAT(cp)) + print(" error: %s\n", fo.ename); + fo.type = Terror9p1+1; + } + + n = convS2M9p1(&fo, outbuf); + if(n == 0) { + print("9p1: bad S2M conversion\n"); + error9p1(chan, outbuf); + continue; + } + + cons.rate.count += n; + send(chan, outbuf, n); + } +} diff --git a/sys/src/cmd/disk/kfs/9p1.h b/sys/src/cmd/disk/kfs/9p1.h new file mode 100755 index 000000000..7949ae0e0 --- /dev/null +++ b/sys/src/cmd/disk/kfs/9p1.h @@ -0,0 +1,113 @@ +#define DIRREC 116 /* size of a directory ascii record */ +#define ERRREC 64 /* size of a error record */ +#define MAXMSG 160 /* max header sans data */ + +typedef struct Oldfcall Oldfcall; + +struct Oldfcall +{ + char type; + ushort fid; + short err; + short tag; + union + { + struct + { + short uid; /* T-Userstr */ + short oldtag; /* T-nFlush */ + Qid9p1 qid; /* R-Attach, R-Clwalk, R-Walk, + * R-Open, R-Create */ + char rauth[AUTHENTLEN]; /* R-attach */ + }; + struct + { + char uname[NAMELEN]; /* T-nAttach */ + char aname[NAMELEN]; /* T-nAttach */ + char ticket[TICKETLEN]; /* T-attach */ + char auth[AUTHENTLEN]; /* T-attach */ + }; + struct + { + char ename[ERRREC]; /* R-nError */ + char chal[CHALLEN]; /* T-session, R-session */ + char authid[NAMELEN]; /* R-session */ + char authdom[DOMLEN]; /* R-session */ + }; + struct + { + char name[NAMELEN]; /* T-Walk, T-Clwalk, T-Create, T-Remove */ + long perm; /* T-Create */ + ushort newfid; /* T-Clone, T-Clwalk */ + char mode; /* T-Create, T-Open */ + }; + struct + { + long offset; /* T-Read, T-Write */ + long count; /* T-Read, T-Write, R-Read */ + char* data; /* T-Write, R-Read */ + }; + struct + { + char stat[DIRREC]; /* T-Wstat, R-Stat */ + }; + }; +}; + +/* + * P9 protocol message types + */ +enum +{ + Tnop9p1 = 50, + Rnop9p1, + Tosession9p1 = 52, + Rosession9p1, + Terror9p1 = 54, /* illegal */ + Rerror9p1, + Tflush9p1 = 56, + Rflush9p1, + Toattach9p1 = 58, + Roattach9p1, + Tclone9p1 = 60, + Rclone9p1, + Twalk9p1 = 62, + Rwalk9p1, + Topen9p1 = 64, + Ropen9p1, + Tcreate9p1 = 66, + Rcreate9p1, + Tread9p1 = 68, + Rread9p1, + Twrite9p1 = 70, + Rwrite9p1, + Tclunk9p1 = 72, + Rclunk9p1, + Tremove9p1 = 74, + Rremove9p1, + Tstat9p1 = 76, + Rstat9p1, + Twstat9p1 = 78, + Rwstat9p1, + Tclwalk9p1 = 80, + Rclwalk9p1, + Tauth9p1 = 82, /* illegal */ + Rauth9p1, /* illegal */ + Tsession9p1 = 84, + Rsession9p1, + Tattach9p1 = 86, + Rattach9p1, + + MAXSYSCALL +}; + +int convD2M9p1(Dentry*, char*); +int convM2D9p1(char*, Dentry*); +int convM2S9p1(uchar*, Oldfcall*, int); +int convS2M9p1(Oldfcall*, uchar*); +void fcall9p1(Chan*, Oldfcall*, Oldfcall*); +int authorize(Chan*, Oldfcall*, Oldfcall*); + +void (*call9p1[MAXSYSCALL])(Chan*, Oldfcall*, Oldfcall*); + +extern Nvrsafe nvr; diff --git a/sys/src/cmd/disk/kfs/9p12.c b/sys/src/cmd/disk/kfs/9p12.c new file mode 100755 index 000000000..a1865b5e7 --- /dev/null +++ b/sys/src/cmd/disk/kfs/9p12.c @@ -0,0 +1,114 @@ +#include "all.h" + +static int +readmsg(Chan *c, void *abuf, int n, int *ninep) +{ + int fd, len; + uchar *buf; + + buf = abuf; + fd = c->chan; + qlock(&c->rlock); + if(readn(fd, buf, 3) != 3){ + qunlock(&c->rlock); + print("readn(3) fails: %r\n"); + return -1; + } + if((50 <= buf[0] && buf[0] <= 87 && (buf[0]&1)==0 && GBIT16(buf+1) == 0xFFFF) + || buf[0] == 86 /* Tattach */){ + *ninep = 1; + /* assume message boundaries */ + n = read(fd, buf+3, n-3); + if(n < 0){ + qunlock(&c->rlock); + return -1; + } + return n+3; + } + + *ninep = 2; + if(read(fd, buf+3, 1) != 1){ + qunlock(&c->rlock); + print("read(1) fails: %r\n"); + return -1; + } + len = GBIT32(buf); + if(len > n){ + print("msg too large\n"); + qunlock(&c->rlock); + return -1; + } + if(readn(fd, buf+4, len-4) != len-4){ + print("readn(%d) fails: %r\n", len-4); + qunlock(&c->rlock); + return -1; + } + qunlock(&c->rlock); + return len; +} + +int +startserveproc(void (*f)(Chan*, uchar*, int), char *name, Chan *c, uchar *b, int nb) +{ + int pid; + + switch(pid = rfork(RFMEM|RFPROC)){ + case -1: + panic("can't fork"); + case 0: + break; + default: + return pid; + } + procname = name; + f(c, b, nb); + _exits(nil); + return -1; /* can't happen */ +} + +void +serve(Chan *chan) +{ + int i, nin, p9, npid; + uchar inbuf[1024]; + void (*s)(Chan*, uchar*, int); + int *pid; + Waitmsg *w; + + p9 = 0; + if((nin = readmsg(chan, inbuf, sizeof inbuf, &p9)) < 0) + return; + + switch(p9){ + default: + print("unknown 9P type\n"); + return; + case 1: + s = serve9p1; + break; + case 2: + s = serve9p2; + break; + } + + pid = malloc(sizeof(pid)*(conf.nserve-1)); + if(pid == nil) + return; + for(i=1; i<conf.nserve; i++) + pid[i-1] = startserveproc(s, "srv", chan, nil, 0); + + (*s)(chan, inbuf, nin); + + /* wait till all other servers for this chan are done */ + for(npid = conf.nserve-1; npid > 0;){ + w = wait(); + if(w == 0) + break; + for(i = 0; i < conf.nserve-1; i++) + if(pid[i] == w->pid) + npid--; + free(w); + } + free(pid); +} + diff --git a/sys/src/cmd/disk/kfs/9p1lib.c b/sys/src/cmd/disk/kfs/9p1lib.c new file mode 100755 index 000000000..d61d76220 --- /dev/null +++ b/sys/src/cmd/disk/kfs/9p1lib.c @@ -0,0 +1,461 @@ +#include "all.h" +#include "9p1.h" + +#define CHAR(x) *p++ = f->x +#define SHORT(x) { ulong vvv = f->x; p[0] = vvv; p[1] = vvv>>8; p += 2; } +#define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4 +#define LONG(x) { ulong vvv = f->x; VLONG(vvv); } +#define BYTES(x,n) memmove(p, f->x, n); p += n +#define STRING(x,n) strncpy((char*)p, f->x, n); p += n + +int +convS2M9p1(Oldfcall *f, uchar *ap) +{ + uchar *p; + int t; + + p = ap; + CHAR(type); + t = f->type; + SHORT(tag); + switch(t) + { + default: + print("convS2M9p1: bad type: %d\n", t); + return 0; + + case Tnop9p1: + case Tosession9p1: + break; + + case Tsession9p1: + BYTES(chal, sizeof(f->chal)); + break; + + case Tflush9p1: + SHORT(oldtag); + break; + + case Tattach9p1: + SHORT(fid); + STRING(uname, sizeof(f->uname)); + STRING(aname, sizeof(f->aname)); + BYTES(ticket, sizeof(f->ticket)); + BYTES(auth, sizeof(f->auth)); + break; + + case Toattach9p1: + SHORT(fid); + STRING(uname, sizeof(f->uname)); + STRING(aname, sizeof(f->aname)); + BYTES(ticket, NAMELEN); + break; + + case Tclone9p1: + SHORT(fid); + SHORT(newfid); + break; + + case Twalk9p1: + SHORT(fid); + STRING(name, sizeof(f->name)); + break; + + case Tclwalk9p1: + SHORT(fid); + SHORT(newfid); + STRING(name, sizeof(f->name)); + break; + + case Topen9p1: + SHORT(fid); + CHAR(mode); + break; + + case Tcreate9p1: + SHORT(fid); + STRING(name, sizeof(f->name)); + LONG(perm); + CHAR(mode); + break; + + case Tread9p1: + SHORT(fid); + LONG(offset); VLONG(0); + SHORT(count); + break; + + case Twrite9p1: + SHORT(fid); + LONG(offset); VLONG(0); + SHORT(count); + p++; + if((uchar*)p == (uchar*)f->data) { + p += f->count; + break; + } + BYTES(data, f->count); + break; + + case Tclunk9p1: + case Tremove9p1: + case Tstat9p1: + SHORT(fid); + break; + + case Twstat9p1: + SHORT(fid); + BYTES(stat, sizeof(f->stat)); + break; +/* + */ + case Rnop9p1: + case Rosession9p1: + case Rflush9p1: + break; + + case Rsession9p1: + BYTES(chal, sizeof(f->chal)); + BYTES(authid, sizeof(f->authid)); + BYTES(authdom, sizeof(f->authdom)); + break; + + case Rerror9p1: + STRING(ename, sizeof(f->ename)); + break; + + case Rclone9p1: + case Rclunk9p1: + case Rremove9p1: + case Rwstat9p1: + SHORT(fid); + break; + + case Rwalk9p1: + case Ropen9p1: + case Rcreate9p1: + case Rclwalk9p1: + SHORT(fid); + LONG(qid.path); + LONG(qid.version); + break; + + case Rattach9p1: + SHORT(fid); + LONG(qid.path); + LONG(qid.version); + BYTES(rauth, sizeof(f->rauth)); + break; + + case Roattach9p1: + SHORT(fid); + LONG(qid.path); + LONG(qid.version); + break; + + case Rread9p1: + SHORT(fid); + SHORT(count); + p++; + if((uchar*)p == (uchar*)f->data) { + p += f->count; + break; + } + BYTES(data, f->count); + break; + + case Rwrite9p1: + SHORT(fid); + SHORT(count); + break; + + case Rstat9p1: + SHORT(fid); + BYTES(stat, sizeof(f->stat)); + break; + } + return p - (uchar*)ap; +} + +/* + * buggery to give false qid for + * the top 2 levels of the dump fs + */ +static ulong +fakeqid9p1(Dentry *f) +{ + ulong q; + int c; + + q = f->qid.path; + if(q == (QPROOT|QPDIR)) { + c = f->name[0]; + if(c >= '0' && c <= '9') { + q = 3|QPDIR; + c = (c-'0')*10 + (f->name[1]-'0'); + if(c >= 1 && c <= 12) + q = 4|QPDIR; + } + } + return q; +} + +int +convD2M9p1(Dentry *f, char *ap) +{ + uchar *p; + ulong q; + + p = (uchar*)ap; + STRING(name, sizeof(f->name)); + + memset(p, 0, 2*NAMELEN); + uidtostr((char*)p, f->uid); + p += NAMELEN; + + uidtostr((char*)p, f->gid); + p += NAMELEN; + + q = fakeqid9p1(f); + VLONG(q); + LONG(qid.version); + { + q = f->mode & 0x0fff; + if(f->mode & DDIR) + q |= PDIR; + if(f->mode & DAPND) + q |= PAPND; + if(f->mode & DLOCK) + q |= PLOCK; + VLONG(q); + } + LONG(atime); + LONG(mtime); + LONG(size); VLONG(0); + VLONG(0); + return p - (uchar*)ap; +} + +#undef CHAR +#undef SHORT +#undef LONG +#undef VLONG +#undef BYTES +#undef STRING + +#define CHAR(x) f->x = *p++ +#define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 +#define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4 +#define LONG(x) VLONG(f->x) +#define BYTES(x,n) memmove(f->x, p, n); p += n +#define STRING(x,n) memmove(f->x, p, n); p += n + +int +convM2S9p1(uchar *ap, Oldfcall *f, int n) +{ + uchar *p; + int t; + + p = ap; + CHAR(type); + t = f->type; + SHORT(tag); + switch(t) + { + default: + /* + * only whine if it couldn't be a 9P2000 Tversion9p1. + */ + if(t != 19 || ap[4] != 100) + print("convM2S9p1: bad type: %d\n", f->type); + return 0; + + case Tnop9p1: + case Tosession9p1: + break; + + case Tsession9p1: + BYTES(chal, sizeof(f->chal)); + break; + + case Tflush9p1: + SHORT(oldtag); + break; + + case Tattach9p1: + SHORT(fid); + BYTES(uname, sizeof(f->uname)); + BYTES(aname, sizeof(f->aname)); + BYTES(ticket, sizeof(f->ticket)); + BYTES(auth, sizeof(f->auth)); + break; + + case Toattach9p1: + SHORT(fid); + BYTES(uname, sizeof(f->uname)); + BYTES(aname, sizeof(f->aname)); + BYTES(ticket, NAMELEN); + break; + + case Tclone9p1: + SHORT(fid); + SHORT(newfid); + break; + + case Twalk9p1: + SHORT(fid); + BYTES(name, sizeof(f->name)); + break; + + case Tclwalk9p1: + SHORT(fid); + SHORT(newfid); + BYTES(name, sizeof(f->name)); + break; + + case Tremove9p1: + SHORT(fid); + break; + + case Topen9p1: + SHORT(fid); + CHAR(mode); + break; + + case Tcreate9p1: + SHORT(fid); + BYTES(name, sizeof(f->name)); + LONG(perm); + CHAR(mode); + break; + + case Tread9p1: + SHORT(fid); + LONG(offset); p += 4; + SHORT(count); + break; + + case Twrite9p1: + SHORT(fid); + LONG(offset); p += 4; + SHORT(count); + p++; + f->data = (char*)p; p += f->count; + break; + + case Tclunk9p1: + case Tstat9p1: + SHORT(fid); + break; + + case Twstat9p1: + SHORT(fid); + BYTES(stat, sizeof(f->stat)); + break; + +/* + */ + case Rnop9p1: + case Rosession9p1: + break; + + case Rsession9p1: + BYTES(chal, sizeof(f->chal)); + BYTES(authid, sizeof(f->authid)); + BYTES(authdom, sizeof(f->authdom)); + break; + + case Rerror9p1: + BYTES(ename, sizeof(f->ename)); + break; + + case Rflush9p1: + break; + + case Rclone9p1: + case Rclunk9p1: + case Rremove9p1: + case Rwstat9p1: + SHORT(fid); + break; + + case Rwalk9p1: + case Rclwalk9p1: + case Ropen9p1: + case Rcreate9p1: + SHORT(fid); + LONG(qid.path); + LONG(qid.version); + break; + + case Rattach9p1: + SHORT(fid); + LONG(qid.path); + LONG(qid.version); + BYTES(rauth, sizeof(f->rauth)); + break; + + case Roattach9p1: + SHORT(fid); + LONG(qid.path); + LONG(qid.version); + break; + + case Rread9p1: + SHORT(fid); + SHORT(count); + p++; + f->data = (char*)p; p += f->count; + break; + + case Rwrite9p1: + SHORT(fid); + SHORT(count); + break; + + case Rstat9p1: + SHORT(fid); + BYTES(stat, sizeof(f->stat)); + break; + } + if((uchar*)ap+n == p) + return n; + return 0; +} + +int +convM2D9p1(char *ap, Dentry *f) +{ + uchar *p; + char str[28]; + + p = (uchar*)ap; + BYTES(name, sizeof(f->name)); + + memmove(str, p, NAMELEN); + p += NAMELEN; + f->uid = strtouid(str); + + memmove(str, p, NAMELEN); + p += NAMELEN; + f->gid = strtouid(str); + + LONG(qid.path); + LONG(qid.version); + { + LONG(atime); + f->mode = (f->atime & 0x0fff) | DALLOC; + if(f->atime & PDIR) + f->mode |= DDIR; + if(f->atime & PAPND) + f->mode |= DAPND; + if(f->atime & PLOCK) + f->mode |= DLOCK; + } + LONG(atime); + LONG(mtime); + LONG(size); p += 4; + p += 4; + return p - (uchar*)ap; +} + diff --git a/sys/src/cmd/disk/kfs/9p2.c b/sys/src/cmd/disk/kfs/9p2.c new file mode 100755 index 000000000..0559d85c4 --- /dev/null +++ b/sys/src/cmd/disk/kfs/9p2.c @@ -0,0 +1,1865 @@ +#include "all.h" + +#define MSIZE (MAXDAT+128) + +static void +seterror(Fcall *ou, int err) +{ + + if(0 <= err && err < MAXERR) + ou->ename = errstring[err]; + else + ou->ename = "unknown error"; +} + +static int +fsversion(Chan* chan, Fcall* f, Fcall* r) +{ + if(f->msize < MSIZE) + r->msize = f->msize; + else + r->msize = MSIZE; + /* + * Should check the '.' stuff here. + * What happens if Tversion has already been seen? + */ + if(strcmp(f->version, VERSION9P) == 0){ + r->version = VERSION9P; + chan->msize = r->msize; + }else + r->version = "unknown"; + + fileinit(chan); + return 0; +} + +char *keyspec = "proto=p9any role=server"; + +static int +fsauth(Chan *chan, Fcall *f, Fcall *r) +{ + int err, fd; + char *aname; + File *file; + int afd; + AuthRpc *rpc; + + err = 0; + if(chan == cons.srvchan) + return Eauthmsg; + file = filep(chan, f->afid, 1); + if(file == nil) + return Efidinuse; + + /* forget any previous authentication */ + file->cuid = 0; + + if(access("/mnt/factotum", 0) < 0) + if((fd = open("/srv/factotum", ORDWR)) >= 0) + mount(fd, -1, "/mnt", MBEFORE, ""); + + afd = open("/mnt/factotum/rpc", ORDWR); + if(afd < 0){ + err = Esystem; + goto out; + } + rpc = auth_allocrpc(afd); + if(rpc == nil){ + close(afd); + err = Esystem; + goto out; + } + file->rpc = rpc; + if(auth_rpc(rpc, "start", keyspec, strlen(keyspec)) != ARok){ + err = Esystem; + goto out; + } + + aname = f->aname; + if(!aname[0]) + aname = "main"; + file->fs = fsstr(aname); + if(file->fs == nil){ + err = Ebadspc; + goto out; + } + file->uid = strtouid(f->uname); + if(file->uid < 0){ + err = Ebadu; + goto out; + } + file->qid.path = 0; + file->qid.vers = 0; + file->qid.type = QTAUTH; + r->qid = file->qid; + +out: + if(file != nil){ + qunlock(file); + if(err != 0) + freefp(file); + } + return err; +} + +int +authread(File *file, uchar *data, int count) +{ + AuthInfo *ai; + AuthRpc *rpc; + int rv; + + rpc = file->rpc; + if(rpc == nil) + return -1; + + rv = auth_rpc(rpc, "read", nil, 0); + switch(rv){ + case ARdone: + ai = auth_getinfo(rpc); + if(ai == nil) + return -1; + if(chat) + print("authread identifies user as %s\n", ai->cuid); + file->cuid = strtouid(ai->cuid); + auth_freeAI(ai); + if(file->cuid == 0) + return -1; + if(chat) + print("%s is a known user\n", ai->cuid); + return 0; + case ARok: + if(count < rpc->narg) + return -1; + memmove(data, rpc->arg, rpc->narg); + return rpc->narg; + case ARphase: + return -1; + default: + return -1; + } +} + +int +authwrite(File *file, uchar *data, int count) +{ + int ret; + + ret = auth_rpc(file->rpc, "write", data, count); + if(ret != ARok) + return -1; + return count; +} + +void +mkqid9p1(Qid9p1* qid9p1, Qid* qid) +{ + if(qid->path & 0xFFFFFFFF00000000LL) + panic("mkqid9p1: path %lluX\n", qid->path); + qid9p1->path = qid->path & 0xFFFFFFFF; + if(qid->type & QTDIR) + qid9p1->path |= QPDIR; + qid9p1->version = qid->vers; +} + +void +authfree(File *fp) +{ + if(fp->rpc != nil){ + close(fp->rpc->afd); + free(fp->rpc); + fp->rpc = nil; + } +} + +void +mkqid9p2(Qid* qid, Qid9p1* qid9p1, int mode) +{ + qid->path = (ulong)(qid9p1->path & ~QPDIR); + qid->vers = qid9p1->version; + qid->type = 0; + if(mode & DDIR) + qid->type |= QTDIR; + if(mode & DAPND) + qid->type |= QTAPPEND; + if(mode & DLOCK) + qid->type |= QTEXCL; +} + +static int +checkattach(Chan *chan, File *afile, File *file, Filsys *fs) +{ + uchar buf[1]; + + if(chan == cons.srvchan || chan == cons.chan) + return 0; + + /* if no afile, this had better be none */ + if(afile == nil){ + if(file->uid == 0){ + if(!allownone && !chan->authed) + return Eauth; + return 0; + } + return Eauth; + } + + /* otherwise, we'ld better have a usable cuid */ + if(!(afile->qid.type&QTAUTH)) + return Eauth; + if(afile->uid != file->uid || afile->fs != fs) + return Eauth; + if(afile->cuid <= 0){ + if(authread(afile, buf, 0) != 0) + return Eauth; + if(afile->cuid <= 0) + return Eauth; + } + file->uid = afile->cuid; + + /* once someone has authenticated on the channel, others can become none */ + chan->authed = 1; + + return 0; +} + +static int +fsattach(Chan* chan, Fcall* f, Fcall* r) +{ + char *aname; + Iobuf *p; + Dentry *d; + File *file; + File *afile; + Filsys *fs; + long raddr; + int error, u; + + aname = f->aname; + if(!aname[0]) /* default */ + aname = "main"; + p = nil; + afile = filep(chan, f->afid, 0); + file = filep(chan, f->fid, 1); + if(file == nil){ + error = Efidinuse; + goto out; + } + + u = -1; + if(chan != cons.chan){ + if(strcmp(f->uname, "adm") == 0){ + error = Eauth; + goto out; + } + u = strtouid(f->uname); + if(u < 0){ + error = Ebadu; + goto out; + } + } + file->uid = u; + + fs = fsstr(aname); + if(fs == nil){ + error = Ebadspc; + goto out; + } + + if(error = checkattach(chan, afile, file, fs)) + goto out; + + raddr = getraddr(fs->dev); + p = getbuf(fs->dev, raddr, Bread); + d = getdir(p, 0); + if(d == nil || checktag(p, Tdir, QPROOT) || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(iaccess(file, d, DEXEC)){ + error = Eaccess; + goto out; + } + if(file->uid == 0 && isro(fs->dev)) { + /* + * 'none' not allowed on dump + */ + error = Eaccess; + goto out; + } + accessdir(p, d, FREAD); + mkqid(&file->qid, d, 1); + file->fs = fs; + file->addr = raddr; + file->slot = 0; + file->open = 0; + freewp(file->wpath); + file->wpath = 0; + + r->qid = file->qid; + +// if(cons.flags & attachflag) +// print("9p2: attach %s %T to \"%s\" C%d\n", +// chan->whoname, chan->whotime, fs->name, chan->chan); + +out: +// if((cons.flags & attachflag) && error) +// print("9p2: attach %s %T SUCK EGGS --- %s\n", +// f->uname, time(), errstr[error]); + if(p != nil) + putbuf(p); + if(afile != nil) + qunlock(afile); + if(file != nil){ + qunlock(file); + if(error) + freefp(file); + } + + return error; +} + +static int +fsflush(Chan* chan, Fcall*, Fcall*) +{ + runlock(&chan->reflock); + wlock(&chan->reflock); + wunlock(&chan->reflock); + rlock(&chan->reflock); + + return 0; +} + +static void +clone(File* nfile, File* file) +{ + Wpath *wpath; + + nfile->qid = file->qid; + + lock(&wpathlock); + nfile->wpath = file->wpath; + for(wpath = nfile->wpath; wpath != nil; wpath = wpath->up) + wpath->refs++; + unlock(&wpathlock); + + nfile->fs = file->fs; + nfile->addr = file->addr; + nfile->slot = file->slot; + nfile->uid = file->uid; + nfile->cuid = 0; + nfile->open = file->open & ~FREMOV; +} + +static int +walkname(File* file, char* wname, Qid* wqid) +{ + Wpath *w; + Iobuf *p, *p1; + Dentry *d, *d1; + int error, slot; + long addr, qpath; + + p = p1 = nil; + + /* + * File must not have been opened for I/O by an open + * or create message and must represent a directory. + */ + if(file->open != 0){ + error = Emode; + goto out; + } + + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Edir1; + goto out; + } + if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(!(d->mode & DDIR)){ + error = Edir1; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + + /* + * For walked elements the implied user must + * have permission to search the directory. + */ + if(file->cp != cons.chan && iaccess(file, d, DEXEC)){ + error = Eaccess; + goto out; + } + accessdir(p, d, FREAD); + + if(strcmp(wname, ".") == 0){ +setdot: + if(wqid != nil) + *wqid = file->qid; + goto out; + } + if(strcmp(wname, "..") == 0){ + if(file->wpath == 0) + goto setdot; + putbuf(p); + p = nil; + addr = file->wpath->addr; + slot = file->wpath->slot; + p1 = getbuf(file->fs->dev, addr, Bread); + if(p1 == nil || checktag(p1, Tdir, QPNONE)){ + error = Edir1; + goto out; + } + if((d1 = getdir(p1, slot)) == nil || !(d1->mode & DALLOC)){ + error = Ephase; + goto out; + } + lock(&wpathlock); + file->wpath->refs--; + file->wpath = file->wpath->up; + unlock(&wpathlock); + goto found; + } + + for(addr = 0; ; addr++){ + if(p == nil){ + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ealloc; + goto out; + } + d = getdir(p, file->slot); + if(d == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + } + qpath = d->qid.path; + p1 = dnodebuf1(p, d, addr, 0); + p = nil; + if(p1 == nil || checktag(p1, Tdir, qpath)){ + error = Eentry; + goto out; + } + for(slot = 0; slot < DIRPERBUF; slot++){ + d1 = getdir(p1, slot); + if(!(d1->mode & DALLOC)) + continue; + if(strncmp(wname, d1->name, NAMELEN) != 0) + continue; + /* + * update walk path + */ + if((w = newwp()) == nil){ + error = Ewalk; + goto out; + } + w->addr = file->addr; + w->slot = file->slot; + w->up = file->wpath; + file->wpath = w; + slot += DIRPERBUF*addr; + goto found; + } + putbuf(p1); + p1 = nil; + } + +found: + file->addr = p1->addr; + mkqid(&file->qid, d1, 1); + putbuf(p1); + p1 = nil; + file->slot = slot; + if(wqid != nil) + *wqid = file->qid; + +out: + if(p1 != nil) + putbuf(p1); + if(p != nil) + putbuf(p); + + return error; +} + +static int +fswalk(Chan* chan, Fcall* f, Fcall* r) +{ + int error, nwname; + File *file, *nfile, tfile; + + /* + * The file identified by f->fid must be valid in the + * current session and must not have been opened for I/O + * by an open or create message. + */ + if((file = filep(chan, f->fid, 0)) == nil) + return Efid; + if(file->open != 0){ + qunlock(file); + return Emode; + } + + /* + * If newfid is not the same as fid, allocate a new file; + * a side effect is checking newfid is not already in use (error); + * if there are no names to walk this will be equivalent to a + * simple 'clone' operation. + * Otherwise, fid and newfid are the same and if there are names + * to walk make a copy of 'file' to be used during the walk as + * 'file' must only be updated on success. + * Finally, it's a no-op if newfid is the same as fid and f->nwname + * is 0. + */ + r->nwqid = 0; + if(f->newfid != f->fid){ + if((nfile = filep(chan, f->newfid, 1)) == nil){ + qunlock(file); + return Efidinuse; + } + } + else if(f->nwname != 0){ + nfile = &tfile; + memset(nfile, 0, sizeof(File)); + nfile->cp = chan; + nfile->fid = ~0; + } + else{ + qunlock(file); + return 0; + } + clone(nfile, file); + + /* + * Should check name is not too long. + */ + error = 0; + for(nwname = 0; nwname < f->nwname; nwname++){ + error = walkname(nfile, f->wname[nwname], &r->wqid[r->nwqid]); + if(error != 0 || ++r->nwqid >= MAXDAT/sizeof(Qid)) + break; + } + + if(f->nwname == 0){ + /* + * Newfid must be different to fid (see above) + * so this is a simple 'clone' operation - there's + * nothing to do except unlock unless there's + * an error. + */ + if(error){ + freewp(nfile->wpath); + qunlock(nfile); + freefp(nfile); + } + else + qunlock(nfile); + } + else if(r->nwqid < f->nwname){ + /* + * Didn't walk all elements, 'clunk' nfile + * and leave 'file' alone. + * Clear error if some of the elements were + * walked OK. + */ + freewp(nfile->wpath); + if(nfile != &tfile){ + qunlock(nfile); + freefp(nfile); + } + if(r->nwqid != 0) + error = 0; + } + else{ + /* + * Walked all elements. If newfid is the same + * as fid must update 'file' from the temporary + * copy used during the walk. + * Otherwise just unlock (when using tfile there's + * no need to unlock as it's a local). + */ + if(nfile == &tfile){ + file->qid = nfile->qid; + freewp(file->wpath); + file->wpath = nfile->wpath; + file->addr = nfile->addr; + file->slot = nfile->slot; + } + else + qunlock(nfile); + } + qunlock(file); + + return error; +} + +static int +fsopen(Chan* chan, Fcall* f, Fcall* r) +{ + Iobuf *p; + Dentry *d; + File *file; + Tlock *t; + Qid qid; + int error, ro, fmod, wok; + + wok = 0; + p = nil; + + if(chan == cons.chan || writeallow) + wok = 1; + + if((file = filep(chan, f->fid, 0)) == nil){ + error = Efid; + goto out; + } + + /* + * if remove on close, check access here + */ + ro = isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup)); + if(f->mode & ORCLOSE){ + if(ro){ + error = Eronly; + goto out; + } + /* + * check on parent directory of file to be deleted + */ + if(file->wpath == 0 || file->wpath->addr == file->addr){ + error = Ephase; + goto out; + } + p = getbuf(file->fs->dev, file->wpath->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ephase; + goto out; + } + if((d = getdir(p, file->wpath->slot)) == nil || !(d->mode & DALLOC)){ + error = Ephase; + goto out; + } + if(iaccess(file, d, DWRITE)){ + error = Eaccess; + goto out; + } + putbuf(p); + } + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ealloc; + goto out; + } + if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + mkqid(&qid, d, 1); + switch(f->mode & 7){ + + case OREAD: + if(iaccess(file, d, DREAD) && !wok) + goto badaccess; + fmod = FREAD; + break; + + case OWRITE: + if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok)) + goto badaccess; + if(ro){ + error = Eronly; + goto out; + } + fmod = FWRITE; + break; + + case ORDWR: + if((d->mode & DDIR) + || (iaccess(file, d, DREAD) && !wok) + || (iaccess(file, d, DWRITE) && !wok)) + goto badaccess; + if(ro){ + error = Eronly; + goto out; + } + fmod = FREAD+FWRITE; + break; + + case OEXEC: + if((d->mode & DDIR) || (iaccess(file, d, DEXEC) && !wok)) + goto badaccess; + fmod = FREAD; + break; + + default: + error = Emode; + goto out; + } + if(f->mode & OTRUNC){ + if((d->mode & DDIR) || (iaccess(file, d, DWRITE) && !wok)) + goto badaccess; + if(ro){ + error = Eronly; + goto out; + } + } + t = 0; + if(d->mode & DLOCK){ + if((t = tlocked(p, d)) == nil){ + error = Elocked; + goto out; + } + } + if(f->mode & ORCLOSE) + fmod |= FREMOV; + file->open = fmod; + if((f->mode & OTRUNC) && !(d->mode & DAPND)){ + dtrunc(p, d); + qid.vers = d->qid.version; + } + r->qid = qid; + file->tlock = t; + if(t != nil) + t->file = file; + file->lastra = 1; + goto out; + +badaccess: + error = Eaccess; + file->open = 0; + +out: + if(p != nil) + putbuf(p); + if(file != nil) + qunlock(file); + + r->iounit = chan->msize-IOHDRSZ; + + return error; +} + +static int +dir9p2(Dir* dir, Dentry* dentry, void* strs) +{ + char *op, *p; + + memset(dir, 0, sizeof(Dir)); + mkqid(&dir->qid, dentry, 1); + dir->mode = (dir->qid.type<<24)|(dentry->mode & 0777); + dir->atime = dentry->atime; + dir->mtime = dentry->mtime; + dir->length = dentry->size; + + op = p = strs; + dir->name = p; + p += sprint(p, "%s", dentry->name)+1; + + dir->uid = p; + uidtostr(p, dentry->uid); + p += strlen(p)+1; + + dir->gid = p; + uidtostr(p, dentry->gid); + p += strlen(p)+1; + + dir->muid = p; + strcpy(p, ""); + p += strlen(p)+1; + + return p-op; +} + +static int +checkname9p2(char* name) +{ + char *p; + + /* + * Return length of string if valid, 0 if not. + */ + if(name == nil) + return 0; + + for(p = name; *p != 0; p++){ + if((*p & 0xFF) <= 040) + return 0; + } + + return p-name; +} + +static int +fscreate(Chan* chan, Fcall* f, Fcall* r) +{ + Iobuf *p, *p1; + Dentry *d, *d1; + File *file; + int error, slot, slot1, fmod, wok, l; + long addr, addr1, path; + Tlock *t; + Wpath *w; + + wok = 0; + p = nil; + + if(chan == cons.chan || writeallow) + wok = 1; + + if((file = filep(chan, f->fid, 0)) == nil){ + error = Efid; + goto out; + } + if(isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup))){ + error = Eronly; + goto out; + } + + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ealloc; + goto out; + } + if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + if(!(d->mode & DDIR)){ + error = Edir2; + goto out; + } + if(iaccess(file, d, DWRITE) && !wok) { + error = Eaccess; + goto out; + } + accessdir(p, d, FREAD); + + /* + * Check the name is valid and will fit in an old + * directory entry. + */ + if((l = checkname9p2(f->name)) == 0){ + error = Ename; + goto out; + } + if(l+1 > NAMELEN){ + error = Etoolong; + goto out; + } + if(strcmp(f->name, ".") == 0 || strcmp(f->name, "..") == 0){ + error = Edot; + goto out; + } + + addr1 = 0; + slot1 = 0; /* set */ + for(addr = 0; ; addr++){ + if((p1 = dnodebuf(p, d, addr, 0)) == nil){ + if(addr1 != 0) + break; + p1 = dnodebuf(p, d, addr, Tdir); + } + if(p1 == nil){ + error = Efull; + goto out; + } + if(checktag(p1, Tdir, d->qid.path)){ + putbuf(p1); + goto phase; + } + for(slot = 0; slot < DIRPERBUF; slot++){ + d1 = getdir(p1, slot); + if(!(d1->mode & DALLOC)){ + if(addr1 == 0){ + addr1 = p1->addr; + slot1 = slot + addr*DIRPERBUF; + } + continue; + } + if(strncmp(f->name, d1->name, sizeof(d1->name)) == 0){ + putbuf(p1); + error = Eexist; + goto out; + } + } + putbuf(p1); + } + + switch(f->mode & 7){ + case OEXEC: + case OREAD: /* seems only useful to make directories */ + fmod = FREAD; + break; + + case OWRITE: + fmod = FWRITE; + break; + + case ORDWR: + fmod = FREAD+FWRITE; + break; + + default: + error = Emode; + goto out; + } + if(f->perm & PDIR) + if((f->mode & OTRUNC) || (f->perm & PAPND) || (fmod & FWRITE)) + goto badaccess; + /* + * do it + */ + path = qidpathgen(&file->fs->dev); + if((p1 = getbuf(file->fs->dev, addr1, Bread|Bimm|Bmod)) == nil) + goto phase; + d1 = getdir(p1, slot1); + if(d1 == nil || checktag(p1, Tdir, d->qid.path)) { + putbuf(p1); + goto phase; + } + if(d1->mode & DALLOC){ + putbuf(p1); + goto phase; + } + + strncpy(d1->name, f->name, sizeof(d1->name)); + if(chan == cons.chan){ + d1->uid = cons.uid; + d1->gid = cons.gid; + } + else{ + d1->uid = file->uid; + d1->gid = d->gid; + f->perm &= d->mode | ~0666; + if(f->perm & PDIR) + f->perm &= d->mode | ~0777; + } + d1->qid.path = path; + d1->qid.version = 0; + d1->mode = DALLOC | (f->perm & 0777); + if(f->perm & PDIR) { + d1->mode |= DDIR; + d1->qid.path |= QPDIR; + } + if(f->perm & PAPND) + d1->mode |= DAPND; + t = nil; + if(f->perm & PLOCK){ + d1->mode |= DLOCK; + t = tlocked(p1, d1); + /* if nil, out of tlock structures */ + } + accessdir(p1, d1, FWRITE); + mkqid(&r->qid, d1, 0); + putbuf(p1); + accessdir(p, d, FWRITE); + + /* + * do a walk to new directory entry + */ + if((w = newwp()) == nil){ + error = Ewalk; + goto out; + } + w->addr = file->addr; + w->slot = file->slot; + w->up = file->wpath; + file->wpath = w; + file->qid = r->qid; + file->tlock = t; + if(t != nil) + t->file = file; + file->lastra = 1; + if(f->mode & ORCLOSE) + fmod |= FREMOV; + file->open = fmod; + file->addr = addr1; + file->slot = slot1; + goto out; + +badaccess: + error = Eaccess; + goto out; + +phase: + error = Ephase; + +out: + if(p != nil) + putbuf(p); + if(file != nil) + qunlock(file); + + r->iounit = chan->msize-IOHDRSZ; + + return error; +} + +static int +fsread(Chan* chan, Fcall* f, Fcall* r) +{ + uchar *data; + Iobuf *p, *p1; + File *file; + Dentry *d, *d1; + Tlock *t; + long addr, offset, start, tim; + int error, iounit, nread, count, n, o, slot; + Dir dir; + char strdata[28*10]; + + p = nil; + data = (uchar*)r->data; + count = f->count; + offset = f->offset; + nread = 0; + if((file = filep(chan, f->fid, 0)) == nil){ + error = Efid; + goto out; + } + if(file->qid.type & QTAUTH){ + nread = authread(file, data, count); + if(nread < 0) + error = Esystem; + else + error = 0; + goto out; + } + if(!(file->open & FREAD)){ + error = Eopen; + goto out; + } + iounit = chan->msize-IOHDRSZ; + if(count < 0 || count > iounit){ + error = Ecount; + goto out; + } + if(offset < 0){ + error = Eoffset; + goto out; + } + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ealloc; + goto out; + } + if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + if(t = file->tlock){ + tim = time(0); + if(t->time < tim || t->file != file){ + error = Ebroken; + goto out; + } + /* renew the lock */ + t->time = tim + TLOCK; + } + accessdir(p, d, FREAD); + if(d->mode & DDIR) + goto dread; + if(offset+count > d->size) + count = d->size - offset; + while(count > 0){ + if(p == nil){ + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ealloc; + goto out; + } + if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + } + addr = offset / BUFSIZE; + o = offset % BUFSIZE; + n = BUFSIZE - o; + if(n > count) + n = count; + p1 = dnodebuf1(p, d, addr, 0); + p = nil; + if(p1 != nil){ + if(checktag(p1, Tfile, QPNONE)){ + error = Ephase; + putbuf(p1); + goto out; + } + memmove(data+nread, p1->iobuf+o, n); + putbuf(p1); + } + else + memset(data+nread, 0, n); + count -= n; + nread += n; + offset += n; + } + goto out; + +dread: + /* + * Pick up where we left off last time if nothing has changed, + * otherwise must scan from the beginning. + */ + if(offset == file->doffset /*&& file->qid.vers == file->dvers*/){ + addr = file->dslot/DIRPERBUF; + slot = file->dslot%DIRPERBUF; + start = offset; + } + else{ + addr = 0; + slot = 0; + start = 0; + } + +dread1: + if(p == nil){ + /* + * This is just a check to ensure the entry hasn't + * gone away during the read of each directory block. + */ + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Ealloc; + goto out1; + } + if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out1; + } + } + p1 = dnodebuf1(p, d, addr, 0); + p = nil; + if(p1 == nil) + goto out1; + if(checktag(p1, Tdir, QPNONE)){ + error = Ephase; + putbuf(p1); + goto out1; + } + + for(; slot < DIRPERBUF; slot++){ + d1 = getdir(p1, slot); + if(!(d1->mode & DALLOC)) + continue; + dir9p2(&dir, d1, strdata); + if((n = convD2M(&dir, data+nread, iounit - nread)) <= BIT16SZ){ + putbuf(p1); + goto out1; + } + start += n; + if(start < offset) + continue; + if(count < n){ + putbuf(p1); + goto out1; + } + count -= n; + nread += n; + offset += n; + } + putbuf(p1); + slot = 0; + addr++; + goto dread1; + +out1: + if(error == 0){ + file->doffset = offset; + file->dvers = file->qid.vers; + file->dslot = slot+DIRPERBUF*addr; + } + +out: + /* + * Do we need this any more? + count = f->count - nread; + if(count > 0) + memset(data+nread, 0, count); + */ + if(p != nil) + putbuf(p); + if(file != nil) + qunlock(file); + r->count = nread; + r->data = (char*)data; + + return error; +} + +static int +fswrite(Chan* chan, Fcall* f, Fcall* r) +{ + Iobuf *p, *p1; + Dentry *d; + File *file; + Tlock *t; + long offset, addr, tim, qpath; + int count, error, nwrite, o, n; + + offset = f->offset; + count = f->count; + + nwrite = 0; + p = nil; + + if((file = filep(chan, f->fid, 0)) == nil){ + error = Efid; + goto out; + } + if(file->qid.type & QTAUTH){ + nwrite = authwrite(file, (uchar*)f->data, count); + if(nwrite < 0) + error = Esystem; + else + error = 0; + goto out; + } + if(!(file->open & FWRITE)){ + error = Eopen; + goto out; + } + if(isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup))){ + error = Eronly; + goto out; + } + if(count < 0 || count > chan->msize-IOHDRSZ){ + error = Ecount; + goto out; + } + if(offset < 0) { + error = Eoffset; + goto out; + } + if((p = getbuf(file->fs->dev, file->addr, Bread|Bmod)) == nil){ + error = Ealloc; + goto out; + } + if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + if(t = file->tlock) { + tim = time(0); + if(t->time < tim || t->file != file){ + error = Ebroken; + goto out; + } + /* renew the lock */ + t->time = tim + TLOCK; + } + accessdir(p, d, FWRITE); + if(d->mode & DAPND) + offset = d->size; + if(offset+count > d->size) + d->size = offset+count; + while(count > 0){ + if(p == nil){ + p = getbuf(file->fs->dev, file->addr, Bread|Bmod); + if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + } + addr = offset / BUFSIZE; + o = offset % BUFSIZE; + n = BUFSIZE - o; + if(n > count) + n = count; + qpath = d->qid.path; + p1 = dnodebuf1(p, d, addr, Tfile); + p = nil; + if(p1 == nil) { + error = Efull; + goto out; + } + if(checktag(p1, Tfile, qpath)){ + putbuf(p1); + error = Ephase; + goto out; + } + memmove(p1->iobuf+o, f->data+nwrite, n); + p1->flags |= Bmod; + putbuf(p1); + count -= n; + nwrite += n; + offset += n; + } + +out: + if(p != nil) + putbuf(p); + if(file != nil) + qunlock(file); + r->count = nwrite; + + return error; +} + +static int +_clunk(File* file, int remove, int wok) +{ + Tlock *t; + int error; + + error = 0; + if(t = file->tlock){ + if(t->file == file) + t->time = 0; /* free the lock */ + file->tlock = 0; + } + if(remove) + error = doremove(file, wok); + file->open = 0; + freewp(file->wpath); + freefp(file); + qunlock(file); + + return error; +} + +static int +fsclunk(Chan* chan, Fcall* f, Fcall*) +{ + File *file; + + if((file = filep(chan, f->fid, 0)) == nil) + return Efid; + + _clunk(file, file->open & FREMOV, 0); + return 0; +} + +static int +fsremove(Chan* chan, Fcall* f, Fcall*) +{ + File *file; + + if((file = filep(chan, f->fid, 0)) == nil) + return Efid; + + return _clunk(file, 1, chan == cons.chan); +} + +static int +fsstat(Chan* chan, Fcall* f, Fcall* r, uchar* data) +{ + Dir dir; + Iobuf *p; + Dentry *d; + File *file; + int error, len; + + if((file = filep(chan, f->fid, 0)) == nil) + return Efid; + + p = getbuf(file->fs->dev, file->addr, Bread); + if(p == nil || checktag(p, Tdir, QPNONE)){ + error = Edir1; + goto out; + } + if((d = getdir(p, file->slot)) == nil || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + + if(d->qid.path == QPROOT) /* stat of root gives time */ + d->atime = time(0); + + len = dir9p2(&dir, d, data); + data += len; + if((r->nstat = convD2M(&dir, data, chan->msize - len)) == 0) + error = Ersc; + else + r->stat = data; + +out: + if(p != nil) + putbuf(p); + if(file != nil) + qunlock(file); + + return error; +} + +static int +fswstat(Chan* chan, Fcall* f, Fcall*, char *strs) +{ + Iobuf *p, *p1; + Dentry *d, *d1, xd; + File *file; + int error, slot, uid, gid, l; + long addr; + Dir dir; + ulong mode; + + p = p1 = nil; + d1 = nil; + + if((file = filep(chan, f->fid, 0)) == nil){ + error = Efid; + goto out; + } + + /* + * if user none, + * can't do anything + * unless allow. + */ + if(file->uid == 0 && !wstatallow){ + error = Eaccess; + goto out; + } + + if(isro(file->fs->dev) || (writegroup && !ingroup(file->uid, writegroup))){ + error = Eronly; + goto out; + } + + /* + * first get parent + */ + if(file->wpath){ + p1 = getbuf(file->fs->dev, file->wpath->addr, Bread); + if(p1 == nil){ + error = Ephase; + goto out; + } + d1 = getdir(p1, file->wpath->slot); + if(d1 == nil || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)){ + error = Ephase; + goto out; + } + } + + if((p = getbuf(file->fs->dev, file->addr, Bread)) == nil){ + error = Ealloc; + goto out; + } + d = getdir(p, file->slot); + if(d == nil || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)){ + error = Ealloc; + goto out; + } + if(error = mkqidcmp(&file->qid, d)) + goto out; + + /* + * Convert the message and fix up + * fields not to be changed. + */ + if(convM2D(f->stat, f->nstat, &dir, strs) == 0){ + print("9p2: convM2D returns 0\n"); + error = Econvert; + goto out; + } + if(dir.uid == nil || strlen(dir.uid) == 0) + uid = d->uid; + else + uid = strtouid(dir.uid); + if(dir.gid == nil || strlen(dir.gid) == 0) + gid = d->gid; + else + gid = strtouid(dir.gid); + if(dir.name == nil || strlen(dir.name) == 0) + dir.name = d->name; + else{ + if((l = checkname9p2(dir.name)) == 0){ + error = Ename; + goto out; + } + if(l > NAMELEN){ + error = Etoolong; + goto out; + } + } + + /* + * Before doing sanity checks, find out what the + * new 'mode' should be: + * if 'type' and 'mode' are both defaults, take the + * new mode from the old directory entry; + * else if 'type' is the default, use the new mode entry; + * else if 'mode' is the default, create the new mode from + * 'type' or'ed with the old directory mode; + * else neither are defaults, use the new mode but check + * it agrees with 'type'. + */ + if(dir.qid.type == 0xFF && dir.mode == ~0){ + dir.mode = d->mode & 0777; + if(d->mode & DLOCK) + dir.mode |= DMEXCL; + if(d->mode & DAPND) + dir.mode |= DMAPPEND; + if(d->mode & DDIR) + dir.mode |= DMDIR; + } + else if(dir.qid.type == 0xFF){ + /* nothing to do */ + } + else if(dir.mode == ~0) + dir.mode = (dir.qid.type<<24)|(d->mode & 0777); + else if(dir.qid.type != ((dir.mode>>24) & 0xFF)){ + error = Eqidmode; + goto out; + } + + /* + * Check for unknown type/mode bits + * and an attempt to change the directory bit. + */ + if(dir.mode & ~(DMDIR|DMAPPEND|DMEXCL|0777)){ + error = Enotm; + goto out; + } + if(d->mode & DDIR) + mode = DMDIR; + else + mode = 0; + if((dir.mode^mode) & DMDIR){ + error = Enotd; + goto out; + } + + if(dir.mtime == ~0) + dir.mtime = d->mtime; + if(dir.length == ~0) + dir.length = d->size; + + /* + * Currently, can't change length. + */ + if(dir.length != d->size){ + error = Enotl; + goto out; + } + + /* + * if chown, + * must be god + * wstatallow set to allow chown during boot + */ + if(uid != d->uid && !wstatallow) { + error = Enotu; + goto out; + } + + /* + * if chgroup, + * must be either + * a) owner and in new group + * b) leader of both groups + * wstatallow and writeallow are set to allow chgrp during boot + */ + while(gid != d->gid) { + if(wstatallow || writeallow) + break; + if(d->uid == file->uid && ingroup(file->uid, gid)) + break; + if(leadgroup(file->uid, gid)) + if(leadgroup(file->uid, d->gid)) + break; + error = Enotg; + goto out; + } + + /* + * if rename, + * must have write permission in parent + */ + while(strncmp(d->name, dir.name, sizeof(d->name)) != 0) { + if(checkname(dir.name) || d1 == nil) { + error = Ename; + goto out; + } + if(strcmp(dir.name, ".") == 0 || strcmp(xd.name, "..") == 0) { + error = Ename; + goto out; + } + + /* + * drop entry to prevent lock, then + * check that destination name is unique, + */ + putbuf(p); + for(addr = 0; ; addr++) { + if((p = dnodebuf(p1, d1, addr, 0)) == nil) + break; + if(checktag(p, Tdir, d1->qid.path)) { + putbuf(p); + continue; + } + for(slot = 0; slot < DIRPERBUF; slot++) { + d = getdir(p, slot); + if(!(d->mode & DALLOC)) + continue; + if(strncmp(dir.name, d->name, sizeof(d->name)) == 0) { + error = Eexist; + goto out; + } + } + putbuf(p); + } + + /* + * reacquire entry + */ + if((p = getbuf(file->fs->dev, file->addr, Bread)) == nil){ + error = Ephase; + goto out; + } + d = getdir(p, file->slot); + if(d == nil || checktag(p, Tdir, QPNONE) || !(d->mode & DALLOC)) { + error = Ephase; + goto out; + } + + if(wstatallow || writeallow) /* set to allow rename during boot */ + break; + if(d1 == nil || iaccess(file, d1, DWRITE)) { + error = Eaccess; + goto out; + } + break; + } + + /* + * if mode/time, either + * a) owner + * b) leader of either group + */ + mode = dir.mode & 0777; + if(dir.mode & DMAPPEND) + mode |= DAPND; + if(dir.mode & DMEXCL) + mode |= DLOCK; + while(d->mtime != dir.mtime || ((d->mode^mode) & (DAPND|DLOCK|0777))) { + if(wstatallow) /* set to allow chmod during boot */ + break; + if(d->uid == file->uid) + break; + if(leadgroup(file->uid, gid)) + break; + if(leadgroup(file->uid, d->gid)) + break; + error = Enotu; + goto out; + } + d->mtime = dir.mtime; + d->uid = uid; + d->gid = gid; + d->mode = (mode & (DAPND|DLOCK|0777)) | (d->mode & (DALLOC|DDIR)); + + strncpy(d->name, dir.name, sizeof(d->name)); + accessdir(p, d, FWSTAT); + +out: + if(p != nil) + putbuf(p); + if(p1 != nil) + putbuf(p1); + if(file != nil) + qunlock(file); + + return error; +} + +static int +recv(Chan *c, uchar *buf, int n) +{ + int fd, m, len; + + fd = c->chan; + /* read count */ + qlock(&c->rlock); + m = readn(fd, buf, BIT32SZ); + if(m != BIT32SZ){ + qunlock(&c->rlock); + if(m < 0){ + print("readn(BIT32SZ) fails: %r\n"); + return -1; + } + print("readn(BIT32SZ) returns %d: %r\n", m); + return 0; + } + + len = GBIT32(buf); + if(len <= BIT32SZ || len > n){ + print("recv bad length %d\n", len); + werrstr("bad length in 9P2000 message header"); + qunlock(&c->rlock); + return -1; + } + len -= BIT32SZ; + m = readn(fd, buf+BIT32SZ, len); + qunlock(&c->rlock); + if(m < len){ + print("recv wanted %d got %d\n", len, m); + return 0; + } + return BIT32SZ+m; +} + +static void +send(Chan *c, uchar *buf, int n) +{ + int fd, m; + + fd = c->chan; + qlock(&c->wlock); + m = write(fd, buf, n); + qunlock(&c->wlock); + if(m == n) + return; + panic("write failed"); +} + +void +serve9p2(Chan *chan, uchar *ib, int nib) +{ + uchar inbuf[MSIZE+IOHDRSZ], outbuf[MSIZE+IOHDRSZ]; + Fcall f, r; + char ename[64]; + int error, n, type; + + chan->msize = MSIZE; + fmtinstall('F', fcallfmt); + + for(;;){ + if(nib){ + memmove(inbuf, ib, nib); + n = nib; + nib = 0; + }else + n = recv(chan, inbuf, sizeof inbuf); + if(chat){ + print("read msg %d (fd %d)\n", n, chan->chan); + if(n <= 0) + print("\terr: %r\n"); + } + if(n == 0 && (chan == cons.srvchan || chan == cons.chan)) + continue; + if(n <= 0) + break; + if(convM2S(inbuf, n, &f) != n){ + print("9p2: cannot decode\n"); + continue; + } + + type = f.type; + if(type < Tversion || type >= Tmax || (type&1) || type == Terror){ + print("9p2: bad message type %d\n", type); + continue; + } + + if(CHAT(chan)) + print("9p2: f %F\n", &f); + + r.type = type+1; + r.tag = f.tag; + error = 0; + + rlock(&mainlock); + rlock(&chan->reflock); + switch(type){ + default: + r.type = Rerror; + snprint(ename, sizeof ename, "unknown message: %F", &f); + r.ename = ename; + break; + case Tversion: + error = fsversion(chan, &f, &r); + break; + case Tauth: + error = fsauth(chan, &f, &r); + break; + case Tattach: + error = fsattach(chan, &f, &r); + break; + case Tflush: + error = fsflush(chan, &f, &r); + break; + case Twalk: + error = fswalk(chan, &f, &r); + break; + case Topen: + error = fsopen(chan, &f, &r); + break; + case Tcreate: + error = fscreate(chan, &f, &r); + break; + case Tread: + r.data = (char*)inbuf; + error = fsread(chan, &f, &r); + break; + case Twrite: + error = fswrite(chan, &f, &r); + break; + case Tclunk: + error = fsclunk(chan, &f, &r); + break; + case Tremove: + error = fsremove(chan, &f, &r); + break; + case Tstat: + error = fsstat(chan, &f, &r, inbuf); + break; + case Twstat: + error = fswstat(chan, &f, &r, (char*)outbuf); + break; + } + runlock(&chan->reflock); + runlock(&mainlock); + + if(error != 0){ + r.type = Rerror; + if(error >= MAXERR){ + snprint(ename, sizeof(ename), "error %d", error); + r.ename = ename; + } + else + r.ename = errstring[error]; + } + if(CHAT(chan)) + print("9p2: r %F\n", &r); + + n = convS2M(&r, outbuf, sizeof outbuf); + if(n == 0){ + type = r.type; + r.type = Rerror; + snprint(ename, sizeof(ename), "9p2: convS2M: type %d", type); + r.ename = ename; + print(ename); + n = convS2M(&r, outbuf, sizeof outbuf); + if(n == 0){ + /* + * What to do here, the failure notification failed? + */ + panic("can't write anything at all"); + } + } + send(chan, outbuf, n); + } + fileinit(chan); + close(chan->chan); + if(chan == cons.srvchan || chan == cons.chan) + print("console chan read error"); +} + diff --git a/sys/src/cmd/disk/kfs/all.h b/sys/src/cmd/disk/kfs/all.h new file mode 100755 index 000000000..3f1e165c5 --- /dev/null +++ b/sys/src/cmd/disk/kfs/all.h @@ -0,0 +1,8 @@ +#include "u.h" +#include "libc.h" +#include "dat.h" +#include "fns.h" + +#include <fcall.h> +#include <auth.h> +#include <authsrv.h> diff --git a/sys/src/cmd/disk/kfs/auth.c b/sys/src/cmd/disk/kfs/auth.c new file mode 100755 index 000000000..4d02c035f --- /dev/null +++ b/sys/src/cmd/disk/kfs/auth.c @@ -0,0 +1,117 @@ +/* + * Network listening authentication. + * This and all the other network-related + * code is due to Richard Miller. + */ +#include "all.h" +#include "9p1.h" + +int allownone; +Nvrsafe nvr; +int didread; + +/* + * create a challenge for a fid space + */ +void +mkchallenge(Chan *cp) +{ + int i; + + if(!didread && readnvram(&nvr, 0) >= 0) + didread = 1; + + srand(truerand()); + for(i = 0; i < CHALLEN; i++) + cp->chal[i] = nrand(256); + + cp->idoffset = 0; + cp->idvec = 0; +} + +/* + * match a challenge from an attach + */ +Nvrsafe nvr; + +int +authorize(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + Ticket t; + Authenticator a; + int x; + ulong bit; + + if (cp == cons.srvchan) /* local channel already safe */ + return 1; + + if(wstatallow) /* set to allow entry during boot */ + return 1; + + if(strcmp(in->uname, "none") == 0) + return allownone || cp->authed; + + if(in->type == Toattach9p1) + return 0; + + if(!didread) + return 0; + + /* decrypt and unpack ticket */ + convM2T(in->ticket, &t, nvr.machkey); + if(t.num != AuthTs){ +print("bad AuthTs num\n"); + return 0; + } + + /* decrypt and unpack authenticator */ + convM2A(in->auth, &a, t.key); + if(a.num != AuthAc){ +print("bad AuthAc num\n"); + return 0; + } + + /* challenges must match */ + if(memcmp(a.chal, cp->chal, sizeof(a.chal)) != 0){ +print("bad challenge\n"); + return 0; + } + + /* + * the id must be in a valid range. the range is specified by a + * lower bount (idoffset) and a bit vector (idvec) where a + * bit set to 1 means unusable + */ + lock(&cp->idlock); + x = a.id - cp->idoffset; + bit = 1<<x; + if(x < 0 || x > 31 || (bit&cp->idvec)){ + unlock(&cp->idlock); + return 0; + } + cp->idvec |= bit; + + /* normalize the vector */ + while(cp->idvec&0xffff0001){ + cp->idvec >>= 1; + cp->idoffset++; + } + unlock(&cp->idlock); + + /* ticket name and attach name must match */ + if(memcmp(in->uname, t.cuid, sizeof(in->uname)) != 0){ +print("names don't match\n"); + return 0; + } + + /* copy translated name into input record */ + memmove(in->uname, t.suid, sizeof(in->uname)); + + /* craft a reply */ + a.num = AuthAs; + memmove(a.chal, cp->rchal, CHALLEN); + convA2M(&a, ou->rauth, t.key); + + cp->authed = 1; + return 1; +} diff --git a/sys/src/cmd/disk/kfs/chk.c b/sys/src/cmd/disk/kfs/chk.c new file mode 100755 index 000000000..bea1cbee1 --- /dev/null +++ b/sys/src/cmd/disk/kfs/chk.c @@ -0,0 +1,654 @@ +#include "all.h" + +#define DSIZE 546000 +#define MAXDEPTH 100 + +static char* abits; +static long sizabits; +static char* qbits; +static long sizqbits; +static char* name; +static long sizname; +static long fstart; +static long fsize; +static long nfiles; +static long maxq; +static char* fence; +static char* fencebase; +static Device dev; +static long ndup; +static long nused; +static long nfdup; +static long nqbad; +static long nfree; +static long nbad; +static int mod; +static int flags; +static int ronly; +static int cwflag; +static long sbaddr; +static long oldblock; +static int depth; +static int maxdepth; + +/* local prototypes */ +static int fsck(Dentry*); +static void ckfreelist(Superb*); +static void mkfreelist(Superb*); +static Dentry* maked(long, int, long); +static void modd(long, int, Dentry*); +static void xread(long, long); +static int amark(long); +static int fmark(long); +static void missing(void); +static void qmark(long); +static void* zalloc(ulong); +static void* dalloc(ulong); +static Iobuf* xtag(long, int, long); + +static +void* +zalloc(ulong n) +{ + char *p; + + p = malloc(n); + if(p == 0) + panic("zalloc: out of memory\n"); + memset(p, '\0', n); + return p; +} + +static +void* +dalloc(ulong n) +{ + char *p; + + if(fencebase == 0) + fence = fencebase = zalloc(MAXDEPTH*sizeof(Dentry)); + p = fence; + fence += n; + if(fence > fencebase+MAXDEPTH*sizeof(Dentry)) + panic("dalloc too much memory\n"); + return p; +} + +void +check(Filsys *fs, long flag) +{ + Iobuf *p; + Superb *sb; + Dentry *d; + long raddr; + long nqid; + + wlock(&mainlock); + dev = fs->dev; + flags = flag; + fence = fencebase; + + sizname = 4000; + name = zalloc(sizname); + sizname -= NAMELEN+10; /* for safety */ + + sbaddr = superaddr(dev); + raddr = getraddr(dev); + p = xtag(sbaddr, Tsuper, QPSUPER); + if(!p){ + cprint("bad superblock\n"); + goto out; + } + sb = (Superb*)p->iobuf; + fstart = 1; + + fsize = sb->fsize; + sizabits = (fsize-fstart + 7)/8; + abits = zalloc(sizabits); + + nqid = sb->qidgen+100; /* not as much of a botch */ + if(nqid > 1024*1024*8) + nqid = 1024*1024*8; + if(nqid < 64*1024) + nqid = 64*1024; + + sizqbits = (nqid+7)/8; + qbits = zalloc(sizqbits); + + mod = 0; + nfree = 0; + nfdup = 0; + nused = 0; + nbad = 0; + ndup = 0; + nqbad = 0; + depth = 0; + maxdepth = 0; + + if(flags & Ctouch) { + oldblock = fsize/DSIZE; + oldblock *= DSIZE; + if(oldblock < 0) + oldblock = 0; + cprint("oldblock = %ld\n", oldblock); + } + if(amark(sbaddr)) + {} + if(cwflag) { + if(amark(sb->roraddr)) + {} + if(amark(sb->next)) + {} + } + + if(!(flags & Cquiet)) + cprint("checking file system: %s\n", fs->name); + nfiles = 0; + maxq = 0; + + d = maked(raddr, 0, QPROOT); + if(d) { + if(amark(raddr)) + {} + if(fsck(d)) + modd(raddr, 0, d); + depth--; + fence -= sizeof(Dentry); + if(depth) + cprint("depth not zero on return\n"); + } + + if(flags & Cfree) { + mkfreelist(sb); + sb->qidgen = maxq; + settag(p, Tsuper, QPNONE); + } + + if(sb->qidgen < maxq) + cprint("qid generator low path=%ld maxq=%ld\n", + sb->qidgen, maxq); + if(!(flags & Cfree)) + ckfreelist(sb); + if(mod) { + cprint("file system was modified\n"); + settag(p, Tsuper, QPNONE); + } + + if(!(flags & Cquiet)){ + cprint("%8ld files\n", nfiles); + cprint("%8ld blocks in the file system\n", fsize-fstart); + cprint("%8ld used blocks\n", nused); + cprint("%8ld free blocks\n", sb->tfree); + } + if(!(flags & Cfree)){ + if(nfree != sb->tfree) + cprint("%8ld free blocks found\n", nfree); + if(nfdup) + cprint("%8ld blocks duplicated in the free list\n", nfdup); + if(fsize-fstart-nused-nfree) + cprint("%8ld missing blocks\n", fsize-fstart-nused-nfree); + } + if(ndup) + cprint("%8ld address duplications\n", ndup); + if(nbad) + cprint("%8ld bad block addresses\n", nbad); + if(nqbad) + cprint("%8ld bad qids\n", nqbad); + if(!(flags & Cquiet)) + cprint("%8ld maximum qid path\n", maxq); + missing(); + +out: + if(p) + putbuf(p); + free(abits); + free(name); + free(qbits); + wunlock(&mainlock); +} + +static +int +touch(long a) +{ + Iobuf *p; + + if((flags&Ctouch) && a && a < oldblock){ + p = getbuf(dev, a, Bread|Bmod); + if(p) + putbuf(p); + return 1; + } + return 0; +} + +static +int +checkdir(long a, long qpath) +{ + Dentry *nd; + int i, ns, dmod; + + ns = strlen(name); + dmod = touch(a); + for(i=0; i<DIRPERBUF; i++) { + nd = maked(a, i, qpath); + if(!nd) + break; + if(fsck(nd)) { + modd(a, i, nd); + dmod++; + } + depth--; + fence -= sizeof(Dentry); + name[ns] = 0; + } + name[ns] = 0; + return dmod; +} + +static +int +checkindir(long a, Dentry *d, long qpath) +{ + Iobuf *p; + int i, dmod; + + dmod = touch(a); + p = xtag(a, Tind1, qpath); + if(!p) + return dmod; + for(i=0; i<INDPERBUF; i++) { + a = ((long*)p->iobuf)[i]; + if(!a) + continue; + if(amark(a)) { + if(flags & Cbad) { + ((long*)p->iobuf)[i] = 0; + p->flags |= Bmod; + } + continue; + } + if(d->mode & DDIR) + dmod += checkdir(a, qpath); + else if(flags & Crdall) + xread(a, qpath); + } + putbuf(p); + return dmod; +} + +static +int +fsck(Dentry *d) +{ + char *s; + Rune r; + Iobuf *p; + int l, i, ns, dmod; + long a, qpath; + + depth++; + if(depth >= maxdepth){ + maxdepth = depth; + if(maxdepth >= MAXDEPTH){ + cprint("max depth exceeded: %s\n", name); + return 0; + } + } + dmod = 0; + if(!(d->mode & DALLOC)) + return 0; + nfiles++; + + ns = strlen(name); + i = strlen(d->name); + if(i >= NAMELEN){ + d->name[NAMELEN-1] = 0; + cprint("%s->name (%s) not terminated\n", name, d->name); + return 0; + } + ns += i; + if(ns >= sizname){ + cprint("%s->name (%s) name too large\n", name, d->name); + return 0; + } + for (s = d->name; *s; s += l){ + l = chartorune(&r, s); + if (r == Runeerror) + for (i = 0; i < l; i++){ + s[i] = '_'; + cprint("%s->name (%s) bad UTF\n", name, d->name); + dmod++; + } + } + strcat(name, d->name); + + if(d->mode & DDIR){ + if(ns > 1) + strcat(name, "/"); + if(flags & Cpdir) + cprint("%s\n", name); + } else + if(flags & Cpfile) + cprint("%s\n", name); + + qpath = d->qid.path & ~QPDIR; + qmark(qpath); + if(qpath > maxq) + maxq = qpath; + for(i=0; i<NDBLOCK; i++) { + a = d->dblock[i]; + if(!a) + continue; + if(amark(a)) { + d->dblock[i] = 0; + dmod++; + continue; + } + if(d->mode & DDIR) + dmod += checkdir(a, qpath); + else if(flags & Crdall) + xread(a, qpath); + } + a = d->iblock; + if(a && amark(a)) { + d->iblock = 0; + dmod++; + } + else if(a) + dmod += checkindir(a, d, qpath); + + a = d->diblock; + if(a && amark(a)) { + d->diblock = 0; + return dmod + 1; + } + dmod += touch(a); + if(p = xtag(a, Tind2, qpath)){ + for(i=0; i<INDPERBUF; i++){ + a = ((long*)p->iobuf)[i]; + if(!a) + continue; + if(amark(a)) { + if(flags & Cbad) { + ((long*)p->iobuf)[i] = 0; + p->flags |= Bmod; + } + continue; + } + dmod += checkindir(a, d, qpath); + } + putbuf(p); + } + return dmod; +} + +static +void +ckfreelist(Superb *sb) +{ + long a, lo, hi; + int n, i; + Iobuf *p; + Fbuf *fb; + + + strcpy(name, "free list"); + cprint("check %s\n", name); + fb = &sb->fbuf; + a = sbaddr; + p = 0; + lo = 0; + hi = 0; + for(;;) { + n = fb->nfree; + if(n < 0 || n > FEPERBUF) { + cprint("check: nfree bad %ld\n", a); + break; + } + for(i=1; i<n; i++) { + a = fb->free[i]; + if(a && !fmark(a)) { + if(!lo || lo > a) + lo = a; + if(!hi || hi < a) + hi = a; + } + } + a = fb->free[0]; + if(!a) + break; + if(fmark(a)) + break; + if(!lo || lo > a) + lo = a; + if(!hi || hi < a) + hi = a; + if(p) + putbuf(p); + p = xtag(a, Tfree, QPNONE); + if(!p) + break; + fb = (Fbuf*)p->iobuf; + } + if(p) + putbuf(p); + cprint("lo = %ld; hi = %ld\n", lo, hi); +} + +/* + * make freelist from scratch + */ +static +void +mkfreelist(Superb *sb) +{ + long a; + int i, b; + + strcpy(name, "free list"); + memset(&sb->fbuf, 0, sizeof(sb->fbuf)); + sb->fbuf.nfree = 1; + sb->tfree = 0; + for(a=fsize-fstart-1; a >= 0; a--) { + i = a/8; + if(i < 0 || i >= sizabits) + continue; + b = 1 << (a&7); + if(abits[i] & b) + continue; + addfree(dev, fstart+a, sb); + abits[i] |= b; + } +} + +static +Dentry* +maked(long a, int s, long qpath) +{ + Iobuf *p; + Dentry *d, *d1; + + p = xtag(a, Tdir, qpath); + if(!p) + return 0; + d = getdir(p, s); + d1 = dalloc(sizeof(Dentry)); + memmove(d1, d, sizeof(Dentry)); + putbuf(p); + return d1; +} + +static +void +modd(long a, int s, Dentry *d1) +{ + Iobuf *p; + Dentry *d; + + if(!(flags & Cbad)) + return; + p = getbuf(dev, a, Bread); + d = getdir(p, s); + if(!d) { + if(p) + putbuf(p); + return; + } + memmove(d, d1, sizeof(Dentry)); + p->flags |= Bmod; + putbuf(p); +} + +static +void +xread(long a, long qpath) +{ + Iobuf *p; + + p = xtag(a, Tfile, qpath); + if(p) + putbuf(p); +} + +static +Iobuf* +xtag(long a, int tag, long qpath) +{ + Iobuf *p; + + if(a == 0) + return 0; + p = getbuf(dev, a, Bread); + if(!p) { + cprint("check: \"%s\": xtag: p null\n", name); + if(flags & (Cream|Ctag)) { + p = getbuf(dev, a, Bmod); + if(p) { + memset(p->iobuf, 0, RBUFSIZE); + settag(p, tag, qpath); + mod++; + return p; + } + } + return 0; + } + if(checktag(p, tag, qpath)) { + cprint("check: \"%s\": xtag: checktag\n", name); + if(flags & Cream) + memset(p->iobuf, 0, RBUFSIZE); + if(flags & (Cream|Ctag)) { + settag(p, tag, qpath); + mod++; + } + return p; + } + return p; +} + +static +int +amark(long a) +{ + long i; + int b; + + if(a < fstart || a >= fsize) { + cprint("check: \"%s\": range %ld\n", + name, a); + nbad++; + return 1; + } + a -= fstart; + i = a/8; + b = 1 << (a&7); + if(abits[i] & b) { + if(!ronly) { + if(ndup < 10) + cprint("check: \"%s\": address dup %ld\n", + name, fstart+a); + else + if(ndup == 10) + cprint("..."); + } + ndup++; + return 0; /* really?? */ + } + abits[i] |= b; + nused++; + return 0; +} + +static +int +fmark(long a) +{ + long i; + int b; + + if(a < fstart || a >= fsize) { + cprint("check: \"%s\": range %ld\n", + name, a); + nbad++; + return 1; + } + a -= fstart; + i = a/8; + b = 1 << (a&7); + if(abits[i] & b) { + cprint("check: \"%s\": address dup %ld\n", + name, fstart+a); + nfdup++; + return 1; + } + abits[i] |= b; + nfree++; + return 0; +} + +static +void +missing(void) +{ + long a, i; + int b, n; + + n = 0; + for(a=fsize-fstart-1; a>=0; a--) { + i = a/8; + b = 1 << (a&7); + if(!(abits[i] & b)) { + cprint("missing: %ld\n", fstart+a); + n++; + } + if(n > 10) { + cprint(" ...\n"); + break; + } + } +} + +static +void +qmark(long qpath) +{ + int i, b; + + i = qpath/8; + b = 1 << (qpath&7); + if(i < 0 || i >= sizqbits) { + nqbad++; + if(nqbad < 20) + cprint("check: \"%s\": qid out of range %lux\n", + name, qpath); + return; + } + if((qbits[i] & b) && !ronly) { + nqbad++; + if(nqbad < 20) + cprint("check: \"%s\": qid dup %lux\n", + name, qpath); + } + qbits[i] |= b; +} diff --git a/sys/src/cmd/disk/kfs/con.c b/sys/src/cmd/disk/kfs/con.c new file mode 100755 index 000000000..7ed350a81 --- /dev/null +++ b/sys/src/cmd/disk/kfs/con.c @@ -0,0 +1,705 @@ +#include "all.h" +#include "9p1.h" + +static char elem[NAMELEN]; +static Filsys* cur_fs; +static char conline[100]; + +void +consserve(void) +{ + con_session(); + cmd_exec("cfs"); + cmd_exec("user"); +} + +int +cmd_exec(char *arg) +{ + char *s, *c; + int i; + + for(i=0; s = command[i].string; i++) { + for(c=arg; *s; c++) + if(*c != *s++) + goto brk; + if(*c == '\0' || *c == ' ' || *c == '\t'){ + cons.arg = c; + (*command[i].func)(); + return 1; + } + brk:; + } + return 0; +} + +void +cmd_check(void) +{ + char *s; + int flags; + + flags = 0; + for(s = cons.arg; *s; s++){ + while(*s == ' ' || *s == '\t') + s++; + if(*s == '\0') + break; + switch(*s){ + /* rebuild the free list */ + case 'f': flags |= Cfree; break; + /* fix bad tags */ + case 't': flags |= Ctag; break; + /* fix bad tags and clear the contents of the block */ + case 'c': flags |= Cream; break; + /* delete all redundant references to a block */ + case 'd': flags |= Cbad; break; + /* read and check tags on all blocks */ + case 'r': flags |= Crdall; break; + /* write all of the blocks you touch */ + case 'w': flags |= Ctouch; break; + /* print all directories as they are read */ + case 'p': flags |= Cpdir; break; + /* print all files as they are read */ + case 'P': flags |= Cpfile; break; + /* quiet, just report really nasty stuff */ + case 'q': flags |= Cquiet; break; + } + } + check(cur_fs, flags); +} + +enum +{ + Sset = (1<<0), + Setc = (1<<1), +}; +void +cmd_stats(void) +{ + cprint("work stats\n"); + cprint(" work = %A rps\n", (Filta){&cons.work, 1}); + cprint(" rate = %A tBps\n", (Filta){&cons.rate, 1000}); + cprint(" hits = %A iops\n", (Filta){&cons.bhit, 1}); + cprint(" read = %A iops\n", (Filta){&cons.bread, 1}); + cprint(" init = %A iops\n", (Filta){&cons.binit, 1}); +/* for(i = 0; i < MAXTAG; i++) + cprint(" tag %G = %A iops\n", i, (Filta){&cons.tags[i], 1}); +*/ +} + +void +cmd_sync(void) +{ + rlock(&mainlock); + syncall(); + runlock(&mainlock); +} + +void +cmd_halt(void) +{ + wlock(&mainlock); + syncall(); + superok(cur_fs->dev, superaddr(cur_fs->dev), 1); + print("kfs: file system halted\n"); +} + +void +cmd_start(void) +{ + superok(cur_fs->dev, superaddr(cur_fs->dev), 0); + wunlock(&mainlock); + print("kfs: file system started\n"); +} + +void +cmd_help(void) +{ + int i; + + for(i=0; command[i].string; i++) + cprint(" %s %s\n", command[i].string, command[i].args); + cprint("check options:\n" + " r read all blocks\n" + " f rebuild the free list\n" + " t fix all bad tags\n" + " c fix bad tags and zero the blocks\n" + " d delete all redundant references to blocks\n" + " p print directories as they are checked\n" + " P print all files as they are checked\n" + " w write all blocks that are read\n"); +} + +void +cmd_create(void) +{ + int uid, gid, err; + long perm; + char oelem[NAMELEN]; + char name[NAMELEN]; + + if(err = con_clone(FID1, FID2)){ + cprint("clone failed: %s\n", errstring[err]); + return; + } + if(skipbl(1)){ + cprint("skipbl\n"); + return; + } + oelem[0] = 0; + while(nextelem()) { + if(oelem[0]) + if(err = con_walk(FID2, oelem)){ + cprint("walk failed: %s\n", errstring[err]); + return; + } + memmove(oelem, elem, NAMELEN); + } + if(skipbl(1)) + return; + uid = strtouid(cname(name)); + if(uid == 0){ + cprint("unknown user %s\n", name); + return; + } + gid = strtouid(cname(name)); + if(gid == 0){ + cprint("unknown group %s\n", name); + return; + } + perm = number(0777, 8); + skipbl(0); + for(; *cons.arg; cons.arg++){ + if(*cons.arg == 'l') + perm |= PLOCK; + else + if(*cons.arg == 'a') + perm |= PAPND; + else + if(*cons.arg == 'd') + perm |= PDIR; + else + break; + } + err = con_create(FID2, elem, uid, gid, perm, 0); + if(err) + cprint("can't create %s: %s\n", elem, errstring[err]); +} + +void +cmd_clri(void) +{ + if(con_clone(FID1, FID2)) + return; + if(skipbl(1)) + return; + while(nextelem()) + if(con_walk(FID2, elem)){ + cprint("can't walk %s\n", elem); + return; + } + con_clri(FID2); +} + +void +cmd_rename(void) +{ + ulong perm; + Dentry d; + char stat[DIRREC]; + char oelem[NAMELEN], noelem[NAMELEN], nxelem[NAMELEN]; + int err; + + if(con_clone(FID1, FID2)) + return; + if(skipbl(1)) + return; + oelem[0] = 0; + while(nextelem()) { + if(oelem[0]) + if(con_walk(FID2, oelem)){ + cprint("file does not exits"); + return; + } + memmove(oelem, elem, NAMELEN); + } + if(skipbl(1)) + return; + if(cons.arg[0]=='/'){ + if(con_clone(FID1, FID3)) + return; + noelem[0] = 0; + while(nextelem()){ + if(noelem[0]) + if(con_walk(FID3, noelem)){ + cprint("target path %s does not exist", noelem); + return; + } + memmove(noelem, elem, NAMELEN); + } + if(!con_walk(FID3, elem)){ + cprint("target %s already exists\n", elem); + return; + } + if(con_walk(FID2, oelem)){ + cprint("src %s does not exist\n", oelem); + return; + } + /* + * we know the target does not exist, + * the source does exist. + * to do the rename, create the target and then + * copy the directory entry directly. then remove the source. + */ + if(err = con_stat(FID2, stat)){ + cprint("can't stat file: %s\n", errstring[err]); + return; + } + convM2D9p1(stat, &d); + perm = (d.mode&0777)|((d.mode&0x7000)<<17); + if(err = con_create(FID3, elem, d.uid, d.gid, perm, (d.mode&DDIR)?OREAD:ORDWR)){ + cprint("can't create %s: %s\n", elem, errstring[err]); + return; + } + if(err = con_swap(FID2, FID3)){ + cprint("can't swap data: %s\n", errstring[err]); + return; + } + if(err = con_remove(FID2)){ + cprint("can't remove file: %s\n", errstring[err]); + return; + } + }else{ + cname(nxelem); + if(strchr(nxelem, '/')){ + cprint("bad rename target: not full path, but contains slashes\n"); + return; + } + if(!con_walk(FID2, nxelem)) + cprint("file %s already exists\n", nxelem); + else if(con_walk(FID2, oelem)) + cprint("file does not already exist\n"); + else if(err = con_stat(FID2, stat)) + cprint("can't stat file: %s\n", errstring[err]); + else{ + convM2D9p1(stat, &d); + strncpy(d.name, nxelem, NAMELEN); + convD2M9p1(&d, stat); + if(err = con_wstat(FID2, stat)) + cprint("can't move file: %s\n", errstring[err]); + } + } +} + +void +cmd_remove(void) +{ + if(con_clone(FID1, FID2)) + return; + if(skipbl(1)) + return; + while(nextelem()) + if(con_walk(FID2, elem)){ + cprint("can't walk %s\n", elem); + return; + } + con_remove(FID2); +} + +void +cmd_cfs(void) +{ + Filsys *fs; + + if(*cons.arg != ' ') { + fs = &filesys[0]; /* default */ + } else { + if(skipbl(1)){ + cprint("skipbl\n"); + return; + } + if(!nextelem()) + fs = &filesys[0]; /* default */ + else + fs = fsstr(elem); + } + if(fs == 0) { + cprint("unknown file system %s\n", elem); + return; + } + if(con_attach(FID1, "adm", fs->name)) + panic("FID1 attach to root"); + cur_fs = fs; +} + +/* + * find out the length of a file + * given the mesg version of a stat buffer + * we call this because convM2D is different + * for the file system than in the os + */ +static uvlong +statlen(char *ap) +{ + uchar *p; + ulong ll, hl; + + p = (uchar*)ap; + p += 3*28+5*4; + ll = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); + hl = p[4] | (p[5]<<8) | (p[6]<<16) | (p[7]<<24); + return ll | ((uvlong) hl << 32); +} + +int +adduser(char *user, int isgroup) +{ + char stat[DIRREC]; + char msg[100]; + Uid *u; + int i, c, nu; + + /* + * check uniq of name + * and get next uid + */ + cmd_exec("cfs"); + cmd_exec("user"); + if(isgroup) + nu = 9000; + else + nu = 0; + for(i=0, u=uid; i<conf.nuid; i++,u++) { + c = u->uid; + if(c == 0) + break; + if(strcmp(uidspace+u->offset, user) == 0) + return 1; + if(c >= 9000 && !isgroup) + continue; + if(c > nu) + nu = c; + } + nu++; + if(isgroup){ + if(nu >= 0x10000) { + cprint("out of group ids\n"); + return 0; + } + } else { + if(nu >= 9000) { + cprint("out of user ids\n"); + return 0; + } + } + + /* + * write onto adm/users + */ + if(con_clone(FID1, FID2) + || con_path(FID2, "/adm/users") + || con_open(FID2, 1)) { + cprint("can't open /adm/users\n"); + return 0; + } + + sprint(msg, "%d:%s:%s:\n", nu, user, user); + cprint("add user %s", msg); + c = strlen(msg); + i = con_stat(FID2, stat); + if(i){ + cprint("can't stat /adm/users: %s\n", errstring[i]); + return 0; + } + i = con_write(FID2, msg, statlen(stat), c); + if(i != c){ + cprint("short write on /adm/users: %d %d\n", c, i); + return 0; + } + return 1; +} + +void +cmd_newuser(void) +{ + char user[NAMELEN], param[NAMELEN], msg[100]; + int i, c; + + /* + * get uid + */ + cname(user); + cname(param); + for(i=0; i<NAMELEN; i++) { + c = user[i]; + if(c == 0) + break; + if(c >= '0' && c <= '9' + || c >= 'a' && c <= 'z' + || c >= 'A' && c <= 'Z') + continue; + cprint("bad character in name: 0x%x\n", c); + return; + } + if(i < 2) { + cprint("name too short: %s\n", user); + return; + } + if(i >= NAMELEN) { + cprint("name too long: %s\n", user); + return; + } + + switch(param[0]){ + case 0: + if(!adduser(user, 0)) + return; + cmd_exec("user"); + break; + case ':': + adduser(user, 1); + cmd_exec("user"); + return; + case '#': + adduser(user, 0); + cmd_exec("user"); + return; + } + + /* + * create directories + */ + cmd_exec("user"); + sprint(msg, "create /usr/%s %s %s 775 d", user, user, user); + cmd_exec(msg); + sprint(msg, "create /usr/%s/tmp %s %s 775 d", user, user, user); + cmd_exec(msg); + sprint(msg, "create /usr/%s/lib %s %s 775 d", user, user, user); + cmd_exec(msg); + sprint(msg, "create /usr/%s/bin %s %s 775 d", user, user, user); + cmd_exec(msg); + sprint(msg, "create /usr/%s/bin/rc %s %s 775 d", user, user, user); + cmd_exec(msg); + sprint(msg, "create /usr/%s/bin/mips %s %s 775 d", user, user, user); + cmd_exec(msg); + sprint(msg, "create /usr/%s/bin/386 %s %s 775 d", user, user, user); + cmd_exec(msg); + sprint(msg, "create /usr/%s/bin/power %s %s 775 d", user, user, user); + cmd_exec(msg); + sprint(msg, "create /usr/%s/bin/alpha %s %s 775 d", user, user, user); + cmd_exec(msg); +} + +void +cmd_checkuser(void) +{ + uchar buf[DIRREC], *p; + static char utime[4]; + + if(con_clone(FID1, FID2) + || con_path(FID2, "/adm/users") + || con_open(FID2, 0) + || con_stat(FID2, (char*)buf)) + return; + p = buf + 3*NAMELEN + 4*4; + if(memcmp(utime, p, 4) == 0) + return; + memmove(utime, p, 4); + cmd_user(); +} + +void +cmd_allow(void) +{ + wstatallow = 1; +} + +void +cmd_disallow(void) +{ + wstatallow = 0; +} + +void +cmd_chat(void) +{ + chat = 1 - chat; +} + +void +cmd_atime(void) +{ + noatime = !noatime; + if(noatime) + cprint("atimes will not be updated\n"); + else + cprint("atimes will be updated\n"); +} + +void +cmd_noneattach(void) +{ + allownone = !allownone; + if(allownone) + cprint("none can attach to new connections\n"); + else + cprint("none can only attach on authenticated connections\n"); +} + +void +cmd_listen(void) +{ + char addr[NAMELEN]; + + if(skipbl(0)) + strcpy(addr, "tcp!*!564"); /* 9fs */ + else + cname(addr); + + if(netserve(addr)) + cprint("announce %s failed\n", addr); + else + cprint("announce %s\n", addr); +} + +void +cmd_nowritegroup(void) +{ + writegroup = 0; +} + +Command command[] = +{ + "allow", cmd_allow, "", + "allowoff", cmd_disallow, "", + "atime", cmd_atime, "", + "cfs", cmd_cfs, "[filesys]", + "chat", cmd_chat, "", + "check", cmd_check, "[cdfpPqrtw]", + "clri", cmd_clri, "filename", + "create", cmd_create, "filename user group perm [ald]", + "disallow", cmd_disallow, "", + "halt", cmd_halt, "", + "help", cmd_help, "", + "listen", cmd_listen, "[address]", + "newuser", cmd_newuser, "username", + "noneattach", cmd_noneattach, "", + "nowritegroup", cmd_nowritegroup, "", + "remove", cmd_remove, "filename", + "rename", cmd_rename, "file newname", + "start", cmd_start, "", + "stats", cmd_stats, "[fw]", + "sync", cmd_sync, "", + "user", cmd_user, "", + 0 +}; + +int +skipbl(int err) +{ + if(*cons.arg != ' ') { + if(err) + cprint("syntax error\n"); + return 1; + } + while(*cons.arg == ' ') + cons.arg++; + return 0; +} + +char* +_cname(char *name) +{ + int i, c; + + memset(name, 0, NAMELEN); + for(i=0;; i++) { + c = *cons.arg; + switch(c) { + case ' ': + case '\t': + case '\n': + case '\0': + return name; + } + if(i < NAMELEN-1) + name[i] = c; + cons.arg++; + } +} + +char* +cname(char *name) +{ + skipbl(0); + return _cname(name); +} + +int +nextelem(void) +{ + char *e; + int i, c; + + e = elem; + while(*cons.arg == '/') + cons.arg++; + c = *cons.arg; + if(c == 0 || c == ' ') + return 0; + for(i = 0; c = *cons.arg; i++) { + if(c == ' ' || c == '/') + break; + if(i == NAMELEN) { + cprint("path name component too long\n"); + return 0; + } + *e++ = c; + cons.arg++; + } + *e = 0; + return 1; +} + +long +number(int d, int base) +{ + int c, sign, any; + long n; + + sign = 0; + any = 0; + n = 0; + + c = *cons.arg; + while(c == ' ') { + cons.arg++; + c = *cons.arg; + } + if(c == '-') { + sign = 1; + cons.arg++; + c = *cons.arg; + } + while((c >= '0' && c <= '9') || + (base == 16 && c >= 'a' && c <= 'f') || + (base == 16 && c >= 'A' && c <= 'F')) { + n *= base; + if(c >= 'a' && c <= 'f') + n += c - 'a' + 10; + else + if(c >= 'A' && c <= 'F') + n += c - 'A' + 10; + else + n += c - '0'; + cons.arg++; + c = *cons.arg; + any = 1; + } + if(!any) + return d; + if(sign) + n = -n; + return n; +} diff --git a/sys/src/cmd/disk/kfs/console.c b/sys/src/cmd/disk/kfs/console.c new file mode 100755 index 000000000..ec7385d42 --- /dev/null +++ b/sys/src/cmd/disk/kfs/console.c @@ -0,0 +1,371 @@ +#include "all.h" +#include "9p1.h" + +void +fcall9p1(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + int t; + + rlock(&mainlock); + t = in->type; + if(t < 0 || t >= MAXSYSCALL || (t&1) || !call9p1[t]) { + print("bad message type %d\n", t); + panic(""); + } + ou->type = t+1; + ou->err = 0; + + rlock(&cp->reflock); + (*call9p1[t])(cp, in, ou); + runlock(&cp->reflock); + + if(ou->err) + if(CHAT(cp)) + print(" error: %s\n", errstring[ou->err]); + cons.work.count++; + runlock(&mainlock); +} + +int +con_session(void) +{ + Oldfcall in, ou; + + in.type = Tsession9p1; + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +con_attach(int fid, char *uid, char *arg) +{ + Oldfcall in, ou; + + in.type = Tattach9p1; + in.fid = fid; + strncpy(in.uname, uid, NAMELEN); + strncpy(in.aname, arg, NAMELEN); + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +con_clone(int fid1, int fid2) +{ + Oldfcall in, ou; + + in.type = Tclone9p1; + in.fid = fid1; + in.newfid = fid2; + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +con_path(int fid, char *path) +{ + Oldfcall in, ou; + char *p; + + in.type = Twalk9p1; + in.fid = fid; + +loop: + if(*path == 0) + return 0; + strncpy(in.name, path, NAMELEN); + if(p = strchr(path, '/')) { + path = p+1; + if(p = strchr(in.name, '/')) + *p = 0; + } else + path = strchr(path, 0); + if(in.name[0]) { + fcall9p1(cons.chan, &in, &ou); + if(ou.err) + return ou.err; + } + goto loop; +} + +int +con_walk(int fid, char *name) +{ + Oldfcall in, ou; + + in.type = Twalk9p1; + in.fid = fid; + strncpy(in.name, name, NAMELEN); + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +con_stat(int fid, char *data) +{ + Oldfcall in, ou; + + in.type = Tstat9p1; + in.fid = fid; + fcall9p1(cons.chan, &in, &ou); + if(ou.err == 0) + memmove(data, ou.stat, sizeof ou.stat); + return ou.err; +} + +int +con_wstat(int fid, char *data) +{ + Oldfcall in, ou; + + in.type = Twstat9p1; + in.fid = fid; + memmove(in.stat, data, sizeof in.stat); + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +con_open(int fid, int mode) +{ + Oldfcall in, ou; + + in.type = Topen9p1; + in.fid = fid; + in.mode = mode; + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +con_read(int fid, char *data, long offset, int count) +{ + Oldfcall in, ou; + + in.type = Tread9p1; + in.fid = fid; + in.offset = offset; + in.count = count; + ou.data = data; + fcall9p1(cons.chan, &in, &ou); + if(ou.err) + return 0; + return ou.count; +} + +int +con_write(int fid, char *data, long offset, int count) +{ + Oldfcall in, ou; + + in.type = Twrite9p1; + in.fid = fid; + in.data = data; + in.offset = offset; + in.count = count; + fcall9p1(cons.chan, &in, &ou); + if(ou.err) + return 0; + return ou.count; +} + +int +con_remove(int fid) +{ + Oldfcall in, ou; + + in.type = Tremove9p1; + in.fid = fid; + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +con_create(int fid, char *name, int uid, int gid, long perm, int mode) +{ + Oldfcall in, ou; + + in.type = Tcreate9p1; + in.fid = fid; + strncpy(in.name, name, NAMELEN); + in.perm = perm; + in.mode = mode; + cons.uid = uid; /* beyond ugly */ + cons.gid = gid; + fcall9p1(cons.chan, &in, &ou); + return ou.err; +} + +int +doclri(File *f) +{ + Iobuf *p, *p1; + Dentry *d, *d1; + int err; + + err = 0; + p = 0; + p1 = 0; + if(isro(f->fs->dev)) { + err = Eronly; + goto out; + } + /* + * check on parent directory of file to be deleted + */ + if(f->wpath == 0 || f->wpath->addr == f->addr) { + err = Ephase; + goto out; + } + p1 = getbuf(f->fs->dev, f->wpath->addr, Bread); + d1 = getdir(p1, f->wpath->slot); + if(!d1 || checktag(p1, Tdir, QPNONE) || !(d1->mode & DALLOC)) { + err = Ephase; + goto out; + } + + accessdir(p1, d1, FWRITE); + putbuf(p1); + p1 = 0; + + /* + * check on file to be deleted + */ + p = getbuf(f->fs->dev, f->addr, Bread); + d = getdir(p, f->slot); + + + /* + * do it + */ + memset(d, 0, sizeof(Dentry)); + settag(p, Tdir, QPNONE); + freewp(f->wpath); + freefp(f); + +out: + if(p1) + putbuf(p1); + if(p) + putbuf(p); + return err; +} + +void +f_clri(Chan *cp, Oldfcall *in, Oldfcall *ou) +{ + File *f; + + if(CHAT(cp)) { + print("c_clri %d\n", cp->chan); + print(" fid = %d\n", in->fid); + } + + f = filep(cp, in->fid, 0); + if(!f) { + ou->err = Efid; + goto out; + } + ou->err = doclri(f); + +out: + ou->fid = in->fid; + if(f) + qunlock(f); +} + +int +con_clri(int fid) +{ + Oldfcall in, ou; + Chan *cp; + + in.type = Tremove9p1; + in.fid = fid; + cp = cons.chan; + + rlock(&mainlock); + ou.type = Tremove9p1+1; + ou.err = 0; + + rlock(&cp->reflock); + + f_clri(cp, &in, &ou); + + runlock(&cp->reflock); + + cons.work.count++; + runlock(&mainlock); + return ou.err; +} + +int +con_swap(int fid1, int fid2) +{ + int err; + Iobuf *p1, *p2; + File *f1, *f2; + Dentry *d1, *d2; + Dentry dt1, dt2; + Chan *cp; + + cp = cons.chan; + err = 0; + rlock(&mainlock); + rlock(&cp->reflock); + + f2 = nil; + p1 = p2 = nil; + f1 = filep(cp, fid1, 0); + if(!f1){ + err = Efid; + goto out; + } + p1 = getbuf(f1->fs->dev, f1->addr, Bread|Bmod); + d1 = getdir(p1, f1->slot); + if(!d1 || !(d1->mode&DALLOC)){ + err = Ealloc; + goto out; + } + + f2 = filep(cp, fid2, 0); + if(!f2){ + err = Efid; + goto out; + } + if(memcmp(&f1->fs->dev, &f2->fs->dev, 4)==0 + && f2->addr == f1->addr) + p2 = p1; + else + p2 = getbuf(f2->fs->dev, f2->addr, Bread|Bmod); + d2 = getdir(p2, f2->slot); + if(!d2 || !(d2->mode&DALLOC)){ + err = Ealloc; + goto out; + } + + dt1 = *d1; + dt2 = *d2; + *d1 = dt2; + *d2 = dt1; + memmove(d1->name, dt1.name, NAMELEN); + memmove(d2->name, dt2.name, NAMELEN); + + mkqid(&f1->qid, d1, 1); + mkqid(&f2->qid, d2, 1); +out: + if(f1) + qunlock(f1); + if(f2) + qunlock(f2); + if(p1) + putbuf(p1); + if(p2 && p2!=p1) + putbuf(p2); + + runlock(&cp->reflock); + cons.work.count++; + runlock(&mainlock); + + return err; +} diff --git a/sys/src/cmd/disk/kfs/dat.c b/sys/src/cmd/disk/kfs/dat.c new file mode 100755 index 000000000..5ab3c0eb8 --- /dev/null +++ b/sys/src/cmd/disk/kfs/dat.c @@ -0,0 +1,87 @@ +#include "all.h" + +Uid* uid; +char* uidspace; +short* gidspace; +RWLock mainlock; +long boottime; +Tlock *tlocks; +Conf conf; +Cons cons; +Chan *chan; +char service[2*NAMELEN]; +char *progname; +char *procname; +int RBUFSIZE; +int BUFSIZE; +int DIRPERBUF; +int INDPERBUF; +int INDPERBUF2; +int FEPERBUF; + +Filsys filesys[MAXFILSYS] = +{ + {"main", {Devwren, 0, 0, 0}, 0}, +}; + +Device devnone = {Devnone, 0, 0, 0}; + +Devcall devcall[MAXDEV] = { + [Devnone] {0}, + [Devwren] {wreninit, wrenream, wrencheck, wrensuper, wrenroot, wrensize, wrenread, wrenwrite}, +}; + +char* tagnames[] = +{ + [Tbuck] "Tbuck", + [Tdir] "Tdir", + [Tfile] "Tfile", + [Tfree] "Tfree", + [Tind1] "Tind1", + [Tind2] "Tind2", + [Tnone] "Tnone", + [Tsuper] "Tsuper", + [Tvirgo] "Tvirgo", + [Tcache] "Tcache", +}; + +char *errstring[MAXERR] = +{ + [Ebadspc] "attach -- bad specifier", + [Efid] "unknown fid", + [Echar] "bad character in directory name", + [Eopen] "read/write -- on non open fid", + [Ecount] "read/write -- count too big", + [Ealloc] "phase error -- directory entry not allocated", + [Eqid] "phase error -- qid does not match", + [Eauth] "authentication failed", + [Eauthmsg] "kfs: authentication not required", + [Eaccess] "access permission denied", + [Eentry] "directory entry not found", + [Emode] "open/create -- unknown mode", + [Edir1] "walk -- in a non-directory", + [Edir2] "create -- in a non-directory", + [Ephase] "phase error -- cannot happen", + [Eexist] "create -- file exists", + [Edot] "create -- . and .. illegal names", + [Eempty] "remove -- directory not empty", + [Ebadu] "attach -- privileged user", + [Enotu] "wstat -- not owner", + [Enotg] "wstat -- not in group", + [Enotl] "wstat -- attempt to change length", + [Enotd] "wstat -- attempt to change directory", + [Enotm] "wstat -- unknown type/mode", + [Ename] "create/wstat -- bad character in file name", + [Ewalk] "walk -- too many (system wide)", + [Eronly] "file system read only", + [Efull] "file system full", + [Eoffset] "read/write -- offset negative", + [Elocked] "open/create -- file is locked", + [Ebroken] "close/read/write -- lock is broken", + [Efidinuse] "fid already in use", + [Etoolong] "name too long", + [Ersc] "it's russ's fault. bug him.", + [Econvert] "protocol botch", + [Eqidmode] "wstat -- qid.type/dir.mode mismatch", + [Esystem] "kfs system error", +}; diff --git a/sys/src/cmd/disk/kfs/dat.h b/sys/src/cmd/disk/kfs/dat.h new file mode 100755 index 000000000..127b1b825 --- /dev/null +++ b/sys/src/cmd/disk/kfs/dat.h @@ -0,0 +1,170 @@ +typedef struct Chan Chan; +typedef struct Command Command; +typedef struct Conf Conf; +typedef struct Cons Cons; +typedef struct Devcall Devcall; + +#define MAXBUFSIZE (16*1024) /* max. buffer size */ + +#include "portdat.h" + +struct Chan +{ + int chan; /* fd request came in on */ + QLock rlock, wlock; /* lock for reading/writing messages on chan */ + int type; + int flags; + long whotime; + File* flist; /* base of file structures */ + Lock flock; /* manipulate flist */ + RWLock reflock; /* lock for Tflush */ + int msize; /* version */ + int authed; /* someone other than ``none'' has authed */ + +/* 9p1 auth */ + uchar chal[8]; + uchar rchal[8]; + int idoffset; + int idvec; + Lock idlock; +}; + +/* + * console cons.flag flags + */ +enum +{ + Fchat = (1<<0), /* print out filesys rpc traffic */ + Fuid = (1<<2), /* print out uids */ + /* debugging flags for drivers */ +}; + +struct Cons +{ + int flags; /* overall flags for all channels */ + int uid; /* botch -- used to get uid on cons_create */ + int gid; /* botch -- used to get gid on cons_create */ + int allow; /* no-protection flag */ + long offset; /* used to read files, c.f. fchar */ + char* arg; /* pointer to remaining line */ + + Chan *chan; /* console channel */ + Chan *srvchan; /* local server channel */ + + Filter work; /* thruput in messages */ + Filter rate; /* thruput in bytes */ + Filter bhit; /* getbufs that hit */ + Filter bread; /* getbufs that miss and read */ + Filter binit; /* getbufs that miss and dont read */ + Filter tags[MAXTAG]; /* reads of each type of block */ +}; + +struct Conf +{ + ulong niobuf; /* number of iobufs to allocate */ + ulong nuid; /* distinct uids */ + ulong uidspace; /* space for uid names -- derrived from nuid */ + ulong gidspace; /* space for gid names -- derrived from nuid */ + ulong nserve; /* server processes */ + ulong nfile; /* number of fid -- system wide */ + ulong nwpath; /* number of active paths, derrived from nfile */ + ulong bootsize; /* number of bytes reserved for booting */ +}; + +struct Command +{ + char *string; + void (*func)(void); + char *args; +}; + +struct Devcall +{ + void (*init)(Device); + void (*ream)(Device); + int (*check)(Device); + long (*super)(Device); + long (*root)(Device); + long (*size)(Device); + int (*read)(Device, long, void*); + int (*write)(Device, long, void*); +}; + +/* + * device types + */ +enum +{ + Devnone = 0, + Devwren, + MAXDEV +}; + +/* + * file systems + */ +enum +{ + MAXFILSYS = 4 +}; + +/* + * should be in portdat.h + */ +#define QPDIR 0x80000000L +#define QPNONE 0 +#define QPROOT 1 +#define QPSUPER 2 + +/* + * perm argument in p9 create + */ +#define PDIR (1L<<31) /* is a directory */ +#define PAPND (1L<<30) /* is append only */ +#define PLOCK (1L<<29) /* is locked on open */ + +#define NOF (-1) + +#define FID1 1 +#define FID2 2 +#define FID3 3 + +#define SECOND(n) (n) +#define MINUTE(n) (n*SECOND(60)) +#define HOUR(n) (n*MINUTE(60)) +#define DAY(n) (n*HOUR(24)) +#define TLOCK MINUTE(5) + +#define CHAT(cp) (chat) +#define QID9P1(a,b) (Qid9p1){a,b} + +extern Uid* uid; +extern char* uidspace; +extern short* gidspace; +extern char* errstring[MAXERR]; +extern Chan* chans; +extern RWLock mainlock; +extern long boottime; +extern Tlock *tlocks; +extern Device devnone; +extern Filsys filesys[]; +extern char service[]; +extern char* tagnames[]; +extern Conf conf; +extern Cons cons; +extern Command command[]; +extern Chan *chan; +extern Devcall devcall[]; +extern char *progname; +extern char *procname; +extern long niob; +extern long nhiob; +extern Hiob *hiob; +extern int chat; +extern int writeallow; +extern int wstatallow; +extern int allownone; +extern int noatime; +extern int writegroup; + +extern Lock wpathlock; diff --git a/sys/src/cmd/disk/kfs/dentry.c b/sys/src/cmd/disk/kfs/dentry.c new file mode 100755 index 000000000..c9f8c486b --- /dev/null +++ b/sys/src/cmd/disk/kfs/dentry.c @@ -0,0 +1,174 @@ +#include "all.h" + +Dentry* +getdir(Iobuf *p, int slot) +{ + Dentry *d; + + if(!p) + return 0; + d = (Dentry*)p->iobuf + slot%DIRPERBUF; + return d; +} + +void +accessdir(Iobuf *p, Dentry *d, int f) +{ + long t; + + if(p && !isro(p->dev)) { + if(!(f & (FWRITE|FWSTAT)) && noatime) + return; + t = time(nil); + if(f & (FREAD|FWRITE|FWSTAT)){ + d->atime = t; + p->flags |= Bmod; + } + if(f & FWRITE) { + d->mtime = t; + d->qid.version++; + p->flags |= Bmod; + } + } +} + +void +dbufread(Iobuf *p, Dentry *d, long a) +{ + USED(p, d, a); +} + +long +rel2abs(Iobuf *p, Dentry *d, long a, int tag, int putb) +{ + long addr, qpath; + Device dev; + + if(a < 0) { + print("dnodebuf: neg\n"); + return 0; + } + qpath = d->qid.path; + dev = p->dev; + if(a < NDBLOCK) { + addr = d->dblock[a]; + if(!addr && tag) { + addr = balloc(dev, tag, qpath); + d->dblock[a] = addr; + p->flags |= Bmod|Bimm; + } + if(putb) + putbuf(p); + return addr; + } + a -= NDBLOCK; + if(a < INDPERBUF) { + addr = d->iblock; + if(!addr && tag) { + addr = balloc(dev, Tind1, qpath); + d->iblock = addr; + p->flags |= Bmod|Bimm; + } + if(putb) + putbuf(p); + addr = indfetch(p, d, addr, a, Tind1, tag); + return addr; + } + a -= INDPERBUF; + if(a < INDPERBUF2) { + addr = d->diblock; + if(!addr && tag) { + addr = balloc(dev, Tind2, qpath); + d->diblock = addr; + p->flags |= Bmod|Bimm; + } + if(putb) + putbuf(p); + addr = indfetch(p, d, addr, a/INDPERBUF, Tind2, Tind1); + addr = indfetch(p, d, addr, a%INDPERBUF, Tind1, tag); + return addr; + } + if(putb) + putbuf(p); + print("dnodebuf: trip indirect\n"); + return 0; +} + +Iobuf* +dnodebuf(Iobuf *p, Dentry *d, long a, int tag) +{ + long addr; + + addr = rel2abs(p, d, a, tag, 0); + if(addr) + return getbuf(p->dev, addr, Bread); + return 0; +} + +/* + * same as dnodebuf but it calls putpuf(p) + * to reduce interference. + */ +Iobuf* +dnodebuf1(Iobuf *p, Dentry *d, long a, int tag) +{ + long addr; + Device dev; + + dev = p->dev; + addr = rel2abs(p, d, a, tag, 1); + if(addr) + return getbuf(dev, addr, Bread); + return 0; + +} + +long +indfetch(Iobuf *p, Dentry *d, long addr, long a, int itag, int tag) +{ + Iobuf *bp; + + if(!addr) + return 0; + bp = getbuf(p->dev, addr, Bread); + if(!bp || checktag(bp, itag, d->qid.path)) { + if(!bp) { + print("ind fetch bp = 0\n"); + return 0; + } + print("ind fetch tag\n"); + putbuf(bp); + return 0; + } + addr = ((long*)bp->iobuf)[a]; + if(!addr && tag) { + addr = balloc(p->dev, tag, d->qid.path); + if(addr) { + ((long*)bp->iobuf)[a] = addr; + bp->flags |= Bmod; + if(localfs || tag == Tdir) + bp->flags |= Bimm; + settag(bp, itag, d->qid.path); + } + } + putbuf(bp); + return addr; +} + +void +dtrunc(Iobuf *p, Dentry *d) +{ + int i; + + bfree(p->dev, d->diblock, 2); + d->diblock = 0; + bfree(p->dev, d->iblock, 1); + d->iblock = 0; + for(i=NDBLOCK-1; i>=0; i--) { + bfree(p->dev, d->dblock[i], 0); + d->dblock[i] = 0; + } + d->size = 0; + p->flags |= Bmod|Bimm; + accessdir(p, d, FWRITE); +} diff --git a/sys/src/cmd/disk/kfs/devmulti.c b/sys/src/cmd/disk/kfs/devmulti.c new file mode 100755 index 000000000..b6548f997 --- /dev/null +++ b/sys/src/cmd/disk/kfs/devmulti.c @@ -0,0 +1,244 @@ +#include "all.h" + +enum{ + MAXWREN = 7, +}; + +static char WMAGIC[] = "kfs wren device\n"; +static char MMAGIC[] = "kfs multi-wren device %4d/%4d\n"; + +typedef struct Wren Wren; + +struct Wren{ + QLock; + Device dev; + ulong nblocks; + int fd; +}; + +static char *wmagic = WMAGIC; +static Wren *wrens; +static int maxwren; +char *wrenfile; +int nwren; +int badmagic; + +static Wren * +wren(Device dev) +{ + int i; + + for(i = 0; i < maxwren; i++) + if(devcmp(dev, wrens[i].dev) == 0) + return &wrens[i]; + panic("can't find wren for %D", dev); + return 0; +} + +/* + * find out the length of a file + * given the mesg version of a stat buffer + * we call this because convM2D is different + * for the file system than in the os + */ +uvlong +statlen(char *ap) +{ + uchar *p; + ulong ll, hl; + + p = (uchar*)ap; + p += 3*NAMELEN+5*4; + ll = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); + hl = p[4] | (p[5]<<8) | (p[6]<<16) | (p[7]<<24); + return ll | ((uvlong) hl << 32); +} + +static void +wrenpartinit(Device dev, int k) +{ + char buf[MAXBUFSIZE], d[DIRREC]; + char file[128], magic[64]; + Wren *w; + int fd, i, nmagic; + + if(wrens == 0) + wrens = ialloc(MAXWREN * sizeof *wrens); + w = &wrens[maxwren]; + if(nwren > 0) + sprint(file, "%s%d", wrenfile, k); + else + strcpy(file, wrenfile); + fd = open(file, ORDWR); + if(fd < 0) + panic("can't open %s", file); + if(fstat(fd, d) < 0) + panic("can't stat %s\n", file); + seek(fd, 0, 0); + i = read(fd, buf, sizeof buf); + if(i < sizeof buf) + panic("can't read %s", file); + badmagic = 0; + RBUFSIZE = 1024; + sprint(magic, wmagic, k, nwren); + nmagic = strlen(magic); + if(strncmp(buf+256, magic, nmagic) == 0){ + RBUFSIZE = atol(buf+256+nmagic); + if(RBUFSIZE % 512){ + fprint(2, "kfs: bad buffersize(%d): assuming 1k blocks\n", RBUFSIZE); + RBUFSIZE = 1024; + } + }else + badmagic = 1; + w->dev = dev; + w->nblocks = statlen(d)/RBUFSIZE; + if(k > 0) + w->nblocks -= 1; /* don't count magic */ + w->fd = fd; + maxwren++; +} + +void +wreninit(Device dev) +{ + int i; + + if(nwren > 0) + wmagic = MMAGIC; + i = 0; + do{ + wrenpartinit(dev, i); + }while(++i < nwren); +} + +static void +wrenpartream(Device dev, int k) +{ + Wren *w; + char buf[MAXBUFSIZE], magic[64]; + int fd, i; + + if(RBUFSIZE % 512) + panic("kfs: bad buffersize(%d): restart a multiple of 512\n", RBUFSIZE); + print("kfs: reaming the file system using %d byte blocks\n", RBUFSIZE); + w = wren(dev)+k; + fd = w->fd; + memset(buf, 0, sizeof buf); + sprint(magic, wmagic, k, nwren); + sprint(buf+256, "%s%d\n", magic, RBUFSIZE); + qlock(w); + i = seek(fd, 0, 0) < 0 || write(fd, buf, RBUFSIZE) != RBUFSIZE; + qunlock(w); + if(i < 0) + panic("can't ream disk"); +} + +void +wrenream(Device dev) +{ + int i; + + i = 0; + do{ + wrenpartream(dev, i); + }while(++i < nwren); +} + +static int +wrentag(char *p, int tag, long qpath) +{ + Tag *t; + + t = (Tag*)(p+BUFSIZE); + return t->tag != tag || (qpath&~QPDIR) != t->path; +} + +int +wrencheck(Device dev) +{ + char buf[MAXBUFSIZE]; + + if(badmagic) + return 1; + if(RBUFSIZE > sizeof buf) + panic("bufsize too big"); + if(wrenread(dev, wrensuper(dev), buf) || wrentag(buf, Tsuper, QPSUPER) + || wrenread(dev, wrenroot(dev), buf) || wrentag(buf, Tdir, QPROOT)) + return 1; + if(((Dentry *)buf)[0].mode & DALLOC) + return 0; + return 1; +} + +long +wrensize(Device dev) +{ + Wren *w; + int i, nb; + + w = wren(dev); + nb = 0; + i = 0; + do{ + nb += w[i].nblocks; + }while(++i < nwren); + return nb; +} + +long +wrensuper(Device dev) +{ + USED(dev); + return 1; +} + +long +wrenroot(Device dev) +{ + USED(dev); + return 2; +} + +int +wrenread(Device dev, long addr, void *b) +{ + Wren *w; + int fd, i; + + w = wren(dev); + for(i=0; i<nwren; i++){ + if(addr < w->nblocks) + break; + addr -= w->nblocks; + ++w; + } + if(i > 0) + addr++; + fd = w->fd; + qlock(w); + i = seek(fd, (vlong)addr*RBUFSIZE, 0) == -1 || read(fd, b, RBUFSIZE) != RBUFSIZE; + qunlock(w); + return i; +} + +int +wrenwrite(Device dev, long addr, void *b) +{ + Wren *w; + int fd, i; + + w = wren(dev); + for(i=0; i<nwren; i++){ + if(addr < w->nblocks) + break; + addr -= w->nblocks; + ++w; + } + if(i > 0) + addr++; + fd = w->fd; + qlock(w); + i = seek(fd, (vlong)addr*RBUFSIZE, 0) == -1 || write(fd, b, RBUFSIZE) != RBUFSIZE; + qunlock(w); + return i; +} diff --git a/sys/src/cmd/disk/kfs/devwren.c b/sys/src/cmd/disk/kfs/devwren.c new file mode 100755 index 000000000..3cb53be99 --- /dev/null +++ b/sys/src/cmd/disk/kfs/devwren.c @@ -0,0 +1,174 @@ +#include "all.h" + +enum{ + MAXWREN = 7, +}; + +#define WMAGIC "kfs wren device\n" + +typedef struct Wren Wren; + +struct Wren{ + QLock; + Device dev; + uvlong size; + int fd; +}; + +static Wren *wrens; +static int maxwren; +char *wrenfile; +int nwren; +int badmagic; + +static Wren * +wren(Device dev) +{ + int i; + + for(i = 0; i < maxwren; i++) + if(devcmp(dev, wrens[i].dev) == 0) + return &wrens[i]; + panic("can't find wren for %D", dev); + return 0; +} + +void +wreninit(Device dev) +{ + char buf[MAXBUFSIZE]; + Wren *w; + Dir *d; + int fd, i; + + if(wrens == 0) + wrens = ialloc(MAXWREN * sizeof *wrens); + w = &wrens[maxwren]; + fd = open(wrenfile, ORDWR); + if(fd < 0) + panic("can't open %s", wrenfile); + if((d = dirfstat(fd)) == nil) + panic("can't stat %s\n", wrenfile); + seek(fd, 0, 0); + i = read(fd, buf, sizeof buf); + if(i < sizeof buf) + panic("can't read %s", wrenfile); + badmagic = 0; + RBUFSIZE = 1024; + if(strncmp(buf+256, WMAGIC, strlen(WMAGIC)) == 0){ + RBUFSIZE = atol(buf+256+strlen(WMAGIC)); + if(RBUFSIZE % 512){ + fprint(2, "kfs: bad buffersize(%d): assuming 1k blocks\n", RBUFSIZE); + RBUFSIZE = 1024; + } + }else + badmagic = 1; + w->dev = dev; + w->size = d->length; + free(d); + w->fd = fd; + maxwren++; +} + +void +wrenream(Device dev) +{ + Wren *w; + char buf[MAXBUFSIZE]; + int fd, i; + + if(RBUFSIZE % 512) + panic("kfs: bad buffersize(%d): restart a multiple of 512\n", RBUFSIZE); + if(RBUFSIZE > sizeof(buf)) + panic("kfs: bad buffersize(%d): must be at most %d\n", RBUFSIZE, sizeof(buf)); + + print("kfs: reaming the file system using %d byte blocks\n", RBUFSIZE); + w = wren(dev); + fd = w->fd; + memset(buf, 0, sizeof buf); + sprint(buf+256, "%s%d\n", WMAGIC, RBUFSIZE); + qlock(w); + i = seek(fd, 0, 0) < 0 || write(fd, buf, RBUFSIZE) != RBUFSIZE; + qunlock(w); + if(i < 0) + panic("can't ream disk"); +} + +int +wrentag(char *p, int tag, long qpath) +{ + Tag *t; + + t = (Tag*)(p+BUFSIZE); + return t->tag != tag || (qpath&~QPDIR) != t->path; +} + +int +wrencheck(Device dev) +{ + char buf[MAXBUFSIZE]; + + if(badmagic) + return 1; + if(RBUFSIZE > sizeof(buf)) + panic("kfs: bad buffersize(%d): must be at most %d\n", RBUFSIZE, sizeof(buf)); + + if(wrenread(dev, wrensuper(dev), buf) || wrentag(buf, Tsuper, QPSUPER) + || wrenread(dev, wrenroot(dev), buf) || wrentag(buf, Tdir, QPROOT)) + return 1; + if(((Dentry *)buf)[0].mode & DALLOC) + return 0; + return 1; +} + +long +wrensize(Device dev) +{ + return wren(dev)->size / RBUFSIZE; +} + +long +wrensuper(Device dev) +{ + USED(dev); + return 1; +} + +long +wrenroot(Device dev) +{ + USED(dev); + return 2; +} + +int +wrenread(Device dev, long addr, void *b) +{ + Wren *w; + int fd, i; + + w = wren(dev); + fd = w->fd; + qlock(w); + i = seek(fd, (vlong)addr*RBUFSIZE, 0) == -1 || read(fd, b, RBUFSIZE) != RBUFSIZE; + qunlock(w); + if(i) + print("wrenread failed: %r\n"); + return i; +} + +int +wrenwrite(Device dev, long addr, void *b) +{ + Wren *w; + int fd, i; + + w = wren(dev); + fd = w->fd; + qlock(w); + i = seek(fd, (vlong)addr*RBUFSIZE, 0) == -1 || write(fd, b, RBUFSIZE) != RBUFSIZE; + qunlock(w); + if(i) + print("wrenwrite failed: %r\n"); + return i; +} diff --git a/sys/src/cmd/disk/kfs/errno.h b/sys/src/cmd/disk/kfs/errno.h new file mode 100755 index 000000000..3e32b1dd3 --- /dev/null +++ b/sys/src/cmd/disk/kfs/errno.h @@ -0,0 +1,35 @@ +enum{ + Enevermind, /* never mind */ + Enofd, /* no free file descriptors */ + Efidinuse, /* fid already in use */ + Ebadfd, /* fid out of range or not open */ + Ebadusefd, /* inappropriate use of fid */ + Ebadarg, /* bad arg in system call */ + Enonexist, /* file does not exist */ + Efilename, /* file name syntax */ + Ebadchar, /* bad character in file name */ + Ebadsharp, /* unknown device in # filename */ + Ebadexec, /* a.out header invalid */ + Eioload, /* i/o error in demand load */ + Eperm, /* permission denied */ + Enotdir, /* not a directory */ + Enochild, /* no living children */ + Enoseg, /* no free segments */ + Ebuf, /* buffer wrong size */ + Ebadmount, /* inconsistent mount */ + Enomount, /* mount table full */ + Enomntdev, /* no free mount devices */ + Eshutdown, /* mounted device shut down */ + Einuse, /* device or object already in use */ + Eio, /* i/o error */ + Eisdir, /* file is a directory */ + Ebaddirread, /* directory read not quantized */ + Esegaddr, /* illegal segment addresses or size */ + Enoenv, /* no free environment resources */ + Eprocdied, /* process exited */ + Enocreate, /* mounted directory forbids creation */ + Enotunion, /* attempt to union with non-mounted directory */ + Emount, /* inconsistent mount */ + Enosrv, /* no free server slots */ + Egreg, /* it's all greg's fault */ +}; diff --git a/sys/src/cmd/disk/kfs/fns.h b/sys/src/cmd/disk/kfs/fns.h new file mode 100755 index 000000000..aeba9e80e --- /dev/null +++ b/sys/src/cmd/disk/kfs/fns.h @@ -0,0 +1,42 @@ +#include "portfns.h" + +long belong(char *); +Chan* chaninit(char*); +void check(Filsys *, long); +int cmd_exec(char*); +void consserve(void); +void confinit(void); +int fsinit(int, int); +void *ialloc(ulong); +int nextelem(void); +long number(int, int); +Device scsidev(char*); +int skipbl(int); +void startproc(void (*)(void), char *); +void syncproc(void); +void syncall(void); + +int fprint(int, char*, ...); +void wreninit(Device); +int wrencheck(Device); +void wrenream(Device); +long wrensize(Device); +long wrensuper(Device); +long wrenroot(Device); +int wrenread(Device, long, void *); +int wrenwrite(Device, long, void *); + +/* + * macros for compat with bootes + */ +#define localfs 1 + +#define devgrow(d, s) 0 +#define nofree(d, a) 0 +#define isro(d) 0 + +#define superaddr(d) ((*devcall[d.type].super)(d)) +#define getraddr(d) ((*devcall[d.type].root)(d)) +#define devsize(d) ((*devcall[d.type].size)(d)) +#define devwrite(d, a, v) ((*devcall[d.type].write)(d, a, v)) +#define devread(d, a, v) ((*devcall[d.type].read)(d, a, v)) diff --git a/sys/src/cmd/disk/kfs/ialloc.c b/sys/src/cmd/disk/kfs/ialloc.c new file mode 100755 index 000000000..6ab880ae3 --- /dev/null +++ b/sys/src/cmd/disk/kfs/ialloc.c @@ -0,0 +1,9 @@ +#include "all.h" + +void *ialloc(ulong n){ + void *p; + + if(p = malloc(n)) + memset(p, 0, n); + return p; +} diff --git a/sys/src/cmd/disk/kfs/iobuf.c b/sys/src/cmd/disk/kfs/iobuf.c new file mode 100755 index 000000000..c78eebc1e --- /dev/null +++ b/sys/src/cmd/disk/kfs/iobuf.c @@ -0,0 +1,235 @@ +#include "all.h" + +#define DEBUG 0 + +long niob; +long nhiob; +Hiob *hiob; + +Iobuf* +getbuf(Device dev, long addr, int flag) +{ + Iobuf *p, *s; + Hiob *hp; + long h; + + if(DEBUG) + print("getbuf %D(%ld) f=%x\n", dev, addr, flag); + h = addr + + dev.type*1009L + + dev.ctrl*10007L + + dev.unit*100003L + + dev.part*1000003L; + if(h < 0) + h = ~h; + h %= nhiob; + hp = &hiob[h]; + +loop: + lock(hp); + +/* + * look for it in the active list + */ + s = hp->link; + for(p=s;;) { + if(p->addr == addr && !devcmp(p->dev, dev)) { + if(p != s) { + p->back->fore = p->fore; + p->fore->back = p->back; + p->fore = s; + p->back = s->back; + s->back = p; + p->back->fore = p; + hp->link = p; + } + unlock(hp); + qlock(p); + if(p->addr != addr || devcmp(p->dev, dev)) { + qunlock(p); + goto loop; + } + p->flags |= flag; + cons.bhit.count++; + p->iobuf = p->xiobuf; + return p; + } + p = p->fore; + if(p == s) + break; + } + if(flag & Bprobe) { + unlock(hp); + return 0; + } + +/* + * not found + * take oldest unlocked entry in this queue + */ +xloop: + p = s->back; + if(!canqlock(p)) { + if(p == hp->link) { + unlock(hp); + print("iobuf all locked\n"); + goto loop; + } + s = p; + goto xloop; + } + /* + * its dangerous to flush the pseudo + * devices since they recursively call + * getbuf/putbuf. deadlock! + */ + if(p->flags & Bres) { + qunlock(p); + if(p == hp->link) { + unlock(hp); + print("iobuf all resed\n"); + goto loop; + } + s = p; + goto xloop; + } + if(p->flags & Bmod) { + unlock(hp); + if(!devwrite(p->dev, p->addr, p->xiobuf)) + p->flags &= ~(Bimm|Bmod); + qunlock(p); + goto loop; + } + hp->link = p; + p->addr = addr; + p->dev = dev; + p->flags = flag; + unlock(hp); + p->iobuf = p->xiobuf; + if(flag & Bread) { + if(devread(p->dev, p->addr, p->iobuf)) { + p->flags = 0; + p->dev = devnone; + p->addr = -1; + p->iobuf = (char*)-1; + qunlock(p); + return 0; + } + cons.bread.count++; + return p; + } + cons.binit.count++; + return p; +} + +/* + * syncblock tries to put out a block per hashline + * returns 0 all done, + * returns 1 if it missed something + */ +int +syncblock(void) +{ + Iobuf *p, *s, *q; + Hiob *hp; + long h; + int flag; + + flag = 0; + for(h=0; h<nhiob; h++) { + q = 0; + hp = &hiob[h]; + lock(hp); + s = hp->link; + for(p=s;;) { + if(p->flags & Bmod) { + if(q) + flag = 1; /* more than 1 mod/line */ + q = p; + } + p = p->fore; + if(p == s) + break; + } + unlock(hp); + if(q) { + if(!canqlock(q)) { + flag = 1; /* missed -- was locked */ + continue; + } + if(!(q->flags & Bmod)) { + qunlock(q); + continue; + } + if(!devwrite(q->dev, q->addr, q->xiobuf)) + q->flags &= ~(Bmod|Bimm); + qunlock(q); + } + } + return flag; +} + +void +sync(char *reason) +{ + long i; + + print("sync: %s\n", reason); + for(i=10*nhiob; i>0; i--) + if(!syncblock()) + return; + print("sync shorted\n"); +} + +void +putbuf(Iobuf *p) +{ + if(canqlock(p)) + print("buffer not locked %D(%ld)\n", p->dev, p->addr); + if(p->flags & Bimm) { + if(!(p->flags & Bmod)) + print("imm and no mod %D(%ld)\n", p->dev, p->addr); + if(!devwrite(p->dev, p->addr, p->iobuf)) + p->flags &= ~(Bmod|Bimm); + } + p->iobuf = (char*)-1; + qunlock(p); +} + +int +checktag(Iobuf *p, int tag, long qpath) +{ + Tag *t; + + t = (Tag*)(p->iobuf+BUFSIZE); + if(t->tag != tag) { + if(1 || CHAT(0)) + print(" tag = %G; expected %G; addr = %lud\n", + t->tag, tag, p->addr); + return 2; + } + if(qpath != QPNONE) { + qpath &= ~QPDIR; + if(qpath != t->path) { + if(qpath == (t->path&~QPDIR)) /* old bug */ + return 0; + if(1 || CHAT(0)) + print(" tag/path = %lux; expected %G/%lux\n", + t->path, tag, qpath); + return 1; + } + } + return 0; +} + +void +settag(Iobuf *p, int tag, long qpath) +{ + Tag *t; + + t = (Tag*)(p->iobuf+BUFSIZE); + t->tag = tag; + if(qpath != QPNONE) + t->path = qpath & ~QPDIR; + p->flags |= Bmod; +} diff --git a/sys/src/cmd/disk/kfs/main.c b/sys/src/cmd/disk/kfs/main.c new file mode 100755 index 000000000..3ca31615b --- /dev/null +++ b/sys/src/cmd/disk/kfs/main.c @@ -0,0 +1,558 @@ +#include "all.h" + +int sfd; +int cmdmode = 0660; +int rfd; +int chat; +extern char *wrenfile; +extern int nwren; +char *myname; +int cmdfd; +int writeallow; /* never on; for compatibility with fs */ +int wstatallow; +int writegroup; +int allownone; +int noatime; +int srvfd(char*, int, int); +void usage(void); +void confinit(void); +Chan *chaninit(char*); +void consinit(void); +void forkserve(void); + +void +main(int argc, char *argv[]) +{ + Filsys *fs; + int ream, fsok; + int newbufsize, nocheck; + char buf[NAMELEN]; + int pid, ctl; + + progname = "kfs"; + procname = "init"; + + /* + * insulate from invoker's environment and keep it from swapping + */ + rfork(RFNAMEG|RFNOTEG|RFREND); + + confinit(); + sfd = -1; + ream = 0; + newbufsize = 0; + nocheck = 0; + wrenfile = "/dev/sdC0/fs"; + + pid = getpid(); + snprint(buf, sizeof buf, "/proc/%d/ctl", pid); + ctl = open(buf, OWRITE); + fprint(ctl, "noswap\n"); + close(ctl); + + buf[0] = '\0'; + + ARGBEGIN{ + case 'b': + newbufsize = atol(ARGF()); + break; + case 'c': + nocheck = 1; + break; + case 'f': + wrenfile = ARGF(); + break; + case 'm': + nwren = atol(ARGF()); + break; + case 'n': + strncpy(buf, ARGF(), NAMELEN-1); + buf[NAMELEN-1] = '\0'; + break; + case 'p': + cmdmode = atol(ARGF()); + break; + case 'r': + ream = 1; + break; + case 's': + sfd = 0; + rfd = dup(1, -1); + close(1); + if(open("/dev/cons", OWRITE) < 0) + open("#c/cons", OWRITE); + break; + case 'B': + conf.niobuf = strtoul(ARGF(), 0, 0); + break; + case 'C': + chat = 1; + break; + default: + usage(); + }ARGEND + + if(argc != 0) + usage(); + + cmdfd = 2; + + if (access(wrenfile, AREAD|AWRITE) == -1) + sysfatal("%s cannot access device", wrenfile); + + formatinit(); + sublockinit(); + + if(buf[0]) + sprint(service, "kfs.%s", buf); + else + strcpy(service, "kfs"); + chan = chaninit(service); + consinit(); + tlocks = ialloc(NTLOCK * sizeof *tlocks); + uid = ialloc(conf.nuid * sizeof(*uid)); + uidspace = ialloc(conf.uidspace * sizeof(*uidspace)); + gidspace = ialloc(conf.gidspace * sizeof(*gidspace)); + + /* + * init global locks + */ + wlock(&mainlock); wunlock(&mainlock); + + /* + * init the file system, ream it if needed, and get the block sizes + */ + ream = fsinit(ream, newbufsize); + iobufinit(); + for(fs=filesys; fs->name; fs++) + if(fs->flags & FREAM){ /* set by fsinit if reamed */ + ream++; + rootream(fs->dev, getraddr(fs->dev)); + superream(fs->dev, superaddr(fs->dev)); + } + + boottime = time(nil); + + consserve(); + fsok = superok(filesys[0].dev, superaddr(filesys[0].dev), 0); + if(!nocheck && !ream && !fsok) + cmd_exec("check fq"); + + startproc(forkserve, "srv"); + startproc(syncproc, "sync"); + + exits(0); +} + +void +forkserve(void) +{ + serve(chan); +} + +static +struct +{ + int nfilter; + Filter* filters[100]; +}f; + +int alarmed; + +void +catchalarm(void *regs, char *msg) +{ + USED(regs, msg); + if(strcmp(msg, "alarm") == 0){ + alarmed = 1; + noted(NCONT); + } else + noted(NDFLT); +} + +/* + * process to synch blocks + * it puts out a block/line every second + * it waits 10 seconds if catches up. + * in both cases, it takes about 10 seconds + * to get up-to-date. + * + * it also updates the filter stats + * and executes commands + */ +void +syncproc(void) +{ + char buf[4*1024]; + Filter *ft; + ulong c0, c1; + long t, n, d; + int i, p[2]; + + /* + * make a pipe for commands + */ + if(pipe(p) < 0) + panic("command pipe"); + sprint(buf, "#s/%s.cmd", service); + srvfd(buf, cmdmode, p[0]); + close(p[0]); + cmdfd = p[1]; + notify(catchalarm); + + t = time(nil); + for(;;){ + i = syncblock(); + alarmed = 0; + alarm(i ? 1000: 10000); + n = read(cmdfd, buf, sizeof buf - 1); + if(n <= 0 && !alarmed) + sleep(i ? 1000: 10000); + alarm(0); + if(n > 0){ + buf[n] = '\0'; + if(cmd_exec(buf)) + fprint(cmdfd, "done"); + else + fprint(cmdfd, "unknown command"); + } + n = time(nil); + d = n - t; + if(d < 0 || d > 5*60) + d = 0; + while(d >= 1) { + d -= 1; + for(i=0; i<f.nfilter; i++) { + ft = f.filters[i]; + c0 = ft->count; + c1 = c0 - ft->oldcount; + ft->oldcount = c0; + ft->filter[0] = famd(ft->filter[0], c1, 59, 60); + ft->filter[1] = famd(ft->filter[1], c1, 599, 600); + ft->filter[2] = famd(ft->filter[2], c1, 5999, 6000); + } + } + t = n; + } +} + +void +dofilter(Filter *ft) +{ + int i; + + i = f.nfilter; + if(i >= sizeof f.filters / sizeof f.filters[0]) { + print("dofilter: too many filters\n"); + return; + } + f.filters[i] = ft; + f.nfilter = i+1; +} + +void +startproc(void (*f)(void), char *name) +{ + switch(rfork(RFMEM|RFFDG|RFPROC)){ + case -1: + panic("can't fork"); + case 0: + break; + default: + return; + } + procname = name; + f(); + _exits(nil); +} + +void +confinit(void) +{ + conf.niobuf = 0; + conf.nuid = 600; + conf.nserve = 2; + conf.uidspace = conf.nuid*6; + conf.gidspace = conf.nuid*3; + cons.flags = 0; +} + +static void +dochaninit(Chan *cp, int fd) +{ + cp->chan = fd; + fileinit(cp); + wlock(&cp->reflock); + wunlock(&cp->reflock); + lock(&cp->flock); + unlock(&cp->flock); +} + +Chan* +chaninit(char *server) +{ + Chan *cp; + char buf[3*NAMELEN]; + int p[2]; + + sprint(buf, "#s/%s", server); + if(sfd < 0){ + if(pipe(p) < 0) + panic("can't make a pipe"); + sfd = p[0]; + rfd = p[1]; + } + srvfd(buf, 0666, sfd); + close(sfd); + cp = ialloc(sizeof *cp); + cons.srvchan = cp; + dochaninit(cp, rfd); + return cp; +} + +int +netserve(char *netaddr) +{ + int afd, lfd, fd; + char adir[2*NAMELEN], ldir[2*NAMELEN]; + Chan *netchan; + + if(access("/net/tcp/clone", 0) < 0) + bind("#I", "/net", MAFTER); + if(access("/net.alt/tcp/clone", 0) < 0) + bind("#I1", "/net.alt", MAFTER); + + afd = announce(netaddr, adir); + if (afd < 0) + return -1; + switch (rfork(RFMEM|RFFDG|RFPROC)) { + case -1: + return -1; + case 0: + break; + default: + return 0; + } + for (;;) { + lfd = listen(adir, ldir); + if (lfd < 0) + continue; + fd = accept(lfd, ldir); + if (fd < 0) { + close(lfd); + continue; + } + netchan = mallocz(sizeof(Chan), 1); + if(netchan == nil) + panic("out of memory"); + dochaninit(netchan, fd); + switch (rfork(RFMEM|RFFDG|RFPROC)) { + case -1: + panic("can't fork"); + case 0: + close(afd); + close(lfd); + serve(netchan); + free(netchan); + exits(0); + default: + close(fd); + close(lfd); + continue; + } + } +} + +int +srvfd(char *s, int mode, int sfd) +{ + int fd; + char buf[32]; + + fd = create(s, ORCLOSE|OWRITE, mode); + if(fd < 0){ + remove(s); + fd = create(s, ORCLOSE|OWRITE, mode); + if(fd < 0) + panic(s); + } + sprint(buf, "%d", sfd); + if(write(fd, buf, strlen(buf)) != strlen(buf)) + panic("srv write"); + return sfd; +} + +void +consinit(void) +{ + int i; + + cons.chan = ialloc(sizeof(Chan)); + wlock(&cons.chan->reflock); + wunlock(&cons.chan->reflock); + lock(&cons.chan->flock); + unlock(&cons.chan->flock); + dofilter(&cons.work); + dofilter(&cons.rate); + dofilter(&cons.bhit); + dofilter(&cons.bread); + dofilter(&cons.binit); + for(i = 0; i < MAXTAG; i++) + dofilter(&cons.tags[i]); +} + +/* + * always called with mainlock locked + */ +void +syncall(void) +{ + for(;;) + if(!syncblock()) + return; +} + +int +askream(Filsys *fs) +{ + char c; + + print("File system %s inconsistent\n", fs->name); + print("Would you like to ream it (y/n)? "); + read(0, &c, 1); + return c == 'y'; +} + +ulong +memsize(void) +{ + char *p, buf[128]; + int fd, n, by2pg, secs; + + by2pg = 4*1024; + p = getenv("cputype"); + if(p && strcmp(p, "68020") == 0) + by2pg = 8*1024; + + secs = 4*1024*1024; + + fd = open("/dev/swap", OREAD); + if(fd < 0) + return secs; + n = read(fd, buf, sizeof(buf)-1); + close(fd); + if(n <= 0) + return secs; + buf[n] = 0; + p = strchr(buf, '/'); + if(p) + secs = strtoul(p+1, 0, 0)*by2pg; + return secs; +} + +/* + * init the devices + * wipe some of the file systems, or all if ream is set + * this code really assumes that only one file system exists + */ +int +fsinit(int ream, int newbufsize) +{ + Filsys *fs; + + RBUFSIZE = 4 * 1024; + for(fs=filesys; fs->name; fs++) + (*devcall[fs->dev.type].init)(fs->dev); + if(newbufsize == 0) + newbufsize = RBUFSIZE; + + if(conf.niobuf == 0) { + conf.niobuf = memsize()/10; + if(conf.niobuf > 2*1024*1024) + conf.niobuf = 2*1024*1024; + conf.niobuf /= newbufsize; + if(conf.niobuf < 30) + conf.niobuf = 30; + } + + BUFSIZE = RBUFSIZE - sizeof(Tag); + + for(fs=filesys; fs->name; fs++) + if(ream || (*devcall[fs->dev.type].check)(fs->dev) && askream(fs)){ + RBUFSIZE = newbufsize; + BUFSIZE = RBUFSIZE - sizeof(Tag); + (*devcall[fs->dev.type].ream)(fs->dev); + fs->flags |= FREAM; + ream = 1; + } + + /* + * set up the block size dependant variables + */ + BUFSIZE = RBUFSIZE - sizeof(Tag); + DIRPERBUF = BUFSIZE / sizeof(Dentry); + INDPERBUF = BUFSIZE / sizeof(long); + INDPERBUF2 = INDPERBUF * INDPERBUF; + FEPERBUF = (BUFSIZE - sizeof(Super1) - sizeof(long)) / sizeof(long); + return ream; +} + +/* + * allocate rest of mem + * for io buffers. + */ +#define HWIDTH 5 /* buffers per hash */ +void +iobufinit(void) +{ + long i; + Iobuf *p, *q; + Hiob *hp; + + i = conf.niobuf*RBUFSIZE; + niob = i / (sizeof(Iobuf) + RBUFSIZE + sizeof(Hiob)/HWIDTH); + nhiob = niob / HWIDTH; + while(!prime(nhiob)) + nhiob++; + if(chat) + print(" %ld buffers; %ld hashes\n", niob, nhiob); + hiob = ialloc(nhiob * sizeof(Hiob)); + hp = hiob; + for(i=0; i<nhiob; i++) { + lock(hp); + unlock(hp); + hp++; + } + p = ialloc(niob * sizeof(Iobuf)); + hp = hiob; + for(i=0; i<niob; i++) { + qlock(p); + qunlock(p); + if(hp == hiob) + hp = hiob + nhiob; + hp--; + q = hp->link; + if(q) { + p->fore = q; + p->back = q->back; + q->back = p; + p->back->fore = p; + } else { + hp->link = p; + p->fore = p; + p->back = p; + } + p->dev = devnone; + p->addr = -1; + p->xiobuf = ialloc(RBUFSIZE); + p->iobuf = (char*)-1; + p++; + } +} + +void +usage(void) +{ + fprint(2, "usage: kfs [-cCr] [-b bufsize] [-s infd outfd] [-f fsfile]\n"); + exits(0); +} diff --git a/sys/src/cmd/disk/kfs/misc.c b/sys/src/cmd/disk/kfs/misc.c new file mode 100755 index 000000000..75619d5f4 --- /dev/null +++ b/sys/src/cmd/disk/kfs/misc.c @@ -0,0 +1,86 @@ +#include "all.h" + +extern int cmdfd; + +Float +famd(Float a, int b, int c, int d) +{ + ulong x, m; + + x = (a + b) * c; + m = x % d; + x /= d; + if(m >= d / 2) + x++; + return x; +} + +ulong +fdf(Float a, int d) +{ + ulong x, m; + + m = a % d; + x = a / d; + if(m >= d / 2) + x++; + return x; +} + +long +belong(char *s) +{ + uchar *x; + + x = (uchar *)s; + return (x[0] << 24) + (x[1] << 16) + (x[2] << 8) + x[3]; +} + +void +panic(char *fmt, ...) +{ + char buf[8192], *s; + va_list arg; + + + s = buf; + s += sprint(s, "%s %s %d: ", progname, procname, getpid()); + va_start(arg, fmt); + s = vseprint(s, buf + sizeof(buf) / sizeof(*buf), fmt, arg); + va_end(arg); + *s++ = '\n'; + write(2, buf, s - buf); +abort(); + exits(buf); +} + +#define SIZE 4096 + +void +cprint(char *fmt, ...) +{ + char buf[SIZE], *out; + va_list arg; + + va_start(arg, fmt); + out = vseprint(buf, buf+SIZE, fmt, arg); + va_end(arg); + write(cmdfd, buf, (long)(out-buf)); +} + +/* + * print goes to fd 2 [sic] because fd 1 might be + * otherwise preoccupied when the -s flag is given to kfs. + */ +int +print(char *fmt, ...) +{ + va_list arg; + int n; + + va_start(arg, fmt); + n = vfprint(2, fmt, arg); + va_end(arg); + return n; +} + diff --git a/sys/src/cmd/disk/kfs/mkfile b/sys/src/cmd/disk/kfs/mkfile new file mode 100755 index 000000000..93e58ac2b --- /dev/null +++ b/sys/src/cmd/disk/kfs/mkfile @@ -0,0 +1,54 @@ +</$objtype/mkfile + +TARG=kfs + +OFILES=\ + 9p1.$O\ + 9p1lib.$O\ + 9p2.$O\ + 9p12.$O\ + auth.$O\ + chk.$O\ + con.$O\ + console.$O\ + dat.$O\ + dentry.$O\ + ialloc.$O\ + iobuf.$O\ + main.$O\ + misc.$O\ + porttime.$O\ + sub.$O\ + uid.$O\ + ofcallfmt.$O\ + +HFILES=\ + all.h\ + dat.h\ + errno.h\ + fns.h\ + portfns.h\ + portdat.h\ + +BIN=/$objtype/bin/disk + +UPDATE=mkfile\ + $HFILES\ + ${OFILES:%.$O=%.c}\ + +</sys/src/cmd/mkone + +$O.out: devwren.$O + +$O.gfs: $OFILES devmulti.$O + $LD $LDFLAGS -o $target $prereq + + +test:VQ: + echo rm -fr /srv/il!$sysname!11111 + echo 'kill 8.out|rc' + echo rm /srv/il!$sysname!11111 + echo 8.out -Crf /tmp/disk + echo disk/kfscmd ''''listen il!*!11111'''' + echo srv il!$sysname!11111 + echo mount /srv/il!$sysname!11111 /n/emelie diff --git a/sys/src/cmd/disk/kfs/ofcallfmt.c b/sys/src/cmd/disk/kfs/ofcallfmt.c new file mode 100755 index 000000000..309d70d91 --- /dev/null +++ b/sys/src/cmd/disk/kfs/ofcallfmt.c @@ -0,0 +1,181 @@ +#include "all.h" +#include "9p1.h" + +static void dumpsome(char*, char*, long); +static void fdirconv(char*, Dentry*); + +int +ofcallfmt(Fmt *f1) +{ + char buf[512]; + Oldfcall *f; + int fid, type, tag, n; + Dentry d; + + f = va_arg(f1->args, Oldfcall*); + type = f->type; + fid = f->fid; + tag = f->tag; + switch(type){ + case Tnop9p1: /* 50 */ + sprint(buf, "Tnop9p1 tag %ud", tag); + break; + case Rnop9p1: + sprint(buf, "Rnop9p1 tag %ud", tag); + break; + case Tsession9p1: /* 52 */ + sprint(buf, "Tsession9p1 tag %ud", tag); + break; + case Rsession9p1: + sprint(buf, "Rsession9p1 tag %ud", tag); + break; + case Rerror9p1: /* 55 */ + sprint(buf, "Rerror9p1 tag %ud error %.64s", tag, f->ename); + break; + case Tflush9p1: /* 56 */ + sprint(buf, "Tflush9p1 tag %ud oldtag %d", tag, f->oldtag); + break; + case Rflush9p1: + sprint(buf, "Rflush9p1 tag %ud", tag); + break; + case Tattach9p1: /* 58 */ + sprint(buf, "Tattach9p1 tag %ud fid %d uname %.28s aname %.28s auth %.28s", + tag, f->fid, f->uname, f->aname, f->auth); + break; + case Rattach9p1: + sprint(buf, "Rattach9p1 tag %ud fid %d qid 0x%lux|0x%lux", + tag, fid, f->qid.path, f->qid.version); + break; + case Tclone9p1: /* 60 */ + sprint(buf, "Tclone9p1 tag %ud fid %d newfid %d", tag, fid, f->newfid); + break; + case Rclone9p1: + sprint(buf, "Rclone9p1 tag %ud fid %d", tag, fid); + break; + case Twalk9p1: /* 62 */ + sprint(buf, "Twalk9p1 tag %ud fid %d name %.28s", tag, fid, f->name); + break; + case Rwalk9p1: + sprint(buf, "Rwalk9p1 tag %ud fid %d qid 0x%lux|0x%lux", + tag, fid, f->qid.path, f->qid.version); + break; + case Topen9p1: /* 64 */ + sprint(buf, "Topen9p1 tag %ud fid %d mode %d", tag, fid, f->mode); + break; + case Ropen9p1: + sprint(buf, "Ropen9p1 tag %ud fid %d qid 0x%lux|0x%lux", + tag, fid, f->qid.path, f->qid.version); + break; + case Tcreate9p1: /* 66 */ + sprint(buf, "Tcreate9p1 tag %ud fid %d name %.28s perm 0x%lux mode %d", + tag, fid, f->name, f->perm, f->mode); + break; + case Rcreate9p1: + sprint(buf, "Rcreate9p1 tag %ud fid %d qid 0x%lux|0x%lux", + tag, fid, f->qid.path, f->qid.version); + break; + case Tread9p1: /* 68 */ + sprint(buf, "Tread9p1 tag %ud fid %d offset %ld count %ld", + tag, fid, f->offset, f->count); + break; + case Rread9p1: + n = sprint(buf, "Rread9p1 tag %ud fid %d count %ld ", tag, fid, f->count); + dumpsome(buf+n, f->data, f->count); + break; + case Twrite9p1: /* 70 */ + n = sprint(buf, "Twrite9p1 tag %ud fid %d offset %ld count %ld ", + tag, fid, f->offset, f->count); + dumpsome(buf+n, f->data, f->count); + break; + case Rwrite9p1: + sprint(buf, "Rwrite9p1 tag %ud fid %d count %ld", tag, fid, f->count); + break; + case Tclunk9p1: /* 72 */ + sprint(buf, "Tclunk9p1 tag %ud fid %d", tag, fid); + break; + case Rclunk9p1: + sprint(buf, "Rclunk9p1 tag %ud fid %d", tag, fid); + break; + case Tremove9p1: /* 74 */ + sprint(buf, "Tremove9p1 tag %ud fid %d", tag, fid); + break; + case Rremove9p1: + sprint(buf, "Rremove9p1 tag %ud fid %d", tag, fid); + break; + case Tstat9p1: /* 76 */ + sprint(buf, "Tstat9p1 tag %ud fid %d", tag, fid); + break; + case Rstat9p1: + n = sprint(buf, "Rstat9p1 tag %ud fid %d", tag, fid); + convM2D9p1(f->stat, &d); + sprint(buf+n, " stat "); + fdirconv(buf+n+6, &d); + break; + case Twstat9p1: /* 78 */ + convM2D9p1(f->stat, &d); + n = sprint(buf, "Twstat9p1 tag %ud fid %d stat ", tag, fid); + fdirconv(buf+n, &d); + break; + case Rwstat9p1: + sprint(buf, "Rwstat9p1 tag %ud fid %d", tag, fid); + break; + case Tclwalk9p1: /* 81 */ + sprint(buf, "Tclwalk9p1 tag %ud fid %d newfid %d name %.28s", + tag, fid, f->newfid, f->name); + break; + case Rclwalk9p1: + sprint(buf, "Rclwalk9p1 tag %ud fid %d qid 0x%lux|0x%lux", + tag, fid, f->qid.path, f->qid.version); + break; + default: + sprint(buf, "unknown type %d", type); + } + return fmtstrcpy(f1, buf); +} + +static void +fdirconv(char *buf, Dentry *d) +{ + sprint(buf, "'%s' uid=%d gid=%d " + "q %lux|%lux m %uo " + "at %ld mt %ld l %ld ", + d->name, d->uid, d->gid, + d->qid.path, d->qid.version, d->mode, + d->atime, d->mtime, d->size); +} + +/* + * dump out count (or DUMPL, if count is bigger) bytes from + * buf to ans, as a string if they are all printable, + * else as a series of hex bytes + */ +#define DUMPL 24 + +static void +dumpsome(char *ans, char *buf, long count) +{ + int i, printable; + char *p; + + printable = 1; + if(count > DUMPL) + count = DUMPL; + for(i=0; i<count && printable; i++) + if((buf[i]<32 && buf[i] !='\n' && buf[i] !='\t') || (uchar)buf[i]>127) + printable = 0; + p = ans; + *p++ = '\''; + if(printable){ + memmove(p, buf, count); + p += count; + }else{ + for(i=0; i<count; i++){ + if(i>0 && i%4==0) + *p++ = ' '; + sprint(p, "%2.2ux", buf[i]); + p += 2; + } + } + *p++ = '\''; + *p = 0; +} diff --git a/sys/src/cmd/disk/kfs/portdat.h b/sys/src/cmd/disk/kfs/portdat.h new file mode 100755 index 000000000..43a937e6d --- /dev/null +++ b/sys/src/cmd/disk/kfs/portdat.h @@ -0,0 +1,368 @@ +/* + * fundamental constants + */ +#define NAMELEN 28 /* size of names */ +#define NDBLOCK 6 /* number of direct blocks in Dentry */ +#define MAXDAT 8192 /* max allowable data message */ +#define NTLOCK 200 /* number of active file Tlocks */ + +typedef struct Fbuf Fbuf; +typedef struct Super1 Super1; +typedef struct Superb Superb; +// typedef struct Qid Qid; +typedef struct Dentry Dentry; +typedef struct Tag Tag; + +typedef struct Device Device; +typedef struct Qid9p1 Qid9p1; +typedef struct File File; +typedef struct Filsys Filsys; +typedef struct Filta Filta; +typedef struct Filter Filter; +typedef ulong Float; +typedef struct Hiob Hiob; +typedef struct Iobuf Iobuf; +typedef struct P9call P9call; +typedef struct Tlock Tlock; +// typedef struct Tm Tm; +typedef struct Uid Uid; +typedef struct Wpath Wpath; +typedef struct AuthRpc AuthRpc; + +/* + * DONT TOUCH -- data structures stored on disk + */ +/* DONT TOUCH, this is the disk structure */ +struct Qid9p1 +{ + long path; + long version; +}; + +/* DONT TOUCH, this is the disk structure */ +struct Dentry +{ + char name[NAMELEN]; + short uid; + short gid; + ushort mode; + #define DALLOC 0x8000 + #define DDIR 0x4000 + #define DAPND 0x2000 + #define DLOCK 0x1000 + #define DREAD 0x4 + #define DWRITE 0x2 + #define DEXEC 0x1 + Qid9p1 qid; + long size; + long dblock[NDBLOCK]; + long iblock; + long diblock; + long atime; + long mtime; +}; + +/* DONT TOUCH, this is the disk structure */ +struct Tag +{ + short pad; + short tag; + long path; +}; + +/* DONT TOUCH, this is the disk structure */ +struct Super1 +{ + long fstart; + long fsize; + long tfree; + long qidgen; /* generator for unique ids */ + + long fsok; /* file system ok */ + + /* + * garbage for WWC device + */ + long roraddr; /* dump root addr */ + long last; /* last super block addr */ + long next; /* next super block addr */ +}; + +/* DONT TOUCH, this is the disk structure */ +struct Fbuf +{ + long nfree; + long free[1]; /* changes based on BUFSIZE */ +}; + +/* DONT TOUCH, this is the disk structure */ +struct Superb +{ + Super1; + Fbuf fbuf; +}; + +struct Device +{ + char type; + char ctrl; + char unit; + char part; +}; + +/* + * for load stats + */ +struct Filter +{ + ulong count; /* count and old count kept separate */ + ulong oldcount; /* so interrput can read them */ + Float filter[3]; /* filters for 1m 10m 100m */ +}; + +struct Filta +{ + Filter* f; + int scale; +}; + +/* + * array of qids that are locked + */ +struct Tlock +{ + Device dev; + long time; + long qpath; + File* file; +}; + +struct File +{ + QLock; + Qid qid; + Wpath* wpath; + Chan* cp; /* null means a free slot */ + Tlock* tlock; /* if file is locked */ + File* next; /* in cp->flist */ + File* list; /* in list of free files */ + Filsys* fs; + long addr; + long slot; + long lastra; /* read ahead address */ + short fid; + short uid; + char open; + #define FREAD 1 + #define FWRITE 2 + #define FREMOV 4 + #define FWSTAT 8 + long doffset; /* directory reading */ + ulong dvers; + long dslot; + + /* for network authentication */ + AuthRpc *rpc; + short cuid; +}; + +struct Filsys +{ + char* name; /* name of filesys */ + Device dev; /* device that filesys is on */ + int flags; + #define FREAM (1<<1) /* mkfs */ + #define FRECOVER (1<<2) /* install last dump */ +}; + +struct Hiob +{ + Iobuf* link; + Lock; +}; + +struct Iobuf +{ + QLock; + Device dev; + Iobuf *next; /* for hash */ + Iobuf *fore; /* for lru */ + Iobuf *back; /* for lru */ + char *iobuf; /* only active while locked */ + char *xiobuf; /* "real" buffer pointer */ + long addr; + int flags; +}; + +struct P9call +{ + uchar calln; + uchar rxflag; + short msize; + void (*func)(Chan*, int); +}; + +// struct Tm +// { +// /* see ctime(3) */ +// int sec; +// int min; +// int hour; +// int mday; +// int mon; +// int year; +// int wday; +// int yday; +// int isdst; +// }; + +struct Uid +{ + short uid; /* user id */ + short lead; /* leader of group */ + short offset; /* byte offset in uidspace */ +}; + +struct Wpath +{ + Wpath *up; /* pointer upwards in path */ + Wpath *list; /* link in free chain */ + long addr; /* directory entry addr of parent */ + long slot; /* directory entry slot of parent */ + short refs; /* number of files using this structure */ +}; + +#define MAXFDATA 8192 + +/* + * error codes generated from the file server + */ +enum +{ + Ebadspc = 1, + Efid, + Efidinuse, + Echar, + Eopen, + Ecount, + Ealloc, + Eqid, + Eauth, + Eauthmsg, + Eaccess, + Eentry, + Emode, + Edir1, + Edir2, + Ephase, + Eexist, + Edot, + Eempty, + Ebadu, + Enotu, + Enotg, + Ename, + Ewalk, + Eronly, + Efull, + Eoffset, + Elocked, + Ebroken, + Etoolong, + Ersc, + Eqidmode, + Econvert, + Enotm, + Enotd, + Enotl, + Enotw, + Esystem, + + MAXERR +}; + +/* + * devnone block numbers + */ +enum +{ + Cwio1 = 1, + Cwio2, + Cwxx1, + Cwxx2, + Cwxx3, + Cwxx4, + Cwdump1, + Cwdump2, + Cuidbuf, +}; + +/* + * tags on block + */ +enum +{ + Tnone = 0, + Tsuper, /* the super block */ + Tdir, /* directory contents */ + Tind1, /* points to blocks */ + Tind2, /* points to Tind1 */ + Tfile, /* file contents */ + Tfree, /* in free list */ + Tbuck, /* cache fs bucket */ + Tvirgo, /* fake worm virgin bits */ + Tcache, /* cw cache things */ + MAXTAG +}; + +/* + * flags to getbuf + */ +enum +{ + Bread = (1<<0), /* read the block if miss */ + Bprobe = (1<<1), /* return null if miss */ + Bmod = (1<<2), /* set modified bit in buffer */ + Bimm = (1<<3), /* set immediate bit in buffer */ + Bres = (1<<4), /* reserved, never renammed */ +}; + +/* + * open modes passed into P9 open/create + */ +enum +{ + MREAD = 0, + MWRITE, + MBOTH, + MEXEC, + MTRUNC = (1<<4), /* truncate on open */ + MCEXEC = (1<<5), /* close on exec (host) */ + MRCLOSE = (1<<6), /* remove on close */ +}; + +/* + * check flags + */ +enum +{ + Crdall = (1<<0), /* read all files */ + Ctag = (1<<1), /* rebuild tags */ + Cpfile = (1<<2), /* print files */ + Cpdir = (1<<3), /* print directories */ + Cfree = (1<<4), /* rebuild free list */ + Cream = (1<<6), /* clear all bad tags */ + Cbad = (1<<7), /* clear all bad blocks */ + Ctouch = (1<<8), /* touch old dir and indir */ + Cquiet = (1<<9), /* report just nasty things */ +}; + +/* + * buffer size variables + */ +extern int RBUFSIZE; +extern int BUFSIZE; +extern int DIRPERBUF; +extern int INDPERBUF; +extern int INDPERBUF2; +extern int FEPERBUF; diff --git a/sys/src/cmd/disk/kfs/portfns.h b/sys/src/cmd/disk/kfs/portfns.h new file mode 100755 index 000000000..3528072f7 --- /dev/null +++ b/sys/src/cmd/disk/kfs/portfns.h @@ -0,0 +1,104 @@ +void accessdir(Iobuf*, Dentry*, int); +void authfree(File*); +void addfree(Device, long, Superb*); +long balloc(Device, int, long); +void bfree(Device, long, int); +int byname(void*, void*); +int byuid(void*, void*); +int checkname(char*); +int checktag(Iobuf*, int, long); +void cmd_user(void); +char* cname(char*); +int con_attach(int, char*, char*); +int con_clone(int, int); +int con_create(int, char*, int, int, long, int); +int con_open(int, int); +int con_path(int, char*); +int con_read(int, char*, long, int); +int con_remove(int); +int con_stat(int, char*); +int con_swap(int, int); +int con_clri(int); +int con_session(void); +int con_walk(int, char*); +int con_write(int, char*, long, int); +int con_wstat(int, char*); +void cprint(char*, ...); +void datestr(char*, long); +void dbufread(Iobuf*, Dentry*, long); +Qid dentryqid(Dentry*); +int devcmp(Device, Device); +Iobuf* dnodebuf(Iobuf*, Dentry*, long, int); +Iobuf* dnodebuf1(Iobuf*, Dentry*, long, int); +void dofilter(Filter*); +int doremove(File *, int); +void dtrunc(Iobuf*, Dentry*); +void exit(void); +Float famd(Float, int, int, int); +int fchar(void); +ulong fdf(Float, int); +void fileinit(Chan*); +void sublockinit(void); +File* filep(Chan*, int, int); +int fname(char*); +void formatinit(void); +void freefp(File*); +void freewp(Wpath*); +Filsys* fsstr(char*); +Iobuf* getbuf(Device, long, int); +Dentry* getdir(Iobuf*, int); +long getraddr(Device); +Wpath* getwp(Wpath*); +void hexdump(void*, int); +int iaccess(File*, Dentry*, int); +long indfetch(Iobuf*, Dentry*, long, long , int, int); +int ingroup(int, int); +void iobufinit(void); +int leadgroup(int, int); +void mkchallenge(Chan*); +void mkqid(Qid*, Dentry*, int); +int mkqidcmp(Qid*, Dentry*); +void mkqid9p1(Qid9p1*, Qid*); +void mkqid9p2(Qid*, Qid9p1*, int); +int netserve(char*); +File* newfp(Chan*); +Qid newqid(Device); +void newstart(void); +Wpath* newwp(void); +int oconvD2M(Dentry*, void*); +int oconvM2D(void*, Dentry*); +int ofcallfmt(Fmt*); +void panic(char*, ...); +int prime(long); +void putbuf(Iobuf*); +void putwp(Wpath*); +long qidpathgen(Device*); +void rootream(Device, long); +void settag(Iobuf*, int, long); +void serve(Chan*); +void serve9p1(Chan*, uchar*, int); +void serve9p2(Chan*, uchar*, int); +void strrand(void*, int); +int strtouid(char*); +int strtouid1(char*); +int superok(Device, long, int); +void superream(Device, long); +void sync(char*); +int syncblock(void); +int Tfmt(Fmt*); +Tlock* tlocked(Iobuf*, Dentry*); +void uidtostr(char*,int); +void uidtostr1(char*,int); + +#pragma varargck argpos cprint 1 +#pragma varargck argpos panic 1 + +#pragma varargck type "C" Chan* +#pragma varargck type "D" Device +#pragma varargck type "A" Filta +#pragma varargck type "G" int +#pragma varargck type "T" long +#pragma varargck type "F" Fcall* + +typedef struct Oldfcall Oldfcall; /* needed for pragma */ +#pragma varargck type "O" Oldfcall* diff --git a/sys/src/cmd/disk/kfs/porttime.c b/sys/src/cmd/disk/kfs/porttime.c new file mode 100755 index 000000000..f9a29f96c --- /dev/null +++ b/sys/src/cmd/disk/kfs/porttime.c @@ -0,0 +1,243 @@ +#include "all.h" + +static int sunday(Tm *t, int d); +static int dysize(int); +static void ct_numb(char*, int); +static void klocaltime(long tim, Tm *ct); +static void kgmtime(long tim, Tm *ct); + +static char dmsize[12] = +{ + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * The following table is used for 1974 and 1975 and + * gives the day number of the first day after the Sunday of the + * change. + */ +static struct +{ + short yrfrom; + short yrto; + short daylb; + short dayle; +} daytab[] = +{ + 87, 999, 97, 303, + 76, 86, 119, 303, + 75, 75, 58, 303, + 74, 74, 5, 333, + 0, 73, 119, 303, +}; + +static struct +{ + short minuteswest; /* minutes west of Greenwich */ + short dsttime; /* dst correction */ +} timezone = +{ + 5*60, 1 +}; + +static void +klocaltime(long tim, Tm *ct) +{ + int daylbegin, daylend, dayno, i; + long copyt; + + copyt = tim - timezone.minuteswest*60L; + kgmtime(copyt, ct); + dayno = ct->yday; + for(i=0;; i++) + if(ct->year >= daytab[i].yrfrom && + ct->year <= daytab[i].yrto) { + daylbegin = sunday(ct, daytab[i].daylb); + daylend = sunday(ct, daytab[i].dayle); + break; + } + if(timezone.dsttime && + (dayno>daylbegin || (dayno==daylbegin && ct->hour>=2)) && + (dayno<daylend || (dayno==daylend && ct->hour<1))) { + copyt += 60L*60L; + kgmtime(copyt, ct); + } +} + +/* + * The argument is a 0-origin day number. + * The value is the day number of the last + * Sunday before or after the day. + */ +static +sunday(Tm *t, int d) +{ + if(d >= 58) + d += dysize(t->year) - 365; + return d - (d - t->yday + t->wday + 700) % 7; +} + +static void +kgmtime(long tim, Tm *ct) +{ + int d0, d1; + long hms, day; + + /* + * break initial number into days + */ + hms = tim % 86400L; + day = tim / 86400L; + if(hms < 0) { + hms += 86400L; + day -= 1; + } + + /* + * generate hours:minutes:seconds + */ + ct->sec = hms % 60; + d1 = hms / 60; + ct->min = d1 % 60; + d1 /= 60; + ct->hour = d1; + + /* + * day is the day number. + * generate day of the week. + * The addend is 4 mod 7 (1/1/1970 was Thursday) + */ + + ct->wday = (day + 7340036L) % 7; + + /* + * year number + */ + if(day >= 0) + for(d1 = 70; day >= dysize(d1); d1++) + day -= dysize(d1); + else + for (d1 = 70; day < 0; d1--) + day += dysize(d1-1); + ct->year = d1; + ct->yday = d0 = day; + + /* + * generate month + */ + + if(dysize(d1) == 366) + dmsize[1] = 29; + for(d1 = 0; d0 >= dmsize[d1]; d1++) + d0 -= dmsize[d1]; + dmsize[1] = 28; + ct->mday = d0 + 1; + ct->mon = d1; +} + +void +datestr(char *s, long t) +{ + Tm tm; + + klocaltime(t, &tm); + sprint(s, "%.4d%.2d%.2d", tm.year+1900, tm.mon+1, tm.mday); +} + +int +Tfmt(Fmt *f1) +{ + char s[30]; + char *cp; + long t; + Tm tm; + + t = va_arg(f1->args, long); + if(t == 0) + return fmtstrcpy(f1, "The Epoch"); + + klocaltime(t, &tm); + strcpy(s, "Day Mon 00 00:00:00 1900"); + cp = &"SunMonTueWedThuFriSat"[tm.wday*3]; + s[0] = cp[0]; + s[1] = cp[1]; + s[2] = cp[2]; + cp = &"JanFebMarAprMayJunJulAugSepOctNovDec"[tm.mon*3]; + s[4] = cp[0]; + s[5] = cp[1]; + s[6] = cp[2]; + ct_numb(s+8, tm.mday); + ct_numb(s+11, tm.hour+100); + ct_numb(s+14, tm.min+100); + ct_numb(s+17, tm.sec+100); + if(tm.year >= 100) { + s[20] = '2'; + s[21] = '0'; + } + ct_numb(s+22, tm.year+100); + + return fmtstrcpy(f1, s); +} + +static +dysize(int y) +{ + + if((y%4) == 0) + return 366; + return 365; +} + +static +void +ct_numb(char *cp, int n) +{ + + if(n >= 10) + cp[0] = (n/10)%10 + '0'; + else + cp[0] = ' '; + cp[1] = n%10 + '0'; +} + +/* + * compute the next time after t + * that has hour hr and is not on + * day in bitpattern -- + * for automatic dumps + */ +long +nextime(long t, int hr, int day) +{ + Tm tm; + int nhr; + + if(hr < 0 || hr >= 24) + hr = 5; + if((day&0x7f) == 0x7f) + day = 0; + +loop: + klocaltime(t, &tm); + t -= tm.sec; + t -= tm.min*60; + nhr = tm.hour; + do { + t += 60*60; + nhr++; + } while(nhr%24 != hr); + klocaltime(t, &tm); + if(tm.hour != hr) { + t += 60*60; + klocaltime(t, &tm); + if(tm.hour != hr) { + t -= 60*60; + klocaltime(t, &tm); + } + } + if(day & (1<<tm.wday)) { + t += 12*60*60; + goto loop; + } + return t; +} diff --git a/sys/src/cmd/disk/kfs/print.c b/sys/src/cmd/disk/kfs/print.c new file mode 100755 index 000000000..339cacda2 --- /dev/null +++ b/sys/src/cmd/disk/kfs/print.c @@ -0,0 +1,288 @@ +#include "all.h" + +#define PTR sizeof(char*) +#define SHORT sizeof(int) +#define INT sizeof(int) +#define LONG sizeof(long) +#define IDIGIT 30 +#define MAXCON 30 + +static int convcount = { 10 }; + +#define PUT(o, c) if((o)->p < (o)->ep) *(o)->p++ = c + +static int noconv(Op*); +static int cconv(Op*); +static int dconv(Op*); +static int hconv(Op*); +static int lconv(Op*); +static int oconv(Op*); +static int sconv(Op*); +static int uconv(Op*); +static int xconv(Op*); +static int percent(Op*); + +static +int (*fmtconv[MAXCON])(Op*) = +{ + noconv, + cconv, dconv, hconv, lconv, + oconv, sconv, uconv, xconv, + percent, +}; +static +char fmtindex[128] = +{ + ['c'] 1, + ['d'] 2, + ['h'] 3, + ['l'] 4, + ['o'] 5, + ['s'] 6, + ['u'] 7, + ['x'] 8, + ['%'] 9, +}; + +int +fmtinstall(char c, int (*f)(Op*)) +{ + + c &= 0177; + if(fmtindex[c] == 0) { + if(convcount >= MAXCON) + return 1; + fmtindex[c] = convcount++; + } + fmtconv[fmtindex[c]] = f; + return 0; +} + +char* +doprint(char *p, char *ep, char *fmt, void *argp) +{ + int sf1, c; + Op o; + + o.p = p; + o.ep = ep; + o.argp = argp; + +loop: + c = *fmt++; + if(c != '%') { + if(c == 0) { + if(o.p < o.ep) + *o.p = 0; + return o.p; + } + PUT(&o, c); + goto loop; + } + o.f1 = 0; + o.f2 = -1; + o.f3 = 0; + c = *fmt++; + sf1 = 0; + if(c == '-') { + sf1 = 1; + c = *fmt++; + } + while(c >= '0' && c <= '9') { + o.f1 = o.f1*10 + c-'0'; + c = *fmt++; + } + if(sf1) + o.f1 = -o.f1; + if(c != '.') + goto l1; + c = *fmt++; + while(c >= '0' && c <= '9') { + if(o.f2 < 0) + o.f2 = 0; + o.f2 = o.f2*10 + c-'0'; + c = *fmt++; + } +l1: + if(c == 0) + fmt--; + c = (*fmtconv[fmtindex[c&0177]])(&o); + if(c < 0) { + o.f3 |= -c; + c = *fmt++; + goto l1; + } + o.argp = (char*)o.argp + c; + goto loop; +} + +int +numbconv(Op *op, int base) +{ + char b[IDIGIT]; + int i, f, n, r; + long v; + short h; + + f = 0; + switch(op->f3 & (FLONG|FSHORT|FUNSIGN)) { + case FLONG: + v = *(long*)op->argp; + r = LONG; + break; + + case FUNSIGN|FLONG: + v = *(ulong*)op->argp; + r = LONG; + break; + + case FSHORT: + h = *(int*)op->argp; + v = h; + r = SHORT; + break; + + case FUNSIGN|FSHORT: + h = *(int*)op->argp; + v = (ushort)h; + r = SHORT; + break; + + default: + v = *(int*)op->argp; + r = INT; + break; + + case FUNSIGN: + v = *(unsigned*)op->argp; + r = INT; + break; + } + if(!(op->f3 & FUNSIGN) && v < 0) { + v = -v; + f = 1; + } + b[IDIGIT-1] = 0; + for(i = IDIGIT-2;; i--) { + n = (ulong)v % base; + n += '0'; + if(n > '9') + n += 'a' - ('9'+1); + b[i] = n; + if(i < 2) + break; + v = (ulong)v / base; + if(op->f2 >= 0 && i >= IDIGIT-op->f2) + continue; + if(v <= 0) + break; + } +sout: + if(f) + b[--i] = '-'; + strconv(b+i, op, op->f1, -1); + return r; +} + +void +strconv(char *o, Op *op, int f1, int f2) +{ + int n, c; + char *p; + + n = strlen(o); + if(f1 >= 0) + while(n < f1) { + PUT(op, ' '); + n++; + } + for(p=o; c = *p++;) + if(f2 != 0) { + PUT(op, c); + f2--; + } + if(f1 < 0) { + f1 = -f1; + while(n < f1) { + PUT(op, ' '); + n++; + } + } +} + +static int +noconv(Op *op) +{ + + strconv("***", op, 0, -1); + return 0; +} + +static int +cconv(Op *op) +{ + char b[2]; + + b[0] = *(int*)op->argp; + b[1] = 0; + strconv(b, op, op->f1, -1); + return INT; +} + +static int +dconv(Op *op) +{ + + return numbconv(op, 10); +} + +static int +hconv(Op *op) +{ + USED(op); + return -FSHORT; +} + +static int +lconv(Op *op) +{ + USED(op); + return -FLONG; +} + +static int +oconv(Op *op) +{ + USED(op); + return numbconv(op, 8); +} + +static int +sconv(Op *op) +{ + + strconv(*(char**)op->argp, op, op->f1, op->f2); + return PTR; +} + +static int +uconv(Op *op) +{ + USED(op); + return -FUNSIGN; +} + +static int +xconv(Op *op) +{ + + return numbconv(op, 16); +} + +static int +percent(Op *op) +{ + + PUT(op, '%'); + return 0; +} diff --git a/sys/src/cmd/disk/kfs/sub.c b/sys/src/cmd/disk/kfs/sub.c new file mode 100755 index 000000000..09485aa46 --- /dev/null +++ b/sys/src/cmd/disk/kfs/sub.c @@ -0,0 +1,690 @@ +#include "all.h" + +Lock wpathlock; + +struct { + Lock flock; + File* ffree; /* free file structures */ + Wpath* wfree; +} suballoc; + +enum{ + Finc= 128, /* allocation chunksize for files */ + Fmax= 10000, /* maximum file structures to be allocated */ + + Winc= 8*128, /* allocation chunksize for wpath */ + Wmax= 8*10000, /* maximum wpath structures to be allocated */ +}; + + +Filsys* +fsstr(char *p) +{ + Filsys *fs; + + for(fs=filesys; fs->name; fs++) + if(strcmp(fs->name, p) == 0) + return fs; + return 0; +} + +void +fileinit(Chan *cp) +{ + File *f; + Tlock *t; + +loop: + lock(&cp->flock); + f = cp->flist; + if(!f) { + unlock(&cp->flock); + return; + } + cp->flist = f->next; + unlock(&cp->flock); + + qlock(f); + if(t = f->tlock) { + t->time = 0; + f->tlock = 0; + } + if(f->open & FREMOV) + doremove(f, 0); + freewp(f->wpath); + f->open = 0; + f->cp = 0; + qunlock(f); + + goto loop; +} + +/* + * returns a locked file structure + */ +File* +filep(Chan *cp, int fid, int flag) +{ + File *f, *prev; + + if(fid == NOF) + return 0; + +loop: + lock(&cp->flock); + for(prev=0,f=cp->flist; f; prev=f,f=f->next) { + if(f->fid != fid) + continue; + if(prev) { + prev->next = f->next; + f->next = cp->flist; + cp->flist = f; + } + goto out; + } + if(flag) { + f = newfp(cp); + if(f) { + f->fid = fid; + goto out; + } + } +else print("cannot find %p.%d (list=%p)\n", cp, fid, cp->flist); + unlock(&cp->flock); + return 0; + +out: + unlock(&cp->flock); + qlock(f); + if(f->fid != fid) { + qunlock(f); + goto loop; + } + return f; +} + +void +sublockinit(void) +{ + lock(&suballoc.flock); + lock(&wpathlock); + conf.nfile = 0; + conf.nwpath = 0; + unlock(&suballoc.flock); + unlock(&wpathlock); +} + +/* + * always called with cp->flock locked + */ +File* +newfp(Chan *cp) +{ + File *f, *e; + +retry: + lock(&suballoc.flock); + f = suballoc.ffree; + if(f != nil){ + suballoc.ffree = f->list; + unlock(&suballoc.flock); + f->list = 0; + f->cp = cp; + f->next = cp->flist; + f->wpath = 0; + f->tlock = 0; + f->dslot = 0; + f->doffset = 0; + f->uid = 0; + f->cuid = 0; + cp->flist = f; + return f; + } + unlock(&suballoc.flock); + + if(conf.nfile > Fmax){ + print("%d: out of files\n", cp->chan); + return 0; + } + + /* + * create a few new files + */ + f = malloc(Finc*sizeof(*f)); + memset(f, 0, Finc*sizeof(*f)); + lock(&suballoc.flock); + for(e = f+Finc; f < e; f++){ + qlock(f); + qunlock(f); + f->list = suballoc.ffree; + suballoc.ffree = f; + } + conf.nfile += Finc; + unlock(&suballoc.flock); + goto retry; +} + +void +freefp(File *fp) +{ + Chan *cp; + File *f, *prev; + + if(!fp || !(cp = fp->cp)) + return; + authfree(fp); + lock(&cp->flock); + for(prev=0,f=cp->flist; f; prev=f,f=f->next) { + if(f != fp) + continue; + if(prev) + prev->next = f->next; + else + cp->flist = f->next; + f->cp = 0; + lock(&suballoc.flock); + f->list = suballoc.ffree; + suballoc.ffree = f; + unlock(&suballoc.flock); + break; + } + unlock(&cp->flock); +} + +Wpath* +newwp(void) +{ + Wpath *w, *e; + +retry: + lock(&wpathlock); + w = suballoc.wfree; + if(w != nil){ + suballoc.wfree = w->list; + unlock(&wpathlock); + memset(w, 0, sizeof(*w)); + w->refs = 1; + w->up = 0; + return w; + } + unlock(&wpathlock); + + if(conf.nwpath > Wmax){ + print("out of wpaths\n"); + return 0; + } + + /* + * create a few new wpaths + */ + w = malloc(Winc*sizeof(*w)); + memset(w, 0, Winc*sizeof(*w)); + lock(&wpathlock); + for(e = w+Winc; w < e; w++){ + w->list = suballoc.wfree; + suballoc.wfree = w; + } + conf.nwpath += Winc; + unlock(&wpathlock); + goto retry; +} + +/* + * increment the references for the whole path + */ +Wpath* +getwp(Wpath *w) +{ + Wpath *nw; + + lock(&wpathlock); + for(nw = w; nw; nw=nw->up) + nw->refs++; + unlock(&wpathlock); + return w; +} + +/* + * decrement the reference for each element of the path + */ +void +freewp(Wpath *w) +{ + lock(&wpathlock); + for(; w; w=w->up){ + w->refs--; + if(w->refs == 0){ + w->list = suballoc.wfree; + suballoc.wfree = w; + } + } + unlock(&wpathlock); +} + +/* + * decrement the reference for just this element + */ +void +putwp(Wpath *w) +{ + lock(&wpathlock); + w->refs--; + if(w->refs == 0){ + w->list = suballoc.wfree; + suballoc.wfree = w; + } + unlock(&wpathlock); +} + +int +iaccess(File *f, Dentry *d, int m) +{ + if(wstatallow) + return 0; + + /* + * owner is next + */ + if(f->uid == d->uid) + if((m<<6) & d->mode) + return 0; + /* + * group membership is hard + */ + if(ingroup(f->uid, d->gid)) + if((m<<3) & d->mode) + return 0; + /* + * other access for everyone except members of group 9999 + */ + if(m & d->mode){ + /* + * walk directories regardless. + * otherwise its impossible to get + * from the root to noworld's directories. + */ + if((d->mode & DDIR) && (m == DEXEC)) + return 0; + if(!ingroup(f->uid, 9999)) + return 0; + } + return 1; +} + +Tlock* +tlocked(Iobuf *p, Dentry *d) +{ + Tlock *t, *t1; + long qpath, tim; + Device dev; + + tim = time(0); + qpath = d->qid.path; + dev = p->dev; + t1 = 0; + for(t=tlocks+NTLOCK-1; t>=tlocks; t--) { + if(t->qpath == qpath) + if(t->time >= tim) + if(devcmp(t->dev, dev) == 0) + return 0; /* its locked */ + if(!t1 && t->time < tim) + t1 = t; /* steal first lock */ + } + if(t1) { + t1->dev = dev; + t1->qpath = qpath; + t1->time = tim + TLOCK; + } + /* botch + * out of tlock nodes simulates + * a locked file + */ + return t1; +} + +Qid +newqid(Device dev) +{ + Iobuf *p; + Superb *sb; + Qid qid; + + p = getbuf(dev, superaddr(dev), Bread|Bmod); + if(!p || checktag(p, Tsuper, QPSUPER)) + panic("newqid: super block"); + sb = (Superb*)p->iobuf; + sb->qidgen++; + qid.path = sb->qidgen; + qid.vers = 0; + qid.type = 0; + putbuf(p); + return qid; +} + +/* + * what are legal characters in a name? + * only disallow control characters. + * a) utf avoids control characters. + * b) '/' may not be the separator + */ +int +checkname(char *n) +{ + int i, c; + + for(i=0; i<NAMELEN; i++) { + c = *n & 0xff; + if(c == 0) { + if(i == 0) + return 1; + memset(n, 0, NAMELEN-i); + return 0; + } + if(c <= 040) + return 1; + n++; + } + return 1; /* too long */ +} + +void +bfree(Device dev, long addr, int d) +{ + Iobuf *p; + long a; + int i; + + if(!addr) + return; + if(d > 0) { + d--; + p = getbuf(dev, addr, Bread); + if(p) { + for(i=INDPERBUF-1; i>=0; i--) { + a = ((long*)p->iobuf)[i]; + bfree(dev, a, d); + } + putbuf(p); + } + } + /* + * stop outstanding i/o + */ + p = getbuf(dev, addr, Bprobe); + if(p) { + p->flags &= ~(Bmod|Bimm); + putbuf(p); + } + /* + * dont put written worm + * blocks into free list + */ + if(nofree(dev, addr)) + return; + p = getbuf(dev, superaddr(dev), Bread|Bmod); + if(!p || checktag(p, Tsuper, QPSUPER)) + panic("bfree: super block"); + addfree(dev, addr, (Superb*)p->iobuf); + putbuf(p); +} + +long +balloc(Device dev, int tag, long qid) +{ + Iobuf *bp, *p; + Superb *sb; + long a; + int n; + + p = getbuf(dev, superaddr(dev), Bread|Bmod); + if(!p || checktag(p, Tsuper, QPSUPER)) + panic("balloc: super block"); + sb = (Superb*)p->iobuf; + +loop: + n = --sb->fbuf.nfree; + sb->tfree--; + if(n < 0 || n >= FEPERBUF) + panic("balloc: bad freelist"); + a = sb->fbuf.free[n]; + if(n <= 0) { + if(a == 0) { + sb->tfree = 0; + sb->fbuf.nfree = 1; + if(devgrow(dev, sb)) + goto loop; + putbuf(p); + return 0; + } + bp = getbuf(dev, a, Bread); + if(!bp || checktag(bp, Tfree, QPNONE)) { + if(bp) + putbuf(bp); + putbuf(p); + return 0; + } + memmove(&sb->fbuf, bp->iobuf, (FEPERBUF+1)*sizeof(long)); + putbuf(bp); + } + bp = getbuf(dev, a, Bmod); + memset(bp->iobuf, 0, RBUFSIZE); + settag(bp, tag, qid); + if(tag == Tind1 || tag == Tind2 || tag == Tdir) + bp->flags |= Bimm; + putbuf(bp); + putbuf(p); + return a; +} + +void +addfree(Device dev, long addr, Superb *sb) +{ + int n; + Iobuf *p; + + if(addr >= sb->fsize){ + print("addfree: bad addr %lux\n", addr); + return; + } + n = sb->fbuf.nfree; + if(n < 0 || n > FEPERBUF) + panic("addfree: bad freelist"); + if(n >= FEPERBUF) { + p = getbuf(dev, addr, Bmod); + if(p == 0) + panic("addfree: getbuf"); + memmove(p->iobuf, &sb->fbuf, (FEPERBUF+1)*sizeof(long)); + settag(p, Tfree, QPNONE); + putbuf(p); + n = 0; + } + sb->fbuf.free[n++] = addr; + sb->fbuf.nfree = n; + sb->tfree++; + if(addr >= sb->fsize) + sb->fsize = addr+1; +} + +int +Cfmt(Fmt *f1) +{ + Chan *cp; + + cp = va_arg(f1->args, Chan*); + return fmtprint(f1, "C%d.%.3d", cp->type, cp->chan); +} + +int +Dfmt(Fmt *f1) +{ + Device d; + + d = va_arg(f1->args, Device); + return fmtprint(f1, "D%d.%d.%d.%d", d.type, d.ctrl, d.unit, d.part); +} + +int +Afmt(Fmt *f1) +{ + Filta a; + + a = va_arg(f1->args, Filta); + return fmtprint(f1, "%6lud %6lud %6lud", + fdf(a.f->filter[0], a.scale*60), + fdf(a.f->filter[1], a.scale*600), + fdf(a.f->filter[2], a.scale*6000)); +} + +int +Gfmt(Fmt *f1) +{ + int t; + + t = va_arg(f1->args, int); + if(t >= 0 && t < MAXTAG) + return fmtstrcpy(f1, tagnames[t]); + else + return fmtprint(f1, "<badtag %d>", t); +} + +void +formatinit(void) +{ + + fmtinstall('C', Cfmt); /* print channels */ + fmtinstall('D', Dfmt); /* print devices */ + fmtinstall('A', Afmt); /* print filters */ + fmtinstall('G', Gfmt); /* print tags */ + fmtinstall('T', Tfmt); /* print times */ + fmtinstall('O', ofcallfmt); /* print old fcalls */ +} +int +devcmp(Device d1, Device d2) +{ + + if(d1.type == d2.type) + if(d1.ctrl == d2.ctrl) + if(d1.unit == d2.unit) + if(d1.part == d2.part) + return 0; + return 1; +} + +void +rootream(Device dev, long addr) +{ + Iobuf *p; + Dentry *d; + + p = getbuf(dev, addr, Bmod|Bimm); + memset(p->iobuf, 0, RBUFSIZE); + settag(p, Tdir, QPROOT); + d = getdir(p, 0); + strcpy(d->name, "/"); + d->uid = -1; + d->gid = -1; + d->mode = DALLOC | DDIR | + ((DREAD|DWRITE|DEXEC) << 6) | + ((DREAD|DWRITE|DEXEC) << 3) | + ((DREAD|DWRITE|DEXEC) << 0); + d->qid = QID9P1(QPROOT|QPDIR,0); + d->atime = time(0); + d->mtime = d->atime; + putbuf(p); +} + +int +superok(Device dev, long addr, int set) +{ + Iobuf *p; + Superb *s; + int ok; + + p = getbuf(dev, addr, Bread|Bmod|Bimm); + s = (Superb*)p->iobuf; + ok = s->fsok; + s->fsok = set; + putbuf(p); + return ok; +} + +void +superream(Device dev, long addr) +{ + Iobuf *p; + Superb *s; + long i; + + p = getbuf(dev, addr, Bmod|Bimm); + memset(p->iobuf, 0, RBUFSIZE); + settag(p, Tsuper, QPSUPER); + + s = (Superb*)p->iobuf; + s->fstart = 1; + s->fsize = devsize(dev); + s->fbuf.nfree = 1; + s->qidgen = 10; + for(i=s->fsize-1; i>=addr+2; i--) + addfree(dev, i, s); + putbuf(p); +} + +/* + * returns 1 if n is prime + * used for adjusting lengths + * of hashing things. + * there is no need to be clever + */ +int +prime(long n) +{ + long i; + + if((n%2) == 0) + return 0; + for(i=3;; i+=2) { + if((n%i) == 0) + return 0; + if(i*i >= n) + return 1; + } +} + +void +hexdump(void *a, int n) +{ + char s1[30], s2[4]; + uchar *p; + int i; + + p = a; + s1[0] = 0; + for(i=0; i<n; i++) { + sprint(s2, " %.2ux", p[i]); + strcat(s1, s2); + if((i&7) == 7) { + print("%s\n", s1); + s1[0] = 0; + } + } + if(s1[0]) + print("%s\n", s1); +} + +long +qidpathgen(Device *dev) +{ + Iobuf *p; + Superb *sb; + long path; + + p = getbuf(*dev, superaddr((*dev)), Bread|Bmod); + if(!p || checktag(p, Tsuper, QPSUPER)) + panic("newqid: super block"); + sb = (Superb*)p->iobuf; + sb->qidgen++; + path = sb->qidgen; + putbuf(p); + return path; +} + diff --git a/sys/src/cmd/disk/kfs/uid.c b/sys/src/cmd/disk/kfs/uid.c new file mode 100755 index 000000000..bc5489a4f --- /dev/null +++ b/sys/src/cmd/disk/kfs/uid.c @@ -0,0 +1,427 @@ +#include "all.h" + +struct +{ + RWLock uidlock; + char* uidbuf; + int flen; + int find; +} uidgc; + +int +byuid(void *a1, void *a2) +{ + Uid *u1, *u2; + + u1 = a1; + u2 = a2; + return u2->uid - u1->uid; +} + +int +byname(void *a1, void *a2) +{ + Uid *u1, *u2; + + u1 = a1; + u2 = a2; + return strcmp(uidspace+u2->offset, uidspace+u1->offset); +} + +int +fchar(void) +{ + + if(uidgc.find >= uidgc.flen) { + uidgc.find = 0; + uidgc.flen = con_read(FID2, uidgc.uidbuf, cons.offset, MAXDAT); + if(uidgc.flen <= 0) + return 0; + cons.offset += uidgc.flen; + } + return uidgc.uidbuf[uidgc.find++]; +} + +int +fname(char *name) +{ + int i, c; + + memset(name, 0, NAMELEN); + for(i=0;; i++) { + c = fchar(); + switch(c) { + case ':': + case '\n': + case ',': + case ' ': + case '#': + case 0: + return c; + } + if(i < NAMELEN-1) + name[i] = c; + } +} + +#ifdef sometime +/* + * file format is + * uid:name:lead:member,member,...\n + */ +void +read_user(void) +{ + int c; + + if((c=fname(ustr))!=':' || (c=fname(name))!=':' || (c=fname(lead))!=':') + goto skipline; + n = atol(ustr); + if(n == 0) + goto skipline; + if(readu){ + o -= strlen(name)+1; + if(o < 0) { + cprint("conf.uidspace(%ld) too small\n", conf.uidspace); + return -1; + } + strcpy(uidspace+o, name); + uid[u].uid = n; + uid[u].offset = o; + u++; + if(u >= conf.nuid) { + cprint("conf.nuid(%ld) too small\n", conf.nuid); + goto initu; + } + }else{ + o = strtouid1(name); + if(o == 0 && strcmp(name, "") != 0) + o = n; + for(c=0; c<u; c++) + if(uid[c].uid == n) { + uid[c].lead = o; + break; + } + while(((c=fname(name))==',' || c=='\n') && name[0]){ +work here + if(c=='\n') + break; + } + } + +skipline: + while(c != '\n') + fname(ustr); +} +#endif + +/* + -1:adm:adm: + 0:none:adm: + 1:tor:tor: + 10000:sys:: + 10001:map:map: + 10002:doc:: + 10003:upas:upas: + 10004:font:: + 10005:bootes:bootes: +*/ + +struct { + int uid; + char *name; + int leader; +} +admusers[] = { + -1, "adm", -1, + 0, "none", -1, + 1, "tor", 1, + 2, "glenda", 2, + 10000, "sys", 0, + 10001, "upas", 10001, + 10002, "bootes", 10002, + 0, 0, 0, +}; + + +void +cmd_user(void) +{ + int c, n, o, u, g, i; + char name[NAMELEN]; + + if(con_clone(FID1, FID2)) + goto ainitu; + if(con_path(FID2, "/adm/users")) + goto ainitu; + if(con_open(FID2, 0)){ + goto ainitu; + } + + wlock(&uidgc.uidlock); + uidgc.uidbuf = malloc(MAXDAT); + + memset(uid, 0, conf.nuid * sizeof(*uid)); + memset(uidspace, 0, conf.uidspace * sizeof(*uidspace)); + memset(gidspace, 0, conf.gidspace * sizeof(*gidspace)); + + uidgc.flen = 0; + uidgc.find = 0; + cons.offset = 0; + u = 0; + o = conf.uidspace; + +ul1: + c = fname(name); + if(c != ':') + goto uskip; + n = atol(name); + if(n == 0) + goto uskip; + c = fname(name); + if(c != ':') + goto uskip; + o -= strlen(name)+1; + if(o < 0) { + cprint("conf.uidspace(%ld) too small\n", conf.uidspace); + goto initu; + } + strcpy(uidspace+o, name); + uid[u].uid = n; + uid[u].offset = o; + u++; + if(u >= conf.nuid) { + cprint("conf.nuid(%ld) too small\n", conf.nuid); + goto initu; + } + +uskip: + if(c == '\n') + goto ul1; + if(c) { + c = fname(name); + goto uskip; + } +/* cprint("%d uids read\n", u);/**/ + qsort(uid, u, sizeof(uid[0]), byuid); + for(c=u-1; c>0; c--) + if(uid[c].uid == uid[c-1].uid) { + cprint("duplicate uids: %d\n", uid[c].uid); + cprint(" %s", uidspace+uid[c].offset); + cprint(" %s\n", uidspace+uid[c-1].offset); + } + qsort(uid, u, sizeof(uid[0]), byname); + for(c=u-1; c>0; c--) + if(!strcmp(uidspace+uid[c].offset, + uidspace+uid[c-1].offset)) { + cprint("kfs: duplicate names: %s\n", uidspace+uid[c].offset); + cprint(" %d", uid[c].uid); + cprint(" %d\n", uid[c-1].uid); + } + if(cons.flags & Fuid) + for(c=0; c<u; c++) + cprint("%6d %s\n", uid[c].uid, uidspace+uid[c].offset); + + uidgc.flen = 0; + uidgc.find = 0; + cons.offset = 0; + g = 0; + +gl1: + c = fname(name); + if(c != ':') + goto gskip; + n = atol(name); /* number */ + if(n == 0) + goto gskip; + c = fname(name); /* name */ + if(c != ':') + goto gskip; + c = fname(name); /* leader */ + if(c != ':') + goto gskip; + o = strtouid1(name); + if(o == 0 && strcmp(name, "") != 0) + o = n; + for(c=0; c<u; c++) + if(uid[c].uid == n) { + uid[c].lead = o; + break; + } + c = fname(name); /* list of members */ + if(c != ',' && c != '\n') + goto gskip; + if(!name[0]) + goto gskip; + gidspace[g++] = n; +gl2: + n = strtouid1(name); + if(n) + gidspace[g++] = n; + if(g >= conf.gidspace-2) { + cprint("conf.gidspace(%ld) too small\n", conf.gidspace); + goto initu; + } + if(c == '\n') + goto gl3; + c = fname(name); + if(c == ',' || c == '\n') + goto gl2; + cprint("gid truncated\n"); + +gl3: + gidspace[g++] = 0; + +gskip: + if(c == '\n') + goto gl1; + if(c) { + c = fname(name); + goto gskip; + } + if(cons.flags & Fuid) { + o = 0; + for(c=0; c<g; c++) { + n = gidspace[c]; + if(n == 0) { + o = 0; + continue; + } + uidtostr1(name, n); + if(o) { + if(o > 6) { + cprint("\n %s", name); + o = 1; + } else + cprint(" %s", name); + } else + cprint("\n%6s", name); + o++; + } + cprint("\n"); + } + goto out; + +ainitu: + wlock(&uidgc.uidlock); + uidgc.uidbuf = malloc(MAXDAT); + +initu: + cprint("initializing minimal user table\n"); + memset(uid, 0, conf.nuid * sizeof(*uid)); + memset(uidspace, 0, conf.uidspace * sizeof(*uidspace)); + memset(gidspace, 0, conf.gidspace * sizeof(*gidspace)); + o = conf.uidspace; + u = 0; + + for(i=0; admusers[i].name; i++){ + o -= strlen(admusers[i].name)+1; + strcpy(uidspace+o, admusers[i].name); + uid[u].uid = admusers[i].uid; + uid[u].lead = admusers[i].leader; + uid[u].offset = o; + u++; + } + +out: + free(uidgc.uidbuf); + writegroup = strtouid1("write"); + wunlock(&uidgc.uidlock); + +} + +void +uidtostr(char *name, int id) +{ + rlock(&uidgc.uidlock); + uidtostr1(name, id); + runlock(&uidgc.uidlock); +} + +void +uidtostr1(char *name, int id) +{ + Uid *u; + int i; + + if(id == 0){ + strncpy(name, "none", NAMELEN); + return; + } + for(i=0, u=uid; i<conf.nuid; i++,u++) { + if(u->uid == id) { + strncpy(name, uidspace+u->offset, NAMELEN); + return; + } + } + strncpy(name, "none", NAMELEN); +} + +int +strtouid(char *s) +{ + int i; + + rlock(&uidgc.uidlock); + i = strtouid1(s); + runlock(&uidgc.uidlock); + return i; +} + +int +strtouid1(char *s) +{ + Uid *u; + int i; + + for(i=0, u=uid; i<conf.nuid; i++,u++) + if(!strcmp(s, uidspace+u->offset)) + return u->uid; + return 0; +} + +int +ingroup(int u, int g) +{ + short *p; + + if(u == g) + return 1; + rlock(&uidgc.uidlock); + for(p=gidspace; *p;) { + if(*p != g) { + for(p++; *p++;) + ; + continue; + } + for(p++; *p; p++) + if(*p == u) { + runlock(&uidgc.uidlock); + return 1; + } + } + runlock(&uidgc.uidlock); + return 0; +} + +int +leadgroup(int ui, int gi) +{ + Uid *u; + int i; + + rlock(&uidgc.uidlock); + for(i=0, u=uid; i<conf.nuid; i++,u++) { + if(u->uid == gi) { + i = u->lead; + runlock(&uidgc.uidlock); + if(i == ui) + return 1; + if(i == 0) + return ingroup(ui, gi); + return 0; + } + } + runlock(&uidgc.uidlock); + return 0; +} diff --git a/sys/src/cmd/disk/kfscmd.c b/sys/src/cmd/disk/kfscmd.c new file mode 100755 index 000000000..05fb0820f --- /dev/null +++ b/sys/src/cmd/disk/kfscmd.c @@ -0,0 +1,56 @@ +#include <u.h> +#include <libc.h> + +void +main(int argc, char *argv[]) +{ + char *name, buf[4*1024]; + int fd, n, i, errs; + + name = 0; + ARGBEGIN{ + case 'n': + name = ARGF(); + break; + default: + fprint(2, "usage: kfscmd [-n server] commands\n"); + exits("usage"); + }ARGEND + + if(name) + snprint(buf, sizeof buf, "/srv/kfs.%s.cmd", name); + else + strcpy(buf, "/srv/kfs.cmd"); + fd = open(buf, ORDWR); + if(fd < 0){ + fprint(2, "kfscmd: can't open commands file\n"); + exits("commands file"); + } + + errs = 0; + for(i = 0; i < argc; i++){ + if(write(fd, argv[i], strlen(argv[i])) != strlen(argv[i])){ + fprint(2, "%s: error writing %s: %r", argv0, argv[i]); + errs++; + continue; + } + for(;;){ + n = read(fd, buf, sizeof buf - 1); + if(n < 0){ + fprint(2, "%s: error executing %s: %r", argv0, argv[i]); + errs++; + break; + } + buf[n] = '\0'; + if(strcmp(buf, "done") == 0 || strcmp(buf, "success") == 0) + break; + if(strcmp(buf, "unknown command") == 0){ + errs++; + print("kfscmd: command %s not recognized\n", argv[i]); + break; + } + write(1, buf, n); + } + } + exits(errs ? "errors" : 0); +} diff --git a/sys/src/cmd/disk/mbr.c b/sys/src/cmd/disk/mbr.c new file mode 100755 index 000000000..c7494a011 --- /dev/null +++ b/sys/src/cmd/disk/mbr.c @@ -0,0 +1,197 @@ +/* + * install new master boot record boot code on PC disk. + */ + +#include <u.h> +#include <libc.h> +#include <disk.h> + +typedef struct { + uchar active; /* active flag */ + uchar starth; /* starting head */ + uchar starts; /* starting sector */ + uchar startc; /* starting cylinder */ + uchar type; /* partition type */ + uchar endh; /* ending head */ + uchar ends; /* ending sector */ + uchar endc; /* ending cylinder */ + uchar lba[4]; /* starting LBA */ + uchar size[4]; /* size in sectors */ +} Tentry; + +enum { + Toffset = 0x1BE, /* offset of partition table */ + + Type9 = 0x39, +}; + +/* + * Default boot block prints an error message and reboots. + */ +static int ndefmbr = Toffset; +static char defmbr[512] = { +[0x000] 0xEB, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +[0x03E] 0xFA, 0xFC, 0x8C, 0xC8, 0x8E, 0xD8, 0x8E, 0xD0, + 0xBC, 0x00, 0x7C, 0xBE, 0x77, 0x7C, 0xE8, 0x19, + 0x00, 0x33, 0xC0, 0xCD, 0x16, 0xBB, 0x40, 0x00, + 0x8E, 0xC3, 0xBB, 0x72, 0x00, 0xB8, 0x34, 0x12, + 0x26, 0x89, 0x07, 0xEA, 0x00, 0x00, 0xFF, 0xFF, + 0xEB, 0xD6, 0xAC, 0x0A, 0xC0, 0x74, 0x09, 0xB4, + 0x0E, 0xBB, 0x07, 0x00, 0xCD, 0x10, 0xEB, 0xF2, + 0xC3, 'N', 'o', 't', ' ', 'a', ' ', 'b', + 'o', 'o', 't', 'a', 'b', 'l', 'e', ' ', + 'd', 'i', 's', 'c', ' ', 'o', 'r', ' ', + 'd', 'i', 's', 'c', ' ', 'e', 'r', 'r', + 'o', 'r', '\r', '\n', 'P', 'r', 'e', 's', + 's', ' ', 'a', 'l', 'm', 'o', 's', 't', + ' ', 'a', 'n', 'y', ' ', 'k', 'e', 'y', + ' ', 't', 'o', ' ', 'r', 'e', 'b', 'o', + 'o', 't', '.', '.', '.', 0x00, 0x00, 0x00, +}; + +void +usage(void) +{ + fprint(2, "usage: disk/mbr [-m mbrfile] disk\n"); + exits("usage"); +} + +void +fatal(char *fmt, ...) +{ + char err[ERRMAX]; + va_list arg; + + va_start(arg, fmt); + vsnprint(err, ERRMAX, fmt, arg); + va_end(arg); + fprint(2, "mbr: %s\n", err); + exits(err); +} + +static void +putle32(void* v, u32int i) +{ + uchar *p; + + p = v; + p[0] = i; + p[1] = i>>8; + p[2] = i>>16; + p[3] = i>>24; +} + +static void +writechs(Disk *disk, uchar *p, vlong lba) +{ + int c, h, s; + + s = lba % disk->s; + h = (lba / disk->s) % disk->h; + c = lba / (disk->s * disk->h); + + if(c >= 1024) { + c = 1023; + h = disk->h - 1; + s = disk->s - 1; + } + + p[0] = h; + p[1] = ((s+1) & 0x3F) | ((c>>2) & 0xC0); + p[2] = c; +} + +static void +wrtentry(Disk *disk, Tentry *tp, int type, u32int base, u32int lba, u32int end) +{ + tp->active = 0x80; /* make this sole partition active */ + tp->type = type; + writechs(disk, &tp->starth, lba); + writechs(disk, &tp->endh, end-1); + putle32(tp->lba, lba-base); + putle32(tp->size, end-lba); +} + +void +main(int argc, char **argv) +{ + Disk *disk; + Tentry *tp; + uchar *mbr, *buf; + char *mbrfile; + ulong secsize; + int flag9, sysfd, nmbr; + + flag9 = 0; + mbrfile = nil; + ARGBEGIN { + case '9': + flag9 = 1; + break; + case 'm': + mbrfile = EARGF(usage()); + break; + default: + usage(); + } ARGEND + + if(argc < 1) + usage(); + + disk = opendisk(argv[0], 0, 0); + if(disk == nil) + fatal("opendisk %s: %r", argv[0]); + + if(disk->type == Tfloppy) + fatal("will not install mbr on floppy"); + if(disk->secsize != 512) + fatal("secsize %d invalid", disk->secsize); + + secsize = disk->secsize; + buf = malloc(secsize*(disk->s+1)); + mbr = malloc(secsize*disk->s); + if(buf == nil || mbr == nil) + fatal("out of memory"); + + /* + * Start with initial sector from disk. + */ + if(seek(disk->fd, 0, 0) < 0) + fatal("seek to boot sector: %r\n"); + if(read(disk->fd, mbr, secsize) != secsize) + fatal("reading boot sector: %r"); + + if(mbrfile == nil){ + nmbr = ndefmbr; + memmove(mbr, defmbr, nmbr); + } else { + memset(buf, 0, secsize*disk->s); + if((sysfd = open(mbrfile, OREAD)) < 0) + fatal("open %s: %r", mbrfile); + if((nmbr = read(sysfd, buf, secsize*(disk->s+1))) < 0) + fatal("read %s: %r", mbrfile); + if(nmbr > secsize*disk->s) + fatal("master boot record too large %d > %d", nmbr, secsize*disk->s); + if(nmbr < secsize) + nmbr = secsize; + close(sysfd); + memmove(buf+Toffset, mbr+Toffset, secsize-Toffset); + memmove(mbr, buf, nmbr); + } + + if(flag9){ + tp = (Tentry*)(mbr+Toffset); + memset(tp, 0, secsize-Toffset); + wrtentry(disk, tp, Type9, 0, disk->s, disk->secs); + } + mbr[secsize-2] = 0x55; + mbr[secsize-1] = 0xAA; + nmbr = (nmbr+secsize-1)&~(secsize-1); + if(seek(disk->wfd, 0, 0) < 0) + fatal("seek to MBR sector: %r\n"); + if(write(disk->wfd, mbr, nmbr) != nmbr) + fatal("writing MBR: %r"); + + exits(0); +} diff --git a/sys/src/cmd/disk/mkext.c b/sys/src/cmd/disk/mkext.c new file mode 100755 index 000000000..1a2210388 --- /dev/null +++ b/sys/src/cmd/disk/mkext.c @@ -0,0 +1,318 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +enum{ + LEN = 8*1024, + NFLDS = 6, /* filename, modes, uid, gid, mtime, bytes */ +}; + +int selected(char*, int, char*[]); +void mkdirs(char*, char*); +void mkdir(char*, ulong, ulong, char*, char*); +void extract(char*, ulong, ulong, char*, char*, uvlong); +void seekpast(uvlong); +void error(char*, ...); +void warn(char*, ...); +void usage(void); +#pragma varargck argpos warn 1 +#pragma varargck argpos error 1 + +Biobufhdr bin; +uchar binbuf[2*LEN]; +char linebuf[LEN]; +int uflag; +int hflag; +int vflag; +int Tflag; + +void +main(int argc, char **argv) +{ + Biobuf bout; + char *fields[NFLDS], name[2*LEN], *p, *namep; + char *uid, *gid; + ulong mode, mtime; + uvlong bytes; + + quotefmtinstall(); + namep = name; + ARGBEGIN{ + case 'd': + p = ARGF(); + if(strlen(p) >= LEN) + error("destination fs name too long\n"); + strcpy(name, p); + namep = name + strlen(name); + break; + case 'h': + hflag = 1; + Binit(&bout, 1, OWRITE); + break; + case 'u': + uflag = 1; + Tflag = 1; + break; + case 'T': + Tflag = 1; + break; + case 'v': + vflag = 1; + break; + default: + usage(); + }ARGEND + + Binits(&bin, 0, OREAD, binbuf, sizeof binbuf); + while(p = Brdline(&bin, '\n')){ + p[Blinelen(&bin)-1] = '\0'; + strcpy(linebuf, p); + p = linebuf; + if(strcmp(p, "end of archive") == 0){ + Bterm(&bout); + fprint(2, "done\n"); + exits(0); + } + if (gettokens(p, fields, NFLDS, " \t") != NFLDS){ + warn("too few fields in file header"); + continue; + } + p = unquotestrdup(fields[0]); + strcpy(namep, p); + free(p); + mode = strtoul(fields[1], 0, 8); + uid = fields[2]; + gid = fields[3]; + mtime = strtoul(fields[4], 0, 10); + bytes = strtoull(fields[5], 0, 10); + if(argc){ + if(!selected(namep, argc, argv)){ + if(bytes) + seekpast(bytes); + continue; + } + mkdirs(name, namep); + } + if(hflag){ + Bprint(&bout, "%q %luo %q %q %lud %llud\n", + name, mode, uid, gid, mtime, bytes); + if(bytes) + seekpast(bytes); + continue; + } + if(mode & DMDIR) + mkdir(name, mode, mtime, uid, gid); + else + extract(name, mode, mtime, uid, gid, bytes); + } + fprint(2, "premature end of archive\n"); + exits("premature end of archive"); +} + +int +fileprefix(char *prefix, char *s) +{ + while(*prefix) + if(*prefix++ != *s++) + return 0; + if(*s && *s != '/') + return 0; + return 1; +} + +int +selected(char *s, int argc, char *argv[]) +{ + int i; + + for(i=0; i<argc; i++) + if(fileprefix(argv[i], s)) + return 1; + return 0; +} + +void +mkdirs(char *name, char *namep) +{ + char buf[2*LEN], *p; + int fd; + + strcpy(buf, name); + for(p = &buf[namep - name]; p = utfrune(p, '/'); p++){ + if(p[1] == '\0') + return; + *p = 0; + fd = create(buf, OREAD, 0775|DMDIR); + close(fd); + *p = '/'; + } +} + +void +mkdir(char *name, ulong mode, ulong mtime, char *uid, char *gid) +{ + Dir *d, xd; + int fd; + char *p; + char olderr[256]; + + fd = create(name, OREAD, mode); + if(fd < 0){ + rerrstr(olderr, sizeof(olderr)); + if((d = dirstat(name)) == nil || !(d->mode & DMDIR)){ + free(d); + warn("can't make directory %q, mode %luo: %s", name, mode, olderr); + return; + } + free(d); + } + close(fd); + + d = &xd; + nulldir(d); + p = utfrrune(name, L'/'); + if(p) + p++; + else + p = name; + d->name = p; + if(uflag){ + d->uid = uid; + d->gid = gid; + } + if(Tflag) + d->mtime = mtime; + d->mode = mode; + if(dirwstat(name, d) < 0) + warn("can't set modes for %q: %r", name); + + if(uflag||Tflag){ + if((d = dirstat(name)) == nil){ + warn("can't reread modes for %q: %r", name); + return; + } + if(Tflag && d->mtime != mtime) + warn("%q: time mismatch %lud %lud\n", name, mtime, d->mtime); + if(uflag && strcmp(uid, d->uid)) + warn("%q: uid mismatch %q %q", name, uid, d->uid); + if(uflag && strcmp(gid, d->gid)) + warn("%q: gid mismatch %q %q", name, gid, d->gid); + } +} + +void +extract(char *name, ulong mode, ulong mtime, char *uid, char *gid, uvlong bytes) +{ + Dir d, *nd; + Biobuf *b; + char buf[LEN]; + char *p; + ulong n; + uvlong tot; + + if(vflag) + print("x %q %llud bytes\n", name, bytes); + + b = Bopen(name, OWRITE); + if(!b){ + warn("can't make file %q: %r", name); + seekpast(bytes); + return; + } + for(tot = 0; tot < bytes; tot += n){ + n = sizeof buf; + if(tot + n > bytes) + n = bytes - tot; + n = Bread(&bin, buf, n); + if(n <= 0) + error("premature eof reading %q", name); + if(Bwrite(b, buf, n) != n) + warn("error writing %q: %r", name); + } + + nulldir(&d); + p = utfrrune(name, '/'); + if(p) + p++; + else + p = name; + d.name = p; + if(uflag){ + d.uid = uid; + d.gid = gid; + } + if(Tflag) + d.mtime = mtime; + d.mode = mode; + Bflush(b); + if(dirfwstat(Bfildes(b), &d) < 0) + warn("can't set modes for %q: %r", name); + if(uflag||Tflag){ + if((nd = dirfstat(Bfildes(b))) == nil) + warn("can't reread modes for %q: %r", name); + else{ + if(Tflag && nd->mtime != mtime) + warn("%q: time mismatch %lud %lud\n", + name, mtime, nd->mtime); + if(uflag && strcmp(uid, nd->uid)) + warn("%q: uid mismatch %q %q", + name, uid, nd->uid); + if(uflag && strcmp(gid, nd->gid)) + warn("%q: gid mismatch %q %q", + name, gid, nd->gid); + free(nd); + } + } + Bterm(b); +} + +void +seekpast(uvlong bytes) +{ + char buf[LEN]; + long n; + uvlong tot; + + for(tot = 0; tot < bytes; tot += n){ + n = sizeof buf; + if(tot + n > bytes) + n = bytes - tot; + n = Bread(&bin, buf, n); + if(n < 0) + error("premature eof"); + } +} + +void +error(char *fmt, ...) +{ + char buf[1024]; + va_list arg; + + sprint(buf, "%q: ", argv0); + va_start(arg, fmt); + vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "%s\n", buf); + exits(0); +} + +void +warn(char *fmt, ...) +{ + char buf[1024]; + va_list arg; + + sprint(buf, "%q: ", argv0); + va_start(arg, fmt); + vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "%s\n", buf); +} + +void +usage(void) +{ + fprint(2, "usage: mkext [-h] [-u] [-v] [-d dest-fs] [file ...]\n"); + exits("usage"); +} diff --git a/sys/src/cmd/disk/mkfile b/sys/src/cmd/disk/mkfile new file mode 100755 index 000000000..8592af83b --- /dev/null +++ b/sys/src/cmd/disk/mkfile @@ -0,0 +1,49 @@ +</$objtype/mkfile + +TARG=exsort\ + format\ + kfscmd\ + mbr\ + mkext\ + mkfs\ + partfs\ + +DIRS=\ + 9660\ + kfs\ + prep\ +# sacfs\ + +OFILES= + +BIN=/$objtype/bin/disk + +UPDATE=\ + mkfile\ + ${TARG:%=%.c}\ + /sys/man/8/prep\ + /sys/man/8/mkfs\ + ${TARG:%=/386/bin/disk/%}\ + +</sys/src/cmd/mkmany + +all:V: all-kfs + +install:V: install-kfs ksync + +clean:V: clean-kfs + +nuke:V: nuke-kfs + +installall:V: installall-kfs + +%-kfs:V: + for(i in $DIRS) @{ + cd $i + mk $MKFLAGS $stem + } + +ksync: + touch $BIN/ksync + +$O.format: /$objtype/lib/libdisk.a diff --git a/sys/src/cmd/disk/mkfs.c b/sys/src/cmd/disk/mkfs.c new file mode 100755 index 000000000..1412011a6 --- /dev/null +++ b/sys/src/cmd/disk/mkfs.c @@ -0,0 +1,858 @@ +#include <u.h> +#include <libc.h> +#include <auth.h> +#include <bio.h> + +enum{ + LEN = 8*1024, + HUNKS = 128, + + /* + * types of destination file sytems + */ + Kfs = 0, + Fs, + Archive, +}; + +typedef struct File File; + +struct File{ + char *new; + char *elem; + char *old; + char *uid; + char *gid; + ulong mode; +}; + +void arch(Dir*); +void copy(Dir*); +int copyfile(File*, Dir*, int); +void* emalloc(ulong); +void error(char *, ...); +void freefile(File*); +File* getfile(File*); +char* getmode(char*, ulong*); +char* getname(char*, char**); +char* getpath(char*); +void kfscmd(char *); +void mkdir(Dir*); +int mkfile(File*); +void mkfs(File*, int); +char* mkpath(char*, char*); +void mktree(File*, int); +void mountkfs(char*); +void printfile(File*); +void setnames(File*); +void setusers(void); +void skipdir(void); +char* strdup(char*); +int uptodate(Dir*, char*); +void usage(void); +void warn(char *, ...); + +Biobuf *b; +Biobufhdr bout; /* stdout when writing archive */ +uchar boutbuf[2*LEN]; +char newfile[LEN]; +char oldfile[LEN]; +char *proto; +char *cputype; +char *users; +char *oldroot; +char *newroot; +char *prog = "mkfs"; +int lineno; +char *buf; +char *zbuf; +int buflen = 1024-8; +int indent; +int verb; +int modes; +int ream; +int debug; +int xflag; +int sfd; +int fskind; /* Kfs, Fs, Archive */ +int setuid; /* on Fs: set uid and gid? */ +char *user; + +void +main(int argc, char **argv) +{ + File file; + char *name; + int i, errs; + + quotefmtinstall(); + user = getuser(); + name = ""; + memset(&file, 0, sizeof file); + file.new = ""; + file.old = 0; + oldroot = ""; + newroot = "/n/kfs"; + users = 0; + fskind = Kfs; + ARGBEGIN{ + case 'a': + if(fskind != Kfs) { + fprint(2, "cannot use -a with -d\n"); + usage(); + } + fskind = Archive; + newroot = ""; + Binits(&bout, 1, OWRITE, boutbuf, sizeof boutbuf); + break; + case 'd': + if(fskind != Kfs) { + fprint(2, "cannot use -d with -a\n"); + usage(); + } + fskind = Fs; + newroot = ARGF(); + break; + case 'D': + debug = 1; + break; + case 'n': + name = EARGF(usage()); + break; + case 'p': + modes = 1; + break; + case 'r': + ream = 1; + break; + case 's': + oldroot = ARGF(); + break; + case 'u': + users = ARGF(); + break; + case 'U': + setuid = 1; + break; + case 'v': + verb = 1; + break; + case 'x': + xflag = 1; + break; + case 'z': + buflen = atoi(ARGF())-8; + break; + default: + usage(); + }ARGEND + + if(!argc) + usage(); + + buf = emalloc(buflen); + zbuf = emalloc(buflen); + memset(zbuf, 0, buflen); + + mountkfs(name); + kfscmd("allow"); + proto = "users"; + setusers(); + cputype = getenv("cputype"); + if(cputype == 0) + cputype = "68020"; + + errs = 0; + for(i = 0; i < argc; i++){ + proto = argv[i]; + fprint(2, "processing %q\n", proto); + + b = Bopen(proto, OREAD); + if(!b){ + fprint(2, "%q: can't open %q: skipping\n", prog, proto); + errs++; + continue; + } + + lineno = 0; + indent = 0; + mkfs(&file, -1); + Bterm(b); + } + fprint(2, "file system made\n"); + kfscmd("disallow"); + kfscmd("sync"); + if(errs) + exits("skipped protos"); + if(fskind == Archive){ + Bprint(&bout, "end of archive\n"); + Bterm(&bout); + } + exits(0); +} + +void +mkfs(File *me, int level) +{ + File *child; + int rec; + + child = getfile(me); + if(!child) + return; + if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){ + rec = child->elem[0] == '+'; + free(child->new); + child->new = strdup(me->new); + setnames(child); + mktree(child, rec); + freefile(child); + child = getfile(me); + } + while(child && indent > level){ + if(mkfile(child)) + mkfs(child, indent); + freefile(child); + child = getfile(me); + } + if(child){ + freefile(child); + Bseek(b, -Blinelen(b), 1); + lineno--; + } +} + +void +mktree(File *me, int rec) +{ + File child; + Dir *d; + int i, n, fd; + + fd = open(oldfile, OREAD); + if(fd < 0){ + warn("can't open %q: %r", oldfile); + return; + } + + child = *me; + while((n = dirread(fd, &d)) > 0){ + for(i = 0; i < n; i++){ + child.new = mkpath(me->new, d[i].name); + if(me->old) + child.old = mkpath(me->old, d[i].name); + child.elem = d[i].name; + setnames(&child); + if(copyfile(&child, &d[i], 1) && rec) + mktree(&child, rec); + free(child.new); + if(child.old) + free(child.old); + } + } + close(fd); +} + +int +mkfile(File *f) +{ + Dir *dir; + + if((dir = dirstat(oldfile)) == nil){ + warn("can't stat file %q: %r", oldfile); + skipdir(); + return 0; + } + return copyfile(f, dir, 0); +} + +int +copyfile(File *f, Dir *d, int permonly) +{ + ulong mode; + Dir nd; + + if(xflag){ + Bprint(&bout, "%q\t%ld\t%lld\n", f->new, d->mtime, d->length); + return (d->mode & DMDIR) != 0; + } + if(verb && (fskind == Archive || ream)) + fprint(2, "%q\n", f->new); + d->name = f->elem; + if(d->type != 'M'){ + d->uid = "sys"; + d->gid = "sys"; + mode = (d->mode >> 6) & 7; + d->mode |= mode | (mode << 3); + } + if(strcmp(f->uid, "-") != 0) + d->uid = f->uid; + if(strcmp(f->gid, "-") != 0) + d->gid = f->gid; + if(fskind == Fs && !setuid){ + d->uid = ""; + d->gid = ""; + } + if(f->mode != ~0){ + if(permonly) + d->mode = (d->mode & ~0666) | (f->mode & 0666); + else if((d->mode&DMDIR) != (f->mode&DMDIR)) + warn("inconsistent mode for %q", f->new); + else + d->mode = f->mode; + } + if(!uptodate(d, newfile)){ + if(verb && (fskind != Archive && ream == 0)) + fprint(2, "%q\n", f->new); + if(d->mode & DMDIR) + mkdir(d); + else + copy(d); + }else if(modes){ + nulldir(&nd); + nd.mode = d->mode; + nd.gid = d->gid; + nd.mtime = d->mtime; + if(verb && (fskind != Archive && ream == 0)) + fprint(2, "%q\n", f->new); + if(dirwstat(newfile, &nd) < 0) + warn("can't set modes for %q: %r", f->new); + nulldir(&nd); + nd.uid = d->uid; + dirwstat(newfile, &nd); + } + return (d->mode & DMDIR) != 0; +} + +/* + * check if file to is up to date with + * respect to the file represented by df + */ +int +uptodate(Dir *df, char *to) +{ + int ret; + Dir *dt; + + if(fskind == Archive || ream || (dt = dirstat(to)) == nil) + return 0; + ret = dt->mtime >= df->mtime; + free(dt); + return ret; +} + +void +copy(Dir *d) +{ + char cptmp[LEN], *p; + int f, t, n, needwrite, nowarnyet = 1; + vlong tot, len; + Dir nd; + + f = open(oldfile, OREAD); + if(f < 0){ + warn("can't open %q: %r", oldfile); + return; + } + t = -1; + if(fskind == Archive) + arch(d); + else{ + strcpy(cptmp, newfile); + p = utfrrune(cptmp, L'/'); + if(!p) + error("internal temporary file error"); + strcpy(p+1, "__mkfstmp"); + t = create(cptmp, OWRITE, 0666); + if(t < 0){ + warn("can't create %q: %r", newfile); + close(f); + return; + } + } + + needwrite = 0; + for(tot = 0; tot < d->length; tot += n){ + len = d->length - tot; + /* don't read beyond d->length */ + if (len > buflen) + len = buflen; + n = read(f, buf, len); + if(n <= 0) { + if(n < 0 && nowarnyet) { + warn("can't read %q: %r", oldfile); + nowarnyet = 0; + } + /* + * don't quit: pad to d->length (in pieces) to agree + * with the length in the header, already emitted. + */ + memset(buf, 0, len); + n = len; + } + if(fskind == Archive){ + if(Bwrite(&bout, buf, n) != n) + error("write error: %r"); + }else if(memcmp(buf, zbuf, n) == 0){ + if(seek(t, n, 1) < 0) + error("can't write zeros to %q: %r", newfile); + needwrite = 1; + }else{ + if(write(t, buf, n) < n) + error("can't write %q: %r", newfile); + needwrite = 0; + } + } + close(f); + if(needwrite){ + if(seek(t, -1, 1) < 0 || write(t, zbuf, 1) != 1) + error("can't write zero at end of %q: %r", newfile); + } + if(tot != d->length){ + /* this should no longer happen */ + warn("wrong number of bytes written to %q (was %lld should be %lld)\n", + newfile, tot, d->length); + if(fskind == Archive){ + warn("seeking to proper position\n"); + /* does no good if stdout is a pipe */ + Bseek(&bout, d->length - tot, 1); + } + } + if(fskind == Archive) + return; + remove(newfile); + nulldir(&nd); + nd.mode = d->mode; + nd.gid = d->gid; + nd.mtime = d->mtime; + nd.name = d->name; + if(dirfwstat(t, &nd) < 0) + error("can't move tmp file to %q: %r", newfile); + nulldir(&nd); + nd.uid = d->uid; + dirfwstat(t, &nd); + close(t); +} + +void +mkdir(Dir *d) +{ + Dir *d1; + Dir nd; + int fd; + + if(fskind == Archive){ + arch(d); + return; + } + fd = create(newfile, OREAD, d->mode); + nulldir(&nd); + nd.mode = d->mode; + nd.gid = d->gid; + nd.mtime = d->mtime; + if(fd < 0){ + if((d1 = dirstat(newfile)) == nil || !(d1->mode & DMDIR)){ + free(d1); + error("can't create %q", newfile); + } + free(d1); + if(dirwstat(newfile, &nd) < 0) + warn("can't set modes for %q: %r", newfile); + nulldir(&nd); + nd.uid = d->uid; + dirwstat(newfile, &nd); + return; + } + if(dirfwstat(fd, &nd) < 0) + warn("can't set modes for %q: %r", newfile); + nulldir(&nd); + nd.uid = d->uid; + dirfwstat(fd, &nd); + close(fd); +} + +void +arch(Dir *d) +{ + Bprint(&bout, "%q %luo %q %q %lud %lld\n", + newfile, d->mode, d->uid, d->gid, d->mtime, d->length); +} + +char * +mkpath(char *prefix, char *elem) +{ + char *p; + int n; + + n = strlen(prefix) + strlen(elem) + 2; + p = emalloc(n); + sprint(p, "%s/%s", prefix, elem); + return p; +} + +char * +strdup(char *s) +{ + char *t; + + t = emalloc(strlen(s) + 1); + return strcpy(t, s); +} + +void +setnames(File *f) +{ + sprint(newfile, "%s%s", newroot, f->new); + if(f->old){ + if(f->old[0] == '/') + sprint(oldfile, "%s%s", oldroot, f->old); + else + strcpy(oldfile, f->old); + }else + sprint(oldfile, "%s%s", oldroot, f->new); + if(strlen(newfile) >= sizeof newfile + || strlen(oldfile) >= sizeof oldfile) + error("name overfile"); +} + +void +freefile(File *f) +{ + if(f->old) + free(f->old); + if(f->new) + free(f->new); + free(f); +} + +/* + * skip all files in the proto that + * could be in the current dir + */ +void +skipdir(void) +{ + char *p, c; + int level; + + if(indent < 0 || b == nil) /* b is nil when copying adm/users */ + return; + level = indent; + for(;;){ + indent = 0; + p = Brdline(b, '\n'); + lineno++; + if(!p){ + indent = -1; + return; + } + while((c = *p++) != '\n') + if(c == ' ') + indent++; + else if(c == '\t') + indent += 8; + else + break; + if(indent <= level){ + Bseek(b, -Blinelen(b), 1); + lineno--; + return; + } + } +} + +File* +getfile(File *old) +{ + File *f; + char *elem; + char *p; + int c; + + if(indent < 0) + return 0; +loop: + indent = 0; + p = Brdline(b, '\n'); + lineno++; + if(!p){ + indent = -1; + return 0; + } + while((c = *p++) != '\n') + if(c == ' ') + indent++; + else if(c == '\t') + indent += 8; + else + break; + if(c == '\n' || c == '#') + goto loop; + p--; + f = emalloc(sizeof *f); + p = getname(p, &elem); + if(debug) + fprint(2, "getfile: %q root %q\n", elem, old->new); + f->new = mkpath(old->new, elem); + f->elem = utfrrune(f->new, L'/') + 1; + p = getmode(p, &f->mode); + p = getname(p, &f->uid); + if(!*f->uid) + f->uid = "-"; + p = getname(p, &f->gid); + if(!*f->gid) + f->gid = "-"; + f->old = getpath(p); + if(f->old && strcmp(f->old, "-") == 0){ + free(f->old); + f->old = 0; + } + setnames(f); + + if(debug) + printfile(f); + + return f; +} + +char* +getpath(char *p) +{ + char *q, *new; + int c, n; + + while((c = *p) == ' ' || c == '\t') + p++; + q = p; + while((c = *q) != '\n' && c != ' ' && c != '\t') + q++; + if(q == p) + return 0; + n = q - p; + new = emalloc(n + 1); + memcpy(new, p, n); + new[n] = 0; + return new; +} + +char* +getname(char *p, char **buf) +{ + char *s, *start; + int c; + + while((c = *p) == ' ' || c == '\t') + p++; + + start = p; + while((c = *p) != '\n' && c != ' ' && c != '\t' && c != '\0') + p++; + + *buf = malloc(p+1-start); + if(*buf == nil) + return nil; + memmove(*buf, start, p-start); + (*buf)[p-start] = '\0'; + + if(**buf == '$'){ + s = getenv(*buf+1); + if(s == 0){ + warn("can't read environment variable %q", *buf+1); + skipdir(); + free(*buf); + return nil; + } + free(*buf); + *buf = s; + } + return p; +} + +char* +getmode(char *p, ulong *xmode) +{ + char *buf, *s; + ulong m; + + *xmode = ~0; + p = getname(p, &buf); + if(p == nil) + return nil; + + s = buf; + if(!*s || strcmp(s, "-") == 0) + return p; + m = 0; + if(*s == 'd'){ + m |= DMDIR; + s++; + } + if(*s == 'a'){ + m |= DMAPPEND; + s++; + } + if(*s == 'l'){ + m |= DMEXCL; + s++; + } + if(s[0] < '0' || s[0] > '7' + || s[1] < '0' || s[1] > '7' + || s[2] < '0' || s[2] > '7' + || s[3]){ + warn("bad mode specification %q", buf); + free(buf); + return p; + } + *xmode = m | strtoul(s, 0, 8); + free(buf); + return p; +} + +void +setusers(void) +{ + File file; + int m; + + if(fskind != Kfs) + return; + m = modes; + modes = 1; + file.uid = "adm"; + file.gid = "adm"; + file.mode = DMDIR|0775; + file.new = "/adm"; + file.elem = "adm"; + file.old = 0; + setnames(&file); + strcpy(oldfile, file.new); /* Don't use root for /adm */ + mkfile(&file); + file.new = "/adm/users"; + file.old = users; + file.elem = "users"; + file.mode = 0664; + setnames(&file); + if (file.old) + strcpy(oldfile, file.old); /* Don't use root for /adm/users */ + mkfile(&file); + kfscmd("user"); + mkfile(&file); + file.mode = DMDIR|0775; + file.new = "/adm"; + file.old = "/adm"; + file.elem = "adm"; + setnames(&file); + strcpy(oldfile, file.old); /* Don't use root for /adm */ + mkfile(&file); + modes = m; +} + +void +mountkfs(char *name) +{ + char kname[64]; + + if(fskind != Kfs) + return; + if(name[0]) + snprint(kname, sizeof kname, "/srv/kfs.%s", name); + else + strcpy(kname, "/srv/kfs"); + sfd = open(kname, ORDWR); + if(sfd < 0){ + fprint(2, "can't open %q\n", kname); + exits("open /srv/kfs"); + } + if(mount(sfd, -1, "/n/kfs", MREPL|MCREATE, "") < 0){ + fprint(2, "can't mount kfs on /n/kfs\n"); + exits("mount kfs"); + } + close(sfd); + strcat(kname, ".cmd"); + sfd = open(kname, ORDWR); + if(sfd < 0){ + fprint(2, "can't open %q\n", kname); + exits("open /srv/kfs"); + } +} + +void +kfscmd(char *cmd) +{ + char buf[4*1024]; + int n; + + if(fskind != Kfs) + return; + if(write(sfd, cmd, strlen(cmd)) != strlen(cmd)){ + fprint(2, "%q: error writing %q: %r", prog, cmd); + return; + } + for(;;){ + n = read(sfd, buf, sizeof buf - 1); + if(n <= 0) + return; + buf[n] = '\0'; + if(strcmp(buf, "done") == 0 || strcmp(buf, "success") == 0) + return; + if(strcmp(buf, "unknown command") == 0){ + fprint(2, "%q: command %q not recognized\n", prog, cmd); + return; + } + } +} + +void * +emalloc(ulong n) +{ + void *p; + + if((p = malloc(n)) == 0) + error("out of memory"); + return p; +} + +void +error(char *fmt, ...) +{ + char buf[1024]; + va_list arg; + + sprint(buf, "%q: %q:%d: ", prog, proto, lineno); + va_start(arg, fmt); + vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "%s\n", buf); + kfscmd("disallow"); + kfscmd("sync"); + exits(0); +} + +void +warn(char *fmt, ...) +{ + char buf[1024]; + va_list arg; + + sprint(buf, "%q: %q:%d: ", prog, proto, lineno); + va_start(arg, fmt); + vseprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "%s\n", buf); +} + +void +printfile(File *f) +{ + if(f->old) + fprint(2, "%q from %q %q %q %lo\n", f->new, f->old, f->uid, f->gid, f->mode); + else + fprint(2, "%q %q %q %lo\n", f->new, f->uid, f->gid, f->mode); +} + +void +usage(void) +{ + fprint(2, "usage: %q [-aprvx] [-d root] [-n name] [-s source] [-u users] [-z n] proto ...\n", prog); + exits("usage"); +} diff --git a/sys/src/cmd/disk/partfs.c b/sys/src/cmd/disk/partfs.c new file mode 100755 index 000000000..d353b73b9 --- /dev/null +++ b/sys/src/cmd/disk/partfs.c @@ -0,0 +1,562 @@ +/* + * partfs - serve an underlying file, with devsd-style partitions + */ +#include <u.h> +#include <libc.h> +#include <auth.h> +#include <fcall.h> +#include <thread.h> +#include <9p.h> + +typedef struct Part Part; +struct Part +{ + int inuse; + int vers; + ulong mode; + char *name; + vlong offset; /* in sectors */ + vlong length; /* in sectors */ +}; + +enum +{ + Qroot = 0, + Qdir, + Qctl, + Qpart, +}; + +int fd = -1, ctlfd = -1; +int rdonly; +ulong ctlmode = 0666; +ulong time0; +vlong nsect, sectsize; + +char *inquiry = "partfs hard drive"; +char *sdname = "sdXX"; +Part tab[64]; + +char* +ctlstring(void) +{ + Part *p; + Fmt fmt; + + fmtstrinit(&fmt); + fmtprint(&fmt, "inquiry %s\n", inquiry); + fmtprint(&fmt, "geometry %lld %lld\n", nsect, sectsize); + for (p = tab; p < tab + nelem(tab); p++) + if (p->inuse) + fmtprint(&fmt, "part %s %lld %lld\n", + p->name, p->offset, p->length); + return fmtstrflush(&fmt); +} + +int +addpart(char *name, vlong start, vlong end) +{ + Part *p; + + if(start < 0 || start > end || end > nsect){ + werrstr("bad partition boundaries"); + return -1; + } + + if (strcmp(name, "ctl") == 0 || strcmp(name, "data") == 0) { + werrstr("partition name already in use"); + return -1; + } + for (p = tab; p < tab + nelem(tab) && p->inuse; p++) + if (strcmp(p->name, name) == 0) { + werrstr("partition name already in use"); + return -1; + } + if(p == tab + nelem(tab)){ + werrstr("no free partition slots"); + return -1; + } + + p->inuse = 1; + free(p->name); + p->name = estrdup9p(name); + p->offset = start; + p->length = end - start; + p->mode = ctlmode; + p->vers++; + return 0; +} + +int +delpart(char *s) +{ + Part *p; + + for (p = tab; p < tab + nelem(tab); p++) + if(p->inuse && strcmp(p->name, s) == 0) + break; + if(p == tab + nelem(tab)){ + werrstr("partition not found"); + return -1; + } + + p->inuse = 0; + free(p->name); + p->name = nil; + return 0; +} + +static void +ctlwrite0(Req *r, char *msg, Cmdbuf *cb) +{ + vlong start, end; + Part *p; + + r->ofcall.count = r->ifcall.count; + + if(cb->nf < 1){ + respond(r, "empty control message"); + return; + } + + if(strcmp(cb->f[0], "part") == 0){ + if(cb->nf != 4){ + respondcmderror(r, cb, "part takes 3 args"); + return; + } + start = strtoll(cb->f[2], 0, 0); + end = strtoll(cb->f[3], 0, 0); + if(addpart(cb->f[1], start, end) < 0){ + respondcmderror(r, cb, "%r"); + return; + } + } + else if(strcmp(cb->f[0], "delpart") == 0){ + if(cb->nf != 2){ + respondcmderror(r, cb, "delpart takes 1 arg"); + return; + } + if(delpart(cb->f[1]) < 0){ + respondcmderror(r, cb, "%r"); + return; + } + } + else if(strcmp(cb->f[0], "inquiry") == 0){ + if(cb->nf != 2){ + respondcmderror(r, cb, "inquiry takes 1 arg"); + return; + } + free(inquiry); + inquiry = estrdup9p(cb->f[1]); + } + else if(strcmp(cb->f[0], "geometry") == 0){ + if(cb->nf != 3){ + respondcmderror(r, cb, "geometry takes 2 args"); + return; + } + nsect = strtoll(cb->f[1], 0, 0); + sectsize = strtoll(cb->f[2], 0, 0); + if(tab[0].inuse && strcmp(tab[0].name, "data") == 0 && + tab[0].vers == 0){ + tab[0].offset = 0; + tab[0].length = nsect; + } + for(p = tab; p < tab + nelem(tab); p++) + if(p->inuse && p->offset + p->length > nsect){ + p->inuse = 0; + free(p->name); + p->name = nil; + } + } else + /* pass through to underlying ctl file, if any */ + if (write(ctlfd, msg, r->ifcall.count) != r->ifcall.count) { + respondcmderror(r, cb, "%r"); + return; + } + respond(r, nil); +} + +void +ctlwrite(Req *r) +{ + char *msg; + Cmdbuf *cb; + + r->ofcall.count = r->ifcall.count; + + msg = emalloc9p(r->ifcall.count+1); + memmove(msg, r->ifcall.data, r->ifcall.count); + msg[r->ifcall.count] = '\0'; + + cb = parsecmd(r->ifcall.data, r->ifcall.count); + ctlwrite0(r, msg, cb); + + free(cb); + free(msg); +} + +int +rootgen(int off, Dir *d, void*) +{ + memset(d, 0, sizeof *d); + d->atime = time0; + d->mtime = time0; + if(off == 0){ + d->name = estrdup9p(sdname); + d->mode = DMDIR|0777; + d->qid.path = Qdir; + d->qid.type = QTDIR; + d->uid = estrdup9p("partfs"); + d->gid = estrdup9p("partfs"); + d->muid = estrdup9p(""); + return 0; + } + return -1; +} + +int +dirgen(int off, Dir *d, void*) +{ + int n; + Part *p; + + memset(d, 0, sizeof *d); + d->atime = time0; + d->mtime = time0; + if(off == 0){ + d->name = estrdup9p("ctl"); + d->mode = ctlmode; + d->qid.path = Qctl; + goto Have; + } + + off--; + n = 0; + for(p = tab; p < tab + nelem(tab); p++, n++){ + if(!p->inuse) + continue; + if(n == off){ + d->name = estrdup9p(p->name); + d->length = p->length*sectsize; + d->mode = p->mode; + d->qid.path = Qpart + p - tab; + d->qid.vers = p->vers; + goto Have; + } + } + return -1; + +Have: + d->uid = estrdup9p("partfs"); + d->gid = estrdup9p("partfs"); + d->muid = estrdup9p(""); + return 0; +} + +void* +evommem(void *a, void *b, ulong n) +{ + return memmove(b, a, n); +} + +int +rdwrpart(Req *r) +{ + int q; + long count, tot; + vlong offset; + uchar *dat; + Part *p; + + q = r->fid->qid.path - Qpart; + if(q < 0 || q > nelem(tab) || !tab[q].inuse || + tab[q].vers != r->fid->qid.vers){ + respond(r, "unknown partition"); + return -1; + } + p = &tab[q]; + + offset = r->ifcall.offset; + count = r->ifcall.count; + if(offset < 0){ + respond(r, "negative offset"); + return -1; + } + if(count < 0){ + respond(r, "negative count"); + return -1; + } + if(offset > p->length*sectsize){ + respond(r, "offset past end of partition"); + return -1; + } + if(offset+count > p->length*sectsize) + count = p->length*sectsize - offset; + offset += p->offset*sectsize; + + if(r->ifcall.type == Tread) + dat = (uchar*)r->ofcall.data; + else + dat = (uchar*)r->ifcall.data; + + /* pass i/o through to underlying file */ + seek(fd, offset, 0); + if (r->ifcall.type == Twrite) { + tot = write(fd, dat, count); + if (tot != count) { + respond(r, "%r"); + return -1; + } + } else { + tot = read(fd, dat, count); + if (tot < 0) { + respond(r, "%r"); + return -1; + } + } + r->ofcall.count = tot; + respond(r, nil); + return 0; +} + +void +fsread(Req *r) +{ + char *s; + + switch((int)r->fid->qid.path){ + case Qroot: + dirread9p(r, rootgen, nil); + break; + case Qdir: + dirread9p(r, dirgen, nil); + break; + case Qctl: + s = ctlstring(); + readstr(r, s); + free(s); + break; + default: + rdwrpart(r); + return; + } + respond(r, nil); +} + +void +fswrite(Req *r) +{ + switch((int)r->fid->qid.path){ + case Qroot: + case Qdir: + respond(r, "write to a directory?"); + break; + case Qctl: + ctlwrite(r); + break; + default: + rdwrpart(r); + break; + } +} + +void +fsopen(Req *r) +{ + if(r->ifcall.mode&ORCLOSE) + respond(r, "cannot open ORCLOSE"); + + switch((int)r->fid->qid.path){ + case Qroot: + case Qdir: + if(r->ifcall.mode != OREAD){ + respond(r, "bad mode for directory open"); + return; + } + } + + respond(r, nil); +} + +void +fsstat(Req *r) +{ + int q; + Dir *d; + Part *p; + + d = &r->d; + memset(d, 0, sizeof *d); + d->qid = r->fid->qid; + d->atime = d->mtime = time0; + q = r->fid->qid.path; + switch(q){ + case Qroot: + d->name = estrdup9p("/"); + d->mode = DMDIR|0777; + break; + + case Qdir: + d->name = estrdup9p(sdname); + d->mode = DMDIR|0777; + break; + + case Qctl: + d->name = estrdup9p("ctl"); + d->mode = 0666; + break; + + default: + q -= Qpart; + if(q < 0 || q > nelem(tab) || tab[q].inuse == 0 || + r->fid->qid.vers != tab[q].vers){ + respond(r, "partition no longer exists"); + return; + } + p = &tab[q]; + d->name = estrdup9p(p->name); + d->length = p->length * sectsize; + d->mode = p->mode; + break; + } + + d->uid = estrdup9p("partfs"); + d->gid = estrdup9p("partfs"); + d->muid = estrdup9p(""); + respond(r, nil); +} + +void +fsattach(Req *r) +{ + char *spec; + + spec = r->ifcall.aname; + if(spec && spec[0]){ + respond(r, "invalid attach specifier"); + return; + } + r->ofcall.qid = (Qid){Qroot, 0, QTDIR}; + r->fid->qid = r->ofcall.qid; + respond(r, nil); +} + +char* +fswalk1(Fid *fid, char *name, Qid *qid) +{ + Part *p; + + switch((int)fid->qid.path){ + case Qroot: + if(strcmp(name, sdname) == 0){ + fid->qid.path = Qdir; + fid->qid.type = QTDIR; + *qid = fid->qid; + return nil; + } + break; + case Qdir: + if(strcmp(name, "ctl") == 0){ + fid->qid.path = Qctl; + fid->qid.vers = 0; + fid->qid.type = 0; + *qid = fid->qid; + return nil; + } + for(p = tab; p < tab + nelem(tab); p++) + if(p->inuse && strcmp(p->name, name) == 0){ + fid->qid.path = p - tab + Qpart; + fid->qid.vers = p->vers; + fid->qid.type = 0; + *qid = fid->qid; + return nil; + } + break; + } + return "file not found"; +} + +Srv fs = { + .attach=fsattach, + .open= fsopen, + .read= fsread, + .write= fswrite, + .stat= fsstat, + .walk1= fswalk1, +}; + +char *mtpt = "/dev"; +char *srvname; + +void +usage(void) +{ + fprint(2, "usage: %s [-Dr] [-d sdname] [-m mtpt] [-s srvname] diskimage\n", + argv0); + fprint(2, "\tdefault mtpt is /dev\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int isdir; + char *file, *cname; + Dir *dir; + + quotefmtinstall(); + time0 = time(0); + + ARGBEGIN{ + case 'D': + chatty9p++; + break; + case 'd': + sdname = EARGF(usage()); + break; + case 'm': + mtpt = EARGF(usage()); + break; + case 'r': + rdonly = 1; + break; + case 's': + srvname = EARGF(usage()); + break; + default: + usage(); + }ARGEND + + if(argc != 1) + usage(); + file = argv[0]; + dir = dirstat(file); + if(!dir) + sysfatal("%s: %r", file); + isdir = (dir->mode & DMDIR) != 0; + free(dir); + + if (isdir) { + cname = smprint("%s/ctl", file); + if ((ctlfd = open(cname, ORDWR)) < 0) + sysfatal("open %s: %r", cname); + file = smprint("%s/data", file); + } + if((fd = open(file, rdonly? OREAD: ORDWR)) < 0) + sysfatal("open %s: %r", file); + + sectsize = 512; /* conventional */ + dir = dirfstat(fd); + if (dir) + nsect = dir->length / sectsize; + free(dir); + + inquiry = estrdup9p(inquiry); + tab[0].inuse = 1; + tab[0].name = estrdup9p("data"); + tab[0].mode = 0666; + tab[0].length = nsect; + + postmountsrv(&fs, srvname, mtpt, MBEFORE); + exits(nil); +} diff --git a/sys/src/cmd/disk/prep/calc.y b/sys/src/cmd/disk/prep/calc.y new file mode 100755 index 000000000..0829f09df --- /dev/null +++ b/sys/src/cmd/disk/prep/calc.y @@ -0,0 +1,200 @@ +%{ +typedef struct Exp Exp; +enum { + NUM, + DOT, + DOLLAR, + ADD, + SUB, + MUL, + DIV, + FRAC, + NEG, +}; + +struct Exp { + int ty; + long long n; + Exp *e1; + Exp *e2; +}; + +typedef Exp* Expptr; +#define YYSTYPE Expptr +Exp *yyexp; +%} + +%token NUMBER + +%left '+' '-' +%left '*' '/' +%left UNARYMINUS '%' +%% +top: expr { yyexp = $1; return 0; } + +expr: NUMBER + | '.' { $$ = mkOP(DOT, nil, nil); } + | '$' { $$ = mkOP(DOLLAR, nil, nil); } + | '(' expr ')' { $$ = $2; } + | expr '+' expr { $$ = mkOP(ADD, $1, $3); } + | expr '-' expr { $$ = mkOP(SUB, $1, $3); } + | expr '*' expr { $$ = mkOP(MUL, $1, $3); } + | expr '/' expr { $$ = mkOP(DIV, $1, $3); } + | expr '%' { $$ = mkOP(FRAC, $1, nil); } + | '-' expr %prec UNARYMINUS { $$ = mkOP(NEG, $2, nil); } + ; + +%% + +#include <u.h> +#include <libc.h> +#include <ctype.h> +#include "disk.h" +#include "edit.h" + +static Exp* +mkNUM(vlong x) +{ + Exp *n; + + n = emalloc(sizeof *n); + + n->ty = NUM; + n->n = x; + return n; +} + +static Exp* +mkOP(int ty, Exp *e1, Exp *e2) +{ + Exp *n; + + n = emalloc(sizeof *n); + n->ty = ty; + n->e1 = e1; + n->e2 = e2; + + return n; +} + +static char *inp; +static jmp_buf jmp; +static vlong dot, size, dollar; +static char** errp; + +static int +yylex(void) +{ + int c; + uvlong n; + + while(isspace(*inp)) + inp++; + + if(*inp == 0) + return 0; + + if(isdigit(*inp)) { + n = strtoull(inp, &inp, 0); /* default unit is sectors */ + c = *inp++; + if(isascii(c) && isupper(c)) + c = tolower(c); + switch(c) { + case 't': + n *= 1024; + /* fall through */ + case 'g': + n *= 1024; + /* fall through */ + case 'm': + n *= 1024; + /* fall through */ + case 'k': + n *= 2; + break; + default: + --inp; + break; + } + yylval = mkNUM(n); + return NUMBER; + } + return *inp++; +} + +static void +yyerror(char *s) +{ + *errp = s; + longjmp(jmp, 1); +} + +static vlong +eval(Exp *e) +{ + vlong i; + + switch(e->ty) { + case NUM: + return e->n; + case DOT: + return dot; + case DOLLAR: + return dollar; + case ADD: + return eval(e->e1)+eval(e->e2); + case SUB: + return eval(e->e1)-eval(e->e2); + case MUL: + return eval(e->e1)*eval(e->e2); + case DIV: + i = eval(e->e2); + if(i == 0) + yyerror("division by zero"); + return eval(e->e1)/i; + case FRAC: + return (size*eval(e->e1))/100; + case NEG: + return -eval(e->e1); + } + assert(0); + return 0; +} + +int yyparse(void); + +char* +parseexpr(char *s, vlong xdot, vlong xdollar, vlong xsize, vlong *result) +{ + char *err; + + errp = &err; + if(setjmp(jmp)) + return err; + + inp = s; + dot = xdot; + size = xsize; + dollar = xdollar; + yyparse(); + if(yyexp == nil) + return "nil yylval?"; + *result = eval(yyexp); + return nil; +} + +#ifdef TEST +void +main(int argc, char **argv) +{ + int i; + vlong r; + char *e; + + for(i=1; i<argc; i++) + if(e = parseexpr(argv[i], 1000, 1000000, 1000000, &r)) + print("%s\n", e); + else + print("%lld\n", r); +} +#endif diff --git a/sys/src/cmd/disk/prep/edit.c b/sys/src/cmd/disk/prep/edit.c new file mode 100755 index 000000000..8a96116fa --- /dev/null +++ b/sys/src/cmd/disk/prep/edit.c @@ -0,0 +1,551 @@ +/* + * disk partition editor + */ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <ctype.h> +#include <disk.h> +#include "edit.h" + +char* +getline(Edit *edit) +{ + static int inited; + static Biobuf bin; + char *p; + int n; + + if(!inited){ + Binit(&bin, 0, OREAD); + inited = 1; + } + p = Brdline(&bin, '\n'); + n = Blinelen(&bin); + if(p == nil || n < 1){ + if(edit->changed) + fprint(2, "?warning: changes not written\n"); + exits(0); + } + p[n - 1] = '\0'; + while(isspace(*p)) + p++; + return p; +} + +Part* +findpart(Edit *edit, char *name) +{ + int i; + + for(i=0; i<edit->npart; i++) + if(strcmp(edit->part[i]->name, name) == 0) + return edit->part[i]; + return nil; +} + +static char* +okname(Edit *edit, char *name) +{ + int i; + static char msg[100]; + + if(name[0] == '\0') + return "partition has no name"; + +// if(strlen(name) >= NAMELEN) +// return "name too long"; +// + for(i=0; i<edit->npart; i++) { + if(strcmp(name, edit->part[i]->name) == 0) { + sprint(msg, "already have partition with name \"%s\"", name); + return msg; + } + } + return nil; +} + +char* +addpart(Edit *edit, Part *p) +{ + int i; + static char msg[100]; + char *err; + + if(err = okname(edit, p->name)) + return err; + + for(i=0; i<edit->npart; i++) { + if(p->start < edit->part[i]->end && edit->part[i]->start < p->end) { + sprint(msg, "\"%s\" %lld-%lld overlaps with \"%s\" %lld-%lld", + p->name, p->start, p->end, + edit->part[i]->name, edit->part[i]->start, edit->part[i]->end); + // return msg; + } + } + + if(edit->npart >= nelem(edit->part)) + return "too many partitions"; + + edit->part[i=edit->npart++] = p; + for(; i > 0 && p->start < edit->part[i-1]->start; i--) { + edit->part[i] = edit->part[i-1]; + edit->part[i-1] = p; + } + + if(p->changed) + edit->changed = 1; + return nil; +} + +char* +delpart(Edit *edit, Part *p) +{ + int i; + + for(i=0; i<edit->npart; i++) + if(edit->part[i] == p) + break; + assert(i < edit->npart); + edit->npart--; + for(; i<edit->npart; i++) + edit->part[i] = edit->part[i+1]; + + edit->changed = 1; + return nil; +} + +static char* +editdot(Edit *edit, int argc, char **argv) +{ + char *err; + vlong ndot; + + if(argc == 1) { + print("\t. %lld\n", edit->dot); + return nil; + } + + if(argc > 2) + return "args"; + + if(err = parseexpr(argv[1], edit->dot, edit->end, edit->end, &ndot)) + return err; + + edit->dot = ndot; + return nil; +} + +static char* +editadd(Edit *edit, int argc, char **argv) +{ + char *name, *err, *q; + static char msg[100]; + vlong start, end, maxend; + int i; + + if(argc < 2) + return "args"; + + name = estrdup(argv[1]); + if((err = okname(edit, name)) || (err = edit->okname(edit, name))) + return err; + + if(argc >= 3) + q = argv[2]; + else { + fprint(2, "start %s: ", edit->unit); + q = getline(edit); + } + if(err = parseexpr(q, edit->dot, edit->end, edit->end, &start)) + return err; + + if(start < 0 || start >= edit->end) + return "start out of range"; + + for(i=0; i < edit->npart; i++) { + if(edit->part[i]->start <= start && start < edit->part[i]->end) { + sprint(msg, "start %s in partition \"%s\"", edit->unit, edit->part[i]->name); + return msg; + } + } + + maxend = edit->end; + for(i=0; i < edit->npart; i++) + if(start < edit->part[i]->start && edit->part[i]->start < maxend) + maxend = edit->part[i]->start; + + if(argc >= 4) + q = argv[3]; + else { + fprint(2, "end [%lld..%lld] ", start, maxend); + q = getline(edit); + } + if(err = parseexpr(q, edit->dot, maxend, edit->end, &end)) + return err; + + if(start == end) + return "size zero partition"; + + if(end <= start || end > maxend) + return "end out of range"; + + if(argc > 4) + return "args"; + + if(err = edit->add(edit, name, start, end)) + return err; + + edit->dot = end; + return nil; +} + +static char* +editdel(Edit *edit, int argc, char **argv) +{ + Part *p; + + if(argc != 2) + return "args"; + + if((p = findpart(edit, argv[1])) == nil) + return "no such partition"; + + return edit->del(edit, p); +} + +static char *helptext = + ". [newdot] - display or set value of dot\n" + "a name [start [end]] - add partition\n" + "d name - delete partition\n" + "h - print help message\n" + "p - print partition table\n" + "P - print commands to update sd(3) device\n" + "w - write partition table\n" + "q - quit\n"; + +static char* +edithelp(Edit *edit, int, char**) +{ + print("%s", helptext); + if(edit->help) + return edit->help(edit); + return nil; +} + +static char* +editprint(Edit *edit, int argc, char**) +{ + vlong lastend; + int i; + Part **part; + + if(argc != 1) + return "args"; + + lastend = 0; + part = edit->part; + for(i=0; i<edit->npart; i++) { + if(lastend < part[i]->start) + edit->sum(edit, nil, lastend, part[i]->start); + edit->sum(edit, part[i], part[i]->start, part[i]->end); + lastend = part[i]->end; + } + if(lastend < edit->end) + edit->sum(edit, nil, lastend, edit->end); + return nil; +} + +char* +editwrite(Edit *edit, int argc, char**) +{ + int i; + char *err; + + if(argc != 1) + return "args"; + + if(edit->disk->rdonly) + return "read only"; + + err = edit->write(edit); + if(err) + return err; + for(i=0; i<edit->npart; i++) + edit->part[i]->changed = 0; + edit->changed = 0; + return nil; +} + +static char* +editquit(Edit *edit, int argc, char**) +{ + static int warned; + + if(argc != 1) { + warned = 0; + return "args"; + } + + if(edit->changed && (!edit->warned || edit->lastcmd != 'q')) { + edit->warned = 1; + return "changes unwritten"; + } + + exits(0); + return nil; /* not reached */ +} + +char* +editctlprint(Edit *edit, int argc, char **) +{ + if(argc != 1) + return "args"; + + if(edit->printctl) + edit->printctl(edit, 1); + else + ctldiff(edit, 1); + return nil; +} + +typedef struct Cmd Cmd; +struct Cmd { + char c; + char *(*fn)(Edit*, int ,char**); +}; + +Cmd cmds[] = { + '.', editdot, + 'a', editadd, + 'd', editdel, + '?', edithelp, + 'h', edithelp, + 'P', editctlprint, + 'p', editprint, + 'w', editwrite, + 'q', editquit, +}; + +void +runcmd(Edit *edit, char *cmd) +{ + char *f[10], *err; + int i, nf; + + while(*cmd && isspace(*cmd)) + cmd++; + + nf = tokenize(cmd, f, nelem(f)); + if(nf >= 10) { + fprint(2, "?\n"); + return; + } + + if(nf < 1) + return; + if(strlen(f[0]) != 1) { + fprint(2, "?\n"); + return; + } + + err = nil; + for(i=0; i<nelem(cmds); i++) { + if(cmds[i].c == f[0][0]) { + err = cmds[i].fn(edit, nf, f); + break; + } + } + if(i == nelem(cmds)){ + if(edit->ext) + err = edit->ext(edit, nf, f); + else + err = "unknown command"; + } + if(err) + fprint(2, "?%s\n", err); + edit->lastcmd = f[0][0]; +} + +static Part* +ctlmkpart(char *name, vlong start, vlong end, int changed) +{ + Part *p; + + p = emalloc(sizeof(*p)); + p->name = estrdup(name); + p->ctlname = estrdup(name); + p->start = start; + p->end = end; + p->changed = changed; + return p; +} + +static void +rdctlpart(Edit *edit) +{ + int i, nline, nf; + char *line[128]; + char buf[4096]; + vlong a, b; + char *f[5]; + Disk *disk; + + disk = edit->disk; + edit->nctlpart = 0; + seek(disk->ctlfd, 0, 0); + if(readn(disk->ctlfd, buf, sizeof buf) <= 0) { + return; + } + + nline = getfields(buf, line, nelem(line), 1, "\n"); + for(i=0; i<nline; i++){ + if(strncmp(line[i], "part ", 5) != 0) + continue; + + nf = getfields(line[i], f, nelem(f), 1, " \t\r"); + if(nf != 4 || strcmp(f[0], "part") != 0) + break; + + a = strtoll(f[2], 0, 0); + b = strtoll(f[3], 0, 0); + + if(a >= b) + break; + + /* only gather partitions contained in the disk partition we are editing */ + if(a < disk->offset || disk->offset+disk->secs < b) + continue; + + a -= disk->offset; + b -= disk->offset; + + /* the partition we are editing does not count */ + if(strcmp(f[1], disk->part) == 0) + continue; + + if(edit->nctlpart >= nelem(edit->ctlpart)) { + fprint(2, "?too many partitions in ctl file\n"); + exits("ctlpart"); + } + edit->ctlpart[edit->nctlpart++] = ctlmkpart(f[1], a, b, 0); + } +} + +static vlong +ctlstart(Part *p) +{ + if(p->ctlstart) + return p->ctlstart; + return p->start; +} + +static vlong +ctlend(Part *p) +{ + if(p->ctlend) + return p->ctlend; + return p->end; +} + +static int +areequiv(Part *p, Part *q) +{ + if(p->ctlname[0]=='\0' || q->ctlname[0]=='\0') + return 0; + + return strcmp(p->ctlname, q->ctlname) == 0 + && ctlstart(p) == ctlstart(q) && ctlend(p) == ctlend(q); +} + +static void +unchange(Edit *edit, Part *p) +{ + int i; + Part *q; + + for(i=0; i<edit->nctlpart; i++) { + q = edit->ctlpart[i]; + if(p->start <= q->start && q->end <= p->end) { + q->changed = 0; + } + } +assert(p->changed == 0); +} + +int +ctldiff(Edit *edit, int ctlfd) +{ + int i, j, waserr; + Part *p; + vlong offset; + + rdctlpart(edit); + + /* everything is bogus until we prove otherwise */ + for(i=0; i<edit->nctlpart; i++) + edit->ctlpart[i]->changed = 1; + + /* + * partitions with same info have not changed, + * and neither have partitions inside them. + */ + for(i=0; i<edit->nctlpart; i++) + for(j=0; j<edit->npart; j++) + if(areequiv(edit->ctlpart[i], edit->part[j])) { + unchange(edit, edit->ctlpart[i]); + break; + } + + waserr = 0; + /* + * delete all the changed partitions except data (we'll add them back if necessary) + */ + for(i=0; i<edit->nctlpart; i++) { + p = edit->ctlpart[i]; + if(p->changed) + if(fprint(ctlfd, "delpart %s\n", p->ctlname)<0) { + fprint(2, "delpart failed: %s: %r\n", p->ctlname); + waserr = -1; + } + } + + /* + * add all the partitions from the real list; + * this is okay since adding a parition with + * information identical to what is there is a no-op. + */ + offset = edit->disk->offset; + for(i=0; i<edit->npart; i++) { + p = edit->part[i]; + if(p->ctlname[0]) { + if(fprint(ctlfd, "part %s %lld %lld\n", p->ctlname, offset+ctlstart(p), offset+ctlend(p)) < 0) { + fprint(2, "adding part failed: %s: %r\n", p->ctlname); + waserr = -1; + } + } + } + return waserr; +} + +void* +emalloc(ulong sz) +{ + void *v; + + v = malloc(sz); + if(v == nil) + sysfatal("malloc %lud fails", sz); + memset(v, 0, sz); + return v; +} + +char* +estrdup(char *s) +{ + s = strdup(s); + if(s == nil) + sysfatal("strdup (%.10s) fails", s); + return s; +} + diff --git a/sys/src/cmd/disk/prep/edit.h b/sys/src/cmd/disk/prep/edit.h new file mode 100755 index 000000000..8751adc4f --- /dev/null +++ b/sys/src/cmd/disk/prep/edit.h @@ -0,0 +1,54 @@ +typedef struct Part Part; +struct Part { + char *name; + char *ctlname; + vlong start; + vlong end; + vlong ctlstart; + vlong ctlend; + int changed; +}; + +enum { + Maxpart = 32 +}; + +typedef struct Edit Edit; +struct Edit { + Disk *disk; + + Part *ctlpart[Maxpart]; + int nctlpart; + + Part *part[Maxpart]; + int npart; + + char *(*add)(Edit*, char*, vlong, vlong); + char *(*del)(Edit*, Part*); + char *(*ext)(Edit*, int, char**); + char *(*help)(Edit*); + char *(*okname)(Edit*, char*); + void (*sum)(Edit*, Part*, vlong, vlong); + char *(*write)(Edit*); + void (*printctl)(Edit*, int); + + char *unit; + void *aux; + vlong dot; + vlong end; + + /* do not use fields below this line */ + int changed; + int warned; + int lastcmd; +}; + +char *getline(Edit*); +void runcmd(Edit*, char*); +Part *findpart(Edit*, char*); +char *addpart(Edit*, Part*); +char *delpart(Edit*, Part*); +char *parseexpr(char *s, vlong xdot, vlong xdollar, vlong xsize, vlong *result); +int ctldiff(Edit *edit, int ctlfd); +void *emalloc(ulong); +char *estrdup(char*); diff --git a/sys/src/cmd/disk/prep/fdisk.c b/sys/src/cmd/disk/prep/fdisk.c new file mode 100755 index 000000000..6903d5adc --- /dev/null +++ b/sys/src/cmd/disk/prep/fdisk.c @@ -0,0 +1,1118 @@ +/* + * fdisk - edit dos disk partition table + */ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <ctype.h> +#include <disk.h> +#include "edit.h" + +typedef struct Dospart Dospart; +enum { + NTentry = 4, + Mpart = 64, +}; + +static void rdpart(Edit*, uvlong, uvlong); +static void findmbr(Edit*); +static void autopart(Edit*); +static void wrpart(Edit*); +static void blankpart(Edit*); +static void cmdnamectl(Edit*); +static void recover(Edit*); +static int Dfmt(Fmt*); +static int blank; +static int dowrite; +static int file; +static int rdonly; +static int doauto; +static vlong mbroffset; +static int printflag; +static int printchs; +static int sec2cyl; +static int written; + +static void cmdsum(Edit*, Part*, vlong, vlong); +static char *cmdadd(Edit*, char*, vlong, vlong); +static char *cmddel(Edit*, Part*); +static char *cmdext(Edit*, int, char**); +static char *cmdhelp(Edit*); +static char *cmdokname(Edit*, char*); +static char *cmdwrite(Edit*); +static void cmdprintctl(Edit*, int); + +#pragma varargck type "D" uchar* + +Edit edit = { + .add= cmdadd, + .del= cmddel, + .ext= cmdext, + .help= cmdhelp, + .okname= cmdokname, + .sum= cmdsum, + .write= cmdwrite, + .printctl= cmdprintctl, + + .unit= "cylinder", +}; + +/* + * Catch the obvious error routines to fix up the disk. + */ +void +sysfatal(char *fmt, ...) +{ + char buf[1024]; + va_list arg; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + if(argv0) + fprint(2, "%s: %s\n", argv0, buf); + else + fprint(2, "%s\n", buf); + + if(written) + recover(&edit); + + exits(buf); +} + +void +abort(void) +{ + fprint(2, "abort\n"); + recover(&edit); +} + +void +usage(void) +{ + fprint(2, "usage: disk/fdisk [-abfprvw] [-s sectorsize] /dev/sdC0/data\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + vlong secsize; + + secsize = 0; + ARGBEGIN{ + case 'a': + doauto++; + break; + case 'b': + blank++; + break; + case 'f': + file++; + break; + case 'p': + printflag++; + break; + case 'r': + rdonly++; + break; + case 's': + secsize = atoi(ARGF()); + break; + case 'v': + printchs++; + break; + case 'w': + dowrite++; + break; + }ARGEND; + + fmtinstall('D', Dfmt); + + if(argc != 1) + usage(); + + edit.disk = opendisk(argv[0], rdonly, file); + if(edit.disk == nil) { + fprint(2, "cannot open disk: %r\n"); + exits("opendisk"); + } + + if(secsize != 0) { + edit.disk->secsize = secsize; + edit.disk->secs = edit.disk->size / secsize; + } + + sec2cyl = edit.disk->h * edit.disk->s; + edit.end = edit.disk->secs / sec2cyl; + + findmbr(&edit); + + if(blank) + blankpart(&edit); + else + rdpart(&edit, 0, 0); + + if(doauto) + autopart(&edit); + + if(dowrite) + runcmd(&edit, "w"); + + if(printflag) + runcmd(&edit, "P"); + + if(dowrite || printflag) + exits(0); + + fprint(2, "cylinder = %lld bytes\n", sec2cyl*edit.disk->secsize); + runcmd(&edit, "p"); + for(;;) { + fprint(2, ">>> "); + runcmd(&edit, getline(&edit)); + } +} + +typedef struct Tentry Tentry; +typedef struct Table Table; +typedef struct Type Type; +typedef struct Tab Tab; +typedef struct Recover Recover; + +struct Tentry { + uchar active; /* active flag */ + uchar starth; /* starting head */ + uchar starts; /* starting sector */ + uchar startc; /* starting cylinder */ + uchar type; /* partition type */ + uchar endh; /* ending head */ + uchar ends; /* ending sector */ + uchar endc; /* ending cylinder */ + uchar xlba[4]; /* starting LBA from beginning of disc or ext. partition */ + uchar xsize[4]; /* size in sectors */ +}; + +enum { + Active = 0x80, /* partition is active */ + Primary = 0x01, /* internal flag */ + + TypeBB = 0xFF, + + TypeEMPTY = 0x00, + TypeFAT12 = 0x01, + TypeXENIX = 0x02, /* root */ + TypeXENIXUSR = 0x03, /* usr */ + TypeFAT16 = 0x04, + TypeEXTENDED = 0x05, + TypeFATHUGE = 0x06, + TypeHPFS = 0x07, + TypeAIXBOOT = 0x08, + TypeAIXDATA = 0x09, + TypeOS2BOOT = 0x0A, /* OS/2 Boot Manager */ + TypeFAT32 = 0x0B, /* FAT 32 */ + TypeFAT32LBA = 0x0C, /* FAT 32 needing LBA support */ + TypeFAT16X = 0x0E, /* FAT 16 needing LBA support */ + TypeEXTHUGE = 0x0F, /* FAT 32 extended partition */ + TypeUNFORMATTED = 0x16, /* unformatted primary partition (OS/2 FDISK)? */ + TypeHPFS2 = 0x17, + TypeIBMRecovery = 0x1C, /* really hidden fat */ + TypeCPM0 = 0x52, + TypeDMDDO = 0x54, /* Disk Manager Dynamic Disk Overlay */ + TypeGB = 0x56, /* ???? */ + TypeSPEEDSTOR = 0x61, + TypeSYSV386 = 0x63, /* also HURD? */ + TypeNETWARE = 0x64, + TypePCIX = 0x75, + TypeMINIX13 = 0x80, /* Minix v1.3 and below */ + TypeMINIX = 0x81, /* Minix v1.5+ */ + TypeLINUXSWAP = 0x82, + TypeLINUX = 0x83, + TypeLINUXEXT = 0x85, + TypeLINUXLVM = 0x8E, /* logical volume manager */ + TypeAMOEBA = 0x93, + TypeAMOEBABB = 0x94, + TypeBSD386 = 0xA5, + TypeNETBSD = 0XA9, + TypeBSDI = 0xB7, + TypeBSDISWAP = 0xB8, + TypeOTHER = 0xDA, + TypeCPM = 0xDB, + TypeDellRecovery= 0xDE, + TypeSPEEDSTOR12 = 0xE1, + TypeSPEEDSTOR16 = 0xE4, + TypeLANSTEP = 0xFE, + + Type9 = 0x39, + + Toffset = 446, /* offset of partition table in sector */ + Magic0 = 0x55, + Magic1 = 0xAA, +}; + +struct Table { + Tentry entry[NTentry]; + uchar magic[2]; +}; + +struct Type { + char *desc; + char *name; +}; + +struct Dospart { + Part; + Tentry; + + u32int lba; + u32int size; + int primary; +}; + +struct Recover { + Table table; + ulong lba; +}; + +static Type types[256] = { + [TypeEMPTY] { "EMPTY", "" }, + [TypeFAT12] { "FAT12", "dos" }, + [TypeFAT16] { "FAT16", "dos" }, + [TypeFAT32] { "FAT32", "dos" }, + [TypeFAT32LBA] { "FAT32LBA", "dos" }, + [TypeFAT16X] { "FAT16X", "dos" }, + [TypeEXTHUGE] { "EXTHUGE", "" }, + [TypeIBMRecovery] { "IBMRECOVERY", "ibm" }, + [TypeEXTENDED] { "EXTENDED", "" }, + [TypeFATHUGE] { "FATHUGE", "dos" }, + [TypeBB] { "BB", "bb" }, + + [TypeXENIX] { "XENIX", "xenix" }, + [TypeXENIXUSR] { "XENIX USR", "xenixusr" }, + [TypeHPFS] { "HPFS", "ntfs" }, + [TypeAIXBOOT] { "AIXBOOT", "aixboot" }, + [TypeAIXDATA] { "AIXDATA", "aixdata" }, + [TypeOS2BOOT] { "OS/2BOOT", "os2boot" }, + [TypeUNFORMATTED] { "UNFORMATTED", "" }, + [TypeHPFS2] { "HPFS2", "hpfs2" }, + [TypeCPM0] { "CPM0", "cpm0" }, + [TypeDMDDO] { "DMDDO", "dmdd0" }, + [TypeGB] { "GB", "gb" }, + [TypeSPEEDSTOR] { "SPEEDSTOR", "speedstor" }, + [TypeSYSV386] { "SYSV386", "sysv386" }, + [TypeNETWARE] { "NETWARE", "netware" }, + [TypePCIX] { "PCIX", "pcix" }, + [TypeMINIX13] { "MINIXV1.3", "minix13" }, + [TypeMINIX] { "MINIXV1.5", "minix15" }, + [TypeLINUXSWAP] { "LINUXSWAP", "linuxswap" }, + [TypeLINUX] { "LINUX", "linux" }, + [TypeLINUXEXT] { "LINUXEXTENDED", "" }, + [TypeLINUXLVM] { "LINUXLVM", "linuxlvm" }, + [TypeAMOEBA] { "AMOEBA", "amoeba" }, + [TypeAMOEBABB] { "AMOEBABB", "amoebaboot" }, + [TypeBSD386] { "BSD386", "bsd386" }, + [TypeNETBSD] { "NETBSD", "netbsd" }, + [TypeBSDI] { "BSDI", "bsdi" }, + [TypeBSDISWAP] { "BSDISWAP", "bsdiswap" }, + [TypeOTHER] { "OTHER", "other" }, + [TypeCPM] { "CPM", "cpm" }, + [TypeDellRecovery] { "DELLRECOVERY", "dell" }, + [TypeSPEEDSTOR12] { "SPEEDSTOR12", "speedstor" }, + [TypeSPEEDSTOR16] { "SPEEDSTOR16", "speedstor" }, + [TypeLANSTEP] { "LANSTEP", "lanstep" }, + + [Type9] { "PLAN9", "plan9" }, +}; + +static Dospart part[Mpart]; +static int npart; + +static char* +typestr0(int type) +{ + static char buf[100]; + + sprint(buf, "type %d", type); + if(type < 0 || type >= 256) + return buf; + if(types[type].desc == nil) + return buf; + return types[type].desc; +} + +static u32int +getle32(void* v) +{ + uchar *p; + + p = v; + return (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]; +} + +static void +putle32(void* v, u32int i) +{ + uchar *p; + + p = v; + p[0] = i; + p[1] = i>>8; + p[2] = i>>16; + p[3] = i>>24; +} + +static void +diskread(Disk *disk, void *data, int ndata, u32int sec, u32int off) +{ + if(seek(disk->fd, (vlong)sec*disk->secsize+off, 0) != (vlong)sec*disk->secsize+off) + sysfatal("diskread seek %lud.%lud: %r", (ulong)sec, (ulong)off); + if(readn(disk->fd, data, ndata) != ndata) + sysfatal("diskread %lud at %lud.%lud: %r", (ulong)ndata, (ulong)sec, (ulong)off); +} + +static int +diskwrite(Disk *disk, void *data, int ndata, u32int sec, u32int off) +{ + written = 1; + if(seek(disk->wfd, (vlong)sec*disk->secsize+off, 0) != (vlong)sec*disk->secsize+off) + goto Error; + if(write(disk->wfd, data, ndata) != ndata) + goto Error; + return 0; + +Error: + fprint(2, "write %d bytes at %lud.%lud failed: %r\n", ndata, (ulong)sec, (ulong)off); + return -1; +} + +static Dospart* +mkpart(char *name, int primary, vlong lba, vlong size, Tentry *t) +{ + static int n; + Dospart *p; + + p = emalloc(sizeof(*p)); + if(name) + p->name = estrdup(name); + else{ + p->name = emalloc(20); + sprint(p->name, "%c%d", primary ? 'p' : 's', ++n); + } + + if(t) + p->Tentry = *t; + else + memset(&p->Tentry, 0, sizeof(Tentry)); + + p->changed = 0; + p->start = lba/sec2cyl; + p->end = (lba+size)/sec2cyl; + p->ctlstart = lba; + p->ctlend = lba+size; + p->lba = lba; + if (p->lba != lba) + fprint(2, "%s: start of partition (%lld) won't fit in MBR table\n", argv0, lba); + p->size = size; + if (p->size != size) + fprint(2, "%s: size of partition (%lld) won't fit in MBR table\n", argv0, size); + p->primary = primary; + return p; +} + +/* + * Recovery takes care of remembering what the various tables + * looked like when we started, attempting to restore them when + * we are finished. + */ +static Recover *rtab; +static int nrtab; + +static void +addrecover(Table t, ulong lba) +{ + if((nrtab%8) == 0) { + rtab = realloc(rtab, (nrtab+8)*sizeof(rtab[0])); + if(rtab == nil) + sysfatal("out of memory"); + } + rtab[nrtab] = (Recover){t, lba}; + nrtab++; +} + +static void +recover(Edit *edit) +{ + int err, i, ctlfd; + vlong offset; + + err = 0; + for(i=0; i<nrtab; i++) + if(diskwrite(edit->disk, &rtab[i].table, sizeof(Table), rtab[i].lba, Toffset) < 0) + err = 1; + if(err) { + fprint(2, "warning: some writes failed during restoration of old partition tables\n"); + exits("inconsistent"); + } else { + fprint(2, "restored old partition tables\n"); + } + + ctlfd = edit->disk->ctlfd; + offset = edit->disk->offset; + if(ctlfd >= 0){ + for(i=0; i<edit->npart; i++) + if(edit->part[i]->ctlname && fprint(ctlfd, "delpart %s", edit->part[i]->ctlname)<0) + fprint(2, "delpart failed: %s: %r", edit->part[i]->ctlname); + for(i=0; i<edit->nctlpart; i++) + if(edit->part[i]->name && fprint(ctlfd, "delpart %s", edit->ctlpart[i]->name)<0) + fprint(2, "delpart failed: %s: %r", edit->ctlpart[i]->name); + for(i=0; i<edit->nctlpart; i++){ + if(fprint(ctlfd, "part %s %lld %lld", edit->ctlpart[i]->name, + edit->ctlpart[i]->start+offset, edit->ctlpart[i]->end+offset) < 0){ + fprint(2, "restored disk partition table but not kernel; reboot\n"); + exits("inconsistent"); + } + } + } + exits("restored"); + +} + +/* + * Read the partition table (including extended partition tables) + * from the disk into the part array. + */ +static void +rdpart(Edit *edit, uvlong lba, uvlong xbase) +{ + char *err; + Table table; + Tentry *tp, *ep; + Dospart *p; + + if(xbase == 0) + xbase = lba; + + diskread(edit->disk, &table, sizeof table, mbroffset+lba, Toffset); + addrecover(table, mbroffset+lba); + + if(table.magic[0] != Magic0 || table.magic[1] != Magic1) { + assert(lba != 0); + return; + } + + for(tp=table.entry, ep=tp+NTentry; tp<ep && npart < Mpart; tp++) { + switch(tp->type) { + case TypeEMPTY: + break; + case TypeEXTENDED: + case TypeEXTHUGE: + case TypeLINUXEXT: + rdpart(edit, xbase+getle32(tp->xlba), xbase); + break; + default: + p = mkpart(nil, lba==0, lba+getle32(tp->xlba), getle32(tp->xsize), tp); + if(err = addpart(edit, p)) + fprint(2, "adding partition: %s\n", err); + break; + } + } +} + +static void +blankpart(Edit *edit) +{ + edit->changed = 1; +} + +static void +findmbr(Edit *edit) +{ + Table table; + Tentry *tp; + + diskread(edit->disk, &table, sizeof(Table), 0, Toffset); + if(table.magic[0] != Magic0 || table.magic[1] != Magic1) + sysfatal("did not find master boot record"); + + for(tp = table.entry; tp < &table.entry[NTentry]; tp++) + if(tp->type == TypeDMDDO) + mbroffset = edit->disk->s; +} + +static int +haveroom(Edit *edit, int primary, vlong start) +{ + int i, lastsec, n; + Dospart *p, *q; + ulong pend, qstart; + + if(primary) { + /* + * must be open primary slot. + * primary slots are taken by primary partitions + * and runs of secondary partitions. + */ + n = 0; + lastsec = 0; + for(i=0; i<edit->npart; i++) { + p = (Dospart*)edit->part[i]; + if(p->primary) + n++, lastsec=0; + else if(!lastsec) + n++, lastsec=1; + } + return n<4; + } + + /* + * secondary partitions can be inserted between two primary + * partitions only if there is an empty primary slot. + * otherwise, we can put a new secondary partition next + * to a secondary partition no problem. + */ + n = 0; + for(i=0; i<edit->npart; i++){ + p = (Dospart*)edit->part[i]; + if(p->primary) + n++; + pend = p->end; + if(i+1<edit->npart){ + q = (Dospart*)edit->part[i+1]; + qstart = q->start; + }else{ + qstart = edit->end; + q = nil; + } + if(start < pend || start >= qstart) + continue; + /* we go between these two */ + if(p->primary==0 || (q && q->primary==0)) + return 1; + } + /* not next to a secondary, need a new primary */ + return n<4; +} + +static void +autopart(Edit *edit) +{ + char *err; + int active, i; + vlong bigstart, bigsize, start; + Dospart *p; + + for(i=0; i<edit->npart; i++) + if(((Dospart*)edit->part[i])->type == Type9) + return; + + /* look for the biggest gap in which we can put a primary partition */ + start = 0; + bigsize = 0; + SET(bigstart); + for(i=0; i<edit->npart; i++) { + p = (Dospart*)edit->part[i]; + if(p->start > start && p->start - start > bigsize && haveroom(edit, 1, start)) { + bigsize = p->start - start; + bigstart = start; + } + start = p->end; + } + + if(edit->end - start > bigsize && haveroom(edit, 1, start)) { + bigsize = edit->end - start; + bigstart = start; + } + if(bigsize < 1) { + fprint(2, "couldn't find space or partition slot for plan 9 partition\n"); + return; + } + + /* set new partition active only if no others are */ + active = Active; + for(i=0; i<edit->npart; i++) + if(((Dospart*)edit->part[i])->primary && (((Dospart*)edit->part[i])->active & Active)) + active = 0; + + /* add new plan 9 partition */ + bigsize *= sec2cyl; + bigstart *= sec2cyl; + if(bigstart == 0) { + bigstart += edit->disk->s; + bigsize -= edit->disk->s; + } + p = mkpart(nil, 1, bigstart, bigsize, nil); + p->active = active; + p->changed = 1; + p->type = Type9; + edit->changed = 1; + if(err = addpart(edit, p)) { + fprint(2, "error adding plan9 partition: %s\n", err); + return; + } +} + +typedef struct Name Name; +struct Name { + char *name; + Name *link; +}; +Name *namelist; +static void +plan9print(Dospart *part, int fd) +{ + int i, ok; + char *name, *vname; + Name *n; + vlong start, end; + char *sep; + + vname = types[part->type].name; + if(vname==nil || strcmp(vname, "")==0) { + part->ctlname = ""; + return; + } + + start = mbroffset+part->lba; + end = start+part->size; + + /* avoid names like plan90 */ + i = strlen(vname) - 1; + if(vname[i] >= '0' && vname[i] <= '9') + sep = "."; + else + sep = ""; + + i = 0; + name = emalloc(strlen(vname)+10); + + sprint(name, "%s", vname); + do { + ok = 1; + for(n=namelist; n; n=n->link) { + if(strcmp(name, n->name) == 0) { + i++; + sprint(name, "%s%s%d", vname, sep, i); + ok = 0; + } + } + } while(ok == 0); + + n = emalloc(sizeof(*n)); + n->name = name; + n->link = namelist; + namelist = n; + part->ctlname = name; + + if(fd >= 0) + print("part %s %lld %lld\n", name, start, end); +} + +static void +freenamelist(void) +{ + Name *n, *next; + + for(n=namelist; n; n=next) { + next = n->link; + free(n); + } + namelist = nil; +} + +static void +cmdprintctl(Edit *edit, int ctlfd) +{ + int i; + + freenamelist(); + for(i=0; i<edit->npart; i++) + plan9print((Dospart*)edit->part[i], -1); + ctldiff(edit, ctlfd); +} + +static char* +cmdokname(Edit*, char *name) +{ + char *q; + + if(name[0] != 'p' && name[0] != 's') + return "name must be pN or sN"; + + strtol(name+1, &q, 10); + if(*q != '\0') + return "name must be pN or sN"; + + return nil; +} + +#define TB (1024LL*GB) +#define GB (1024*1024*1024) +#define MB (1024*1024) +#define KB (1024) + +static void +cmdsum(Edit *edit, Part *vp, vlong a, vlong b) +{ + char *name, *ty; + char buf[3]; + char *suf; + Dospart *p; + vlong sz, div; + + p = (Dospart*)vp; + + buf[0] = p && p->changed ? '\'' : ' '; + buf[1] = p && (p->active & Active) ? '*' : ' '; + buf[2] = '\0'; + + name = p ? p->name : "empty"; + ty = p ? typestr0(p->type) : ""; + + sz = (b-a)*edit->disk->secsize*sec2cyl; + if(sz >= 1*TB){ + suf = "TB"; + div = TB; + }else if(sz >= 1*GB){ + suf = "GB"; + div = GB; + }else if(sz >= 1*MB){ + suf = "MB"; + div = MB; + }else if(sz >= 1*KB){ + suf = "KB"; + div = KB; + }else{ + suf = "B "; + div = 1; + } + + if(div == 1) + print("%s %-12s %*lld %-*lld (%lld cylinders, %lld %s) %s\n", buf, name, + edit->disk->width, a, edit->disk->width, b, b-a, sz, suf, ty); + else + print("%s %-12s %*lld %-*lld (%lld cylinders, %lld.%.2d %s) %s\n", buf, name, + edit->disk->width, a, edit->disk->width, b, b-a, + sz/div, (int)(((sz%div)*100)/div), suf, ty); +} + +static char* +cmdadd(Edit *edit, char *name, vlong start, vlong end) +{ + Dospart *p; + + if(!haveroom(edit, name[0]=='p', start)) + return "no room for partition"; + start *= sec2cyl; + end *= sec2cyl; + if(start == 0 || name[0] != 'p') + start += edit->disk->s; + p = mkpart(name, name[0]=='p', start, end-start, nil); + p->changed = 1; + p->type = Type9; + return addpart(edit, p); +} + +static char* +cmddel(Edit *edit, Part *p) +{ + return delpart(edit, p); +} + +static char* +cmdwrite(Edit *edit) +{ + wrpart(edit); + return nil; +} + +static char *help = + "A name - set partition active\n" + "P - print table in ctl format\n" + "R - restore disk back to initial configuration and exit\n" + "e - show empty dos partitions\n" + "t name [type] - set partition type\n"; + +static char* +cmdhelp(Edit*) +{ + print("%s\n", help); + return nil; +} + +static char* +cmdactive(Edit *edit, int nf, char **f) +{ + int i; + Dospart *p, *ip; + + if(nf != 2) + return "args"; + + if(f[1][0] != 'p') + return "cannot set secondary partition active"; + + if((p = (Dospart*)findpart(edit, f[1])) == nil) + return "unknown partition"; + + for(i=0; i<edit->npart; i++) { + ip = (Dospart*)edit->part[i]; + if(ip->active & Active) { + ip->active &= ~Active; + ip->changed = 1; + edit->changed = 1; + } + } + + if((p->active & Active) == 0) { + p->active |= Active; + p->changed = 1; + edit->changed = 1; + } + + return nil; +} + +static char* +strupr(char *s) +{ + char *p; + + for(p=s; *p; p++) + *p = toupper(*p); + return s; +} + +static void +dumplist(void) +{ + int i, n; + + n = 0; + for(i=0; i<256; i++) { + if(types[i].desc) { + print("%-16s", types[i].desc); + if(n++%4 == 3) + print("\n"); + } + } + if(n%4) + print("\n"); +} + +static char* +cmdtype(Edit *edit, int nf, char **f) +{ + char *q; + Dospart *p; + int i; + + if(nf < 2) + return "args"; + + if((p = (Dospart*)findpart(edit, f[1])) == nil) + return "unknown partition"; + + if(nf == 2) { + for(;;) { + fprint(2, "new partition type [? for list]: "); + q = getline(edit); + if(q[0] == '?') + dumplist(); + else + break; + } + } else + q = f[2]; + + strupr(q); + for(i=0; i<256; i++) + if(types[i].desc && strcmp(types[i].desc, q) == 0) + break; + if(i < 256 && p->type != i) { + p->type = i; + p->changed = 1; + edit->changed = 1; + } + return nil; +} + +static char* +cmdext(Edit *edit, int nf, char **f) +{ + switch(f[0][0]) { + case 'A': + return cmdactive(edit, nf, f); + case 't': + return cmdtype(edit, nf, f); + case 'R': + recover(edit); + return nil; + default: + return "unknown command"; + } +} + +static int +Dfmt(Fmt *f) +{ + char buf[60]; + uchar *p; + int c, h, s; + + p = va_arg(f->args, uchar*); + h = p[0]; + c = p[2]; + c |= (p[1]&0xC0)<<2; + s = (p[1] & 0x3F); + + sprint(buf, "%d/%d/%d", c, h, s); + return fmtstrcpy(f, buf); +} + +static void +writechs(Disk *disk, uchar *p, vlong lba) +{ + int c, h, s; + + s = lba % disk->s; + h = (lba / disk->s) % disk->h; + c = lba / (disk->s * disk->h); + + if(c >= 1024) { + c = 1023; + h = disk->h - 1; + s = disk->s - 1; + } + + p[0] = h; + p[1] = ((s+1) & 0x3F) | ((c>>2) & 0xC0); + p[2] = c; +} + +static void +wrtentry(Disk *disk, Tentry *tp, int type, u32int xbase, u32int lba, u32int end) +{ + tp->type = type; + writechs(disk, &tp->starth, lba); + writechs(disk, &tp->endh, end-1); + putle32(tp->xlba, lba-xbase); + putle32(tp->xsize, end-lba); +} + +static int +wrextend(Edit *edit, int i, vlong xbase, vlong startlba, vlong *endlba) +{ + int ni; + Table table; + Tentry *tp, *ep; + Dospart *p; + Disk *disk; + + if(i == edit->npart){ + *endlba = edit->disk->secs; + Finish: + if(startlba < *endlba){ + disk = edit->disk; + diskread(disk, &table, sizeof table, mbroffset+startlba, Toffset); + tp = table.entry; + ep = tp+NTentry; + for(; tp<ep; tp++) + memset(tp, 0, sizeof *tp); + table.magic[0] = Magic0; + table.magic[1] = Magic1; + + if(diskwrite(edit->disk, &table, sizeof table, mbroffset+startlba, Toffset) < 0) + recover(edit); + } + return i; + } + + p = (Dospart*)edit->part[i]; + if(p->primary){ + *endlba = (vlong)p->start*sec2cyl; + goto Finish; + } + + disk = edit->disk; + diskread(disk, &table, sizeof table, mbroffset+startlba, Toffset); + tp = table.entry; + ep = tp+NTentry; + + ni = wrextend(edit, i+1, xbase, p->end*sec2cyl, endlba); + + *tp = p->Tentry; + wrtentry(disk, tp, p->type, startlba, startlba+disk->s, p->end*sec2cyl); + tp++; + + if(p->end*sec2cyl != *endlba){ + memset(tp, 0, sizeof *tp); + wrtentry(disk, tp, TypeEXTENDED, xbase, p->end*sec2cyl, *endlba); + tp++; + } + + for(; tp<ep; tp++) + memset(tp, 0, sizeof *tp); + + table.magic[0] = Magic0; + table.magic[1] = Magic1; + + if(diskwrite(edit->disk, &table, sizeof table, mbroffset+startlba, Toffset) < 0) + recover(edit); + return ni; +} + +static void +wrpart(Edit *edit) +{ + int i, ni, t; + Table table; + Tentry *tp, *ep; + Disk *disk; + vlong s, endlba; + Dospart *p; + + disk = edit->disk; + + diskread(disk, &table, sizeof table, mbroffset, Toffset); + + tp = table.entry; + ep = tp+NTentry; + for(i=0; i<edit->npart && tp<ep; ) { + p = (Dospart*)edit->part[i]; + if(p->start == 0) + s = disk->s; + else + s = p->start*sec2cyl; + if(p->primary) { + *tp = p->Tentry; + wrtentry(disk, tp, p->type, 0, s, p->end*sec2cyl); + tp++; + i++; + } else { + ni = wrextend(edit, i, p->start*sec2cyl, p->start*sec2cyl, &endlba); + memset(tp, 0, sizeof *tp); + if(endlba >= 1024*sec2cyl) + t = TypeEXTHUGE; + else + t = TypeEXTENDED; + wrtentry(disk, tp, t, 0, s, endlba); + tp++; + i = ni; + } + } + for(; tp<ep; tp++) + memset(tp, 0, sizeof(*tp)); + + if(i != edit->npart) + sysfatal("cannot happen #1"); + + if(diskwrite(disk, &table, sizeof table, mbroffset, Toffset) < 0) + recover(edit); + + /* bring parts up to date */ + freenamelist(); + for(i=0; i<edit->npart; i++) + plan9print((Dospart*)edit->part[i], -1); + + if(ctldiff(edit, disk->ctlfd) < 0) + fprint(2, "?warning: partitions could not be updated in devsd\n"); +} diff --git a/sys/src/cmd/disk/prep/mkfile b/sys/src/cmd/disk/prep/mkfile new file mode 100755 index 000000000..5557c9b59 --- /dev/null +++ b/sys/src/cmd/disk/prep/mkfile @@ -0,0 +1,22 @@ +</$objtype/mkfile + +TARG=fdisk prep + +YFILES=calc.y +HFILES=edit.h +OFILES=\ + edit.$O\ + y.tab.$O\ + +BIN=/$objtype/bin/disk + +UPDATE=\ + mkfile\ + /sys/man/8/prep\ + edit.c\ + ${TARG:%=%.c}\ + $HFILES\ + $YFILES\ + ${TARG:%=/386/bin/disk/%}\ + +</sys/src/cmd/mkmany diff --git a/sys/src/cmd/disk/prep/prep.c b/sys/src/cmd/disk/prep/prep.c new file mode 100755 index 000000000..b7848188d --- /dev/null +++ b/sys/src/cmd/disk/prep/prep.c @@ -0,0 +1,523 @@ +/* + * prep - prepare plan9 disk partition + */ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <disk.h> +#include "edit.h" + +enum { + Maxpath = 128, +}; + +static int blank; +static int file; +static int doautox; +static int printflag; +static Part **opart; +static int nopart; +static char *osecbuf; +static char *secbuf; +static int rdonly; +static int dowrite; +static int docache; +static int donvram; + +static void autoxpart(Edit*); +static Part *mkpart(char*, vlong, vlong, int); +static void rdpart(Edit*); +static void wrpart(Edit*); +static void checkfat(Disk*); + +static void cmdsum(Edit*, Part*, vlong, vlong); +static char *cmdadd(Edit*, char*, vlong, vlong); +static char *cmddel(Edit*, Part*); +static char *cmdokname(Edit*, char*); +static char *cmdwrite(Edit*); +static char *cmdctlprint(Edit*, int, char**); + +Edit edit = { + .add= cmdadd, + .del= cmddel, + .okname=cmdokname, + .sum= cmdsum, + .write= cmdwrite, + + .unit= "sector", +}; + +typedef struct Auto Auto; +struct Auto +{ + char *name; + uvlong min; + uvlong max; + uint weight; + uchar alloc; + uvlong size; +}; + +#define TB (1024LL*GB) +#define GB (1024*1024*1024) +#define MB (1024*1024) +#define KB (1024) + +/* + * Order matters -- this is the layout order on disk. + */ +Auto autox[] = +{ + { "9fat", 10*MB, 100*MB, 10, }, + { "nvram", 512, 512, 1, }, + { "fscfg", 512, 512, 1, }, + { "fs", 200*MB, 0, 10, }, + { "fossil", 200*MB, 0, 4, }, + { "arenas", 500*MB, 0, 20, }, + { "isect", 25*MB, 0, 1, }, + { "bloom", 4*MB, 512*MB, 1, }, + + { "other", 200*MB, 0, 4, }, + { "swap", 100*MB, 512*MB, 1, }, + { "cache", 50*MB, 1*GB, 2, }, +}; + +void +usage(void) +{ + fprint(2, "usage: disk/prep [-bcfprw] [-a partname]... [-s sectorsize] /dev/sdC0/plan9\n"); + exits("usage"); +} + +void +main(int argc, char **argv) +{ + int i; + char *p; + Disk *disk; + vlong secsize; + + secsize = 0; + ARGBEGIN{ + case 'a': + p = EARGF(usage()); + for(i=0; i<nelem(autox); i++){ + if(strcmp(p, autox[i].name) == 0){ + if(autox[i].alloc){ + fprint(2, "you said -a %s more than once.\n", p); + usage(); + } + autox[i].alloc = 1; + break; + } + } + if(i == nelem(autox)){ + fprint(2, "don't know how to create automatic partition %s\n", p); + usage(); + } + doautox = 1; + break; + case 'b': + blank++; + break; + case 'c': + docache++; + break; + case 'f': + file++; + break; + case 'n': + donvram++; + break; + case 'p': + printflag++; + rdonly++; + break; + case 'r': + rdonly++; + break; + case 's': + secsize = atoi(ARGF()); + break; + case 'w': + dowrite++; + break; + default: + usage(); + }ARGEND; + + if(argc != 1) + usage(); + + disk = opendisk(argv[0], rdonly, file); + if(disk == nil) + sysfatal("cannot open disk: %r"); + + if(secsize != 0) { + disk->secsize = secsize; + disk->secs = disk->size / secsize; + } + edit.end = disk->secs; + + checkfat(disk); + + secbuf = emalloc(disk->secsize+1); + osecbuf = emalloc(disk->secsize+1); + edit.disk = disk; + + if(blank == 0) + rdpart(&edit); + + opart = emalloc(edit.npart*sizeof(opart[0])); + + /* save old partition table */ + for(i=0; i<edit.npart; i++) + opart[i] = edit.part[i]; + nopart = edit.npart; + + if(printflag) { + runcmd(&edit, "P"); + exits(0); + } + + if(doautox) + autoxpart(&edit); + + if(dowrite) { + runcmd(&edit, "w"); + exits(0); + } + + runcmd(&edit, "p"); + for(;;) { + fprint(2, ">>> "); + runcmd(&edit, getline(&edit)); + } +} + +static void +cmdsum(Edit *edit, Part *p, vlong a, vlong b) +{ + vlong sz, div; + char *suf, *name; + char c; + + c = p && p->changed ? '\'' : ' '; + name = p ? p->name : "empty"; + + sz = (b-a)*edit->disk->secsize; + if(sz >= 1*TB){ + suf = "TB"; + div = TB; + }else if(sz >= 1*GB){ + suf = "GB"; + div = GB; + }else if(sz >= 1*MB){ + suf = "MB"; + div = MB; + }else if(sz >= 1*KB){ + suf = "KB"; + div = KB; + }else{ + if (sz < 0) + fprint(2, "%s: negative size!\n", argv0); + suf = "B "; + div = 1; + } + + if(div == 1) + print("%c %-12s %*lld %-*lld (%lld sectors, %lld %s)\n", c, name, + edit->disk->width, a, edit->disk->width, b, b-a, sz, suf); + else + print("%c %-12s %*lld %-*lld (%lld sectors, %lld.%.2d %s)\n", c, name, + edit->disk->width, a, edit->disk->width, b, b-a, + sz/div, (int)(((sz%div)*100)/div), suf); +} + +static char* +cmdadd(Edit *edit, char *name, vlong start, vlong end) +{ + if(start < 2 && strcmp(name, "9fat") != 0) + return "overlaps with the pbs and/or the partition table"; + + return addpart(edit, mkpart(name, start, end, 1)); +} + +static char* +cmddel(Edit *edit, Part *p) +{ + return delpart(edit, p); +} + +static char* +cmdwrite(Edit *edit) +{ + wrpart(edit); + return nil; +} + +static char isfrog[256]={ + /*NUL*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*BKS*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*DLE*/ 1, 1, 1, 1, 1, 1, 1, 1, + /*CAN*/ 1, 1, 1, 1, 1, 1, 1, 1, + [' '] 1, + ['/'] 1, + [0x7f] 1, +}; + +static char* +cmdokname(Edit*, char *elem) +{ + for(; *elem; elem++) + if(isfrog[*(uchar*)elem]) + return "bad character in name"; + return nil; +} + +static Part* +mkpart(char *name, vlong start, vlong end, int changed) +{ + Part *p; + + p = emalloc(sizeof(*p)); + p->name = estrdup(name); + p->ctlname = estrdup(name); + p->start = start; + p->end = end; + p->changed = changed; + return p; +} + +/* plan9 partition is first sector of the disk */ +static void +rdpart(Edit *edit) +{ + int i, nline, nf, waserr; + vlong a, b; + char *line[128]; + char *f[5]; + char *err; + Disk *disk; + + disk = edit->disk; + seek(disk->fd, disk->secsize, 0); + if(readn(disk->fd, osecbuf, disk->secsize) != disk->secsize) + return; + osecbuf[disk->secsize] = '\0'; + memmove(secbuf, osecbuf, disk->secsize+1); + + if(strncmp(secbuf, "part", 4) != 0){ + fprint(2, "no plan9 partition table found\n"); + return; + } + + waserr = 0; + nline = getfields(secbuf, line, nelem(line), 1, "\n"); + for(i=0; i<nline; i++){ + if(strncmp(line[i], "part", 4) != 0) { + Error: + if(waserr == 0) + fprint(2, "syntax error reading partition\n"); + waserr = 1; + continue; + } + + nf = getfields(line[i], f, nelem(f), 1, " \t\r"); + if(nf != 4 || strcmp(f[0], "part") != 0) + goto Error; + + a = strtoll(f[2], 0, 0); + b = strtoll(f[3], 0, 0); + if(a >= b) + goto Error; + + if(err = addpart(edit, mkpart(f[1], a, b, 0))) { + fprint(2, "?%s: not continuing\n", err); + exits("partition"); + } + } +} + +static vlong +min(vlong a, vlong b) +{ + if(a < b) + return a; + return b; +} + +static void +autoxpart(Edit *edit) +{ + int i, totw, futz; + vlong secs, secsize, s; + char *err; + + if(edit->npart > 0) { + if(doautox) + fprint(2, "partitions already exist; not repartitioning\n"); + return; + } + + secs = edit->disk->secs; + secsize = edit->disk->secsize; + for(;;){ + /* compute total weights */ + totw = 0; + for(i=0; i<nelem(autox); i++){ + if(autox[i].alloc==0 || autox[i].size) + continue; + totw += autox[i].weight; + } + if(totw == 0) + break; + + if(secs <= 0){ + fprint(2, "ran out of disk space during autoxpartition.\n"); + return; + } + + /* assign any minimums for small disks */ + futz = 0; + for(i=0; i<nelem(autox); i++){ + if(autox[i].alloc==0 || autox[i].size) + continue; + s = (secs*autox[i].weight)/totw; + if(s < autox[i].min/secsize){ + autox[i].size = autox[i].min/secsize; + secs -= autox[i].size; + futz = 1; + break; + } + } + if(futz) + continue; + + /* assign any maximums for big disks */ + futz = 0; + for(i=0; i<nelem(autox); i++){ + if(autox[i].alloc==0 || autox[i].size) + continue; + s = (secs*autox[i].weight)/totw; + if(autox[i].max && s > autox[i].max/secsize){ + autox[i].size = autox[i].max/secsize; + secs -= autox[i].size; + futz = 1; + break; + } + } + if(futz) + continue; + + /* finally, assign partition sizes according to weights */ + for(i=0; i<nelem(autox); i++){ + if(autox[i].alloc==0 || autox[i].size) + continue; + s = (secs*autox[i].weight)/totw; + autox[i].size = s; + + /* use entire disk even in face of rounding errors */ + secs -= autox[i].size; + totw -= autox[i].weight; + } + } + + for(i=0; i<nelem(autox); i++) + if(autox[i].alloc) + print("%s %llud\n", autox[i].name, autox[i].size); + + s = 0; + for(i=0; i<nelem(autox); i++){ + if(autox[i].alloc == 0) + continue; + if(err = addpart(edit, mkpart(autox[i].name, s, s+autox[i].size, 1))) + fprint(2, "addpart %s: %s\n", autox[i].name, err); + s += autox[i].size; + } +} + +static void +restore(Edit *edit, int ctlfd) +{ + int i; + vlong offset; + + offset = edit->disk->offset; + fprint(2, "attempting to restore partitions to previous state\n"); + if(seek(edit->disk->wfd, edit->disk->secsize, 0) != 0){ + fprint(2, "cannot restore: error seeking on disk\n"); + exits("inconsistent"); + } + + if(write(edit->disk->wfd, osecbuf, edit->disk->secsize) != edit->disk->secsize){ + fprint(2, "cannot restore: couldn't write old partition table to disk\n"); + exits("inconsistent"); + } + + if(ctlfd >= 0){ + for(i=0; i<edit->npart; i++) + fprint(ctlfd, "delpart %s", edit->part[i]->name); + for(i=0; i<nopart; i++){ + if(fprint(ctlfd, "part %s %lld %lld", opart[i]->name, opart[i]->start+offset, opart[i]->end+offset) < 0){ + fprint(2, "restored disk partition table but not kernel; reboot\n"); + exits("inconsistent"); + } + } + } + exits("restored"); +} + +static void +wrpart(Edit *edit) +{ + int i, n; + Disk *disk; + + disk = edit->disk; + + memset(secbuf, 0, disk->secsize); + n = 0; + for(i=0; i<edit->npart; i++) + n += snprint(secbuf+n, disk->secsize-n, "part %s %lld %lld\n", + edit->part[i]->name, edit->part[i]->start, edit->part[i]->end); + + if(seek(disk->wfd, disk->secsize, 0) != disk->secsize){ + fprint(2, "error seeking %d %lld on disk: %r\n", disk->wfd, disk->secsize); + exits("seek"); + } + + if(write(disk->wfd, secbuf, disk->secsize) != disk->secsize){ + fprint(2, "error writing partition table to disk\n"); + restore(edit, -1); + } + + if(ctldiff(edit, disk->ctlfd) < 0) + fprint(2, "?warning: partitions could not be updated in devsd\n"); +} + +/* + * Look for a boot sector in sector 1, as would be + * the case if editing /dev/sdC0/data when that + * was really a bootable disk. + */ +static void +checkfat(Disk *disk) +{ + uchar buf[32]; + + if(seek(disk->fd, disk->secsize, 0) < 0 + || read(disk->fd, buf, sizeof(buf)) < sizeof(buf)) + return; + + if(buf[0] != 0xEB || buf[1] != 0x3C || buf[2] != 0x90) + return; + + fprint(2, + "there's a fat partition where the\n" + "plan9 partition table would go.\n" + "if you really want to overwrite it, zero\n" + "the second sector of the disk and try again\n"); + + exits("fat partition"); +} diff --git a/sys/src/cmd/disk/rd9660.c b/sys/src/cmd/disk/rd9660.c new file mode 100755 index 000000000..868c43290 --- /dev/null +++ b/sys/src/cmd/disk/rd9660.c @@ -0,0 +1,377 @@ +/* + * dump a 9660 cd image for a little while. + * for debugging. + */ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <disk.h> + +Biobuf *b; + +#pragma varargck type "s" uchar* +#pragma varargck type "L" uchar* +#pragma varargck type "B" uchar* +#pragma varargck type "N" uchar* +#pragma varargck type "T" uchar* +#pragma varargck type "D" uchar* + +typedef struct Voldesc Voldesc; +struct Voldesc { + uchar magic[8]; /* 0x01, "CD001", 0x01, 0x00 */ + uchar systemid[32]; /* system identifier */ + uchar volumeid[32]; /* volume identifier */ + uchar unused[8]; /* character set in secondary desc */ + uchar volsize[8]; /* volume size */ + uchar charset[32]; + uchar volsetsize[4]; /* volume set size = 1 */ + uchar volseqnum[4]; /* volume sequence number = 1 */ + uchar blocksize[4]; /* logical block size */ + uchar pathsize[8]; /* path table size */ + uchar lpathloc[4]; /* Lpath */ + uchar olpathloc[4]; /* optional Lpath */ + uchar mpathloc[4]; /* Mpath */ + uchar ompathloc[4]; /* optional Mpath */ + uchar rootdir[34]; /* root directory */ + uchar volsetid[128]; /* volume set identifier */ + uchar publisher[128]; + uchar prepid[128]; /* data preparer identifier */ + uchar applid[128]; /* application identifier */ + uchar notice[37]; /* copyright notice file */ + uchar abstract[37]; /* abstract file */ + uchar biblio[37]; /* bibliographic file */ + uchar cdate[17]; /* creation date */ + uchar mdate[17]; /* modification date */ + uchar xdate[17]; /* expiration date */ + uchar edate[17]; /* effective date */ + uchar fsvers; /* file system version = 1 */ +}; + +void +dumpbootvol(void *a) +{ + Voldesc *v; + + v = a; + print("magic %.2ux %.5s %.2ux %2ux\n", + v->magic[0], v->magic+1, v->magic[6], v->magic[7]); + if(v->magic[0] == 0xFF) + return; + + print("system %.32T\n", v->systemid); + print("volume %.32T\n", v->volumeid); + print("volume size %.4N\n", v->volsize); + print("charset %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux %.2ux\n", + v->charset[0], v->charset[1], v->charset[2], v->charset[3], + v->charset[4], v->charset[5], v->charset[6], v->charset[7]); + print("volume set size %.2N\n", v->volsetsize); + print("volume sequence number %.2N\n", v->volseqnum); + print("logical block size %.2N\n", v->blocksize); + print("path size %.4L\n", v->pathsize); + print("lpath loc %.4L\n", v->lpathloc); + print("opt lpath loc %.4L\n", v->olpathloc); + print("mpath loc %.4B\n", v->mpathloc); + print("opt mpath loc %.4B\n", v->ompathloc); + print("rootdir %D\n", v->rootdir); + print("volume set identifier %.128T\n", v->volsetid); + print("publisher %.128T\n", v->publisher); + print("preparer %.128T\n", v->prepid); + print("application %.128T\n", v->applid); + print("notice %.37T\n", v->notice); + print("abstract %.37T\n", v->abstract); + print("biblio %.37T\n", v->biblio); + print("creation date %.17s\n", v->cdate); + print("modification date %.17s\n", v->mdate); + print("expiration date %.17s\n", v->xdate); + print("effective date %.17s\n", v->edate); + print("fs version %d\n", v->fsvers); +} + +typedef struct Cdir Cdir; +struct Cdir { + uchar len; + uchar xlen; + uchar dloc[8]; + uchar dlen[8]; + uchar date[7]; + uchar flags; + uchar unitsize; + uchar gapsize; + uchar volseqnum[4]; + uchar namelen; + uchar name[1]; /* chumminess */ +}; +#pragma varargck type "D" Cdir* + +int +Dfmt(Fmt *fmt) +{ + char buf[128]; + Cdir *c; + + c = va_arg(fmt->args, Cdir*); + if(c->namelen == 1 && c->name[0] == '\0' || c->name[0] == '\001') { + snprint(buf, sizeof buf, ".%s dloc %.4N dlen %.4N", + c->name[0] ? "." : "", c->dloc, c->dlen); + } else { + snprint(buf, sizeof buf, "%.*T dloc %.4N dlen %.4N", c->namelen, c->name, + c->dloc, c->dlen); + } + fmtstrcpy(fmt, buf); + return 0; +} + +typedef struct Path Path; +struct Path { + uchar namelen; + uchar xlen; + uchar dloc[4]; + uchar parent[2]; + uchar name[1]; /* chumminess */ +}; +#pragma varargck type "P" Path* + +char longc, shortc; +void +bigend(void) +{ + longc = 'B'; +} + +void +littleend(void) +{ + longc = 'L'; +} + +int +Pfmt(Fmt *fmt) +{ + char xfmt[128], buf[128]; + Path *p; + + p = va_arg(fmt->args, Path*); + sprint(xfmt, "data=%%.4%c up=%%.2%c name=%%.*T (%%d)", longc, longc); + snprint(buf, sizeof buf, xfmt, p->dloc, p->parent, p->namelen, p->name, p->namelen); + fmtstrcpy(fmt, buf); + return 0; +} + +ulong +big(void *a, int n) +{ + uchar *p; + ulong v; + int i; + + p = a; + v = 0; + for(i=0; i<n; i++) + v = (v<<8) | *p++; + return v; +} + +ulong +little(void *a, int n) +{ + uchar *p; + ulong v; + int i; + + p = a; + v = 0; + for(i=0; i<n; i++) + v |= (*p++<<(i*8)); + return v; +} + +/* numbers in big or little endian. */ +int +BLfmt(Fmt *fmt) +{ + ulong v; + uchar *p; + char buf[20]; + + p = va_arg(fmt->args, uchar*); + + if(!(fmt->flags&FmtPrec)) { + fmtstrcpy(fmt, "*BL*"); + return 0; + } + + if(fmt->r == 'B') + v = big(p, fmt->prec); + else + v = little(p, fmt->prec); + + sprint(buf, "0x%.*lux", fmt->prec*2, v); + fmt->flags &= ~FmtPrec; + fmtstrcpy(fmt, buf); + return 0; +} + +/* numbers in both little and big endian */ +int +Nfmt(Fmt *fmt) +{ + char buf[100]; + uchar *p; + + p = va_arg(fmt->args, uchar*); + + sprint(buf, "%.*L %.*B", fmt->prec, p, fmt->prec, p+fmt->prec); + fmt->flags &= ~FmtPrec; + fmtstrcpy(fmt, buf); + return 0; +} + +int +asciiTfmt(Fmt *fmt) +{ + char *p, buf[256]; + int i; + + p = va_arg(fmt->args, char*); + for(i=0; i<fmt->prec; i++) + buf[i] = *p++; + buf[i] = '\0'; + for(p=buf+strlen(buf); p>buf && p[-1]==' '; p--) + ; + p[0] = '\0'; + fmt->flags &= ~FmtPrec; + fmtstrcpy(fmt, buf); + return 0; +} + +int +runeTfmt(Fmt *fmt) +{ + Rune buf[256], *r; + int i; + uchar *p; + + p = va_arg(fmt->args, uchar*); + for(i=0; i*2+2<=fmt->prec; i++, p+=2) + buf[i] = (p[0]<<8)|p[1]; + buf[i] = L'\0'; + for(r=buf+i; r>buf && r[-1]==L' '; r--) + ; + r[0] = L'\0'; + fmt->flags &= ~FmtPrec; + return fmtprint(fmt, "%S", buf); +} + +void +ascii(void) +{ + fmtinstall('T', asciiTfmt); +} + +void +joliet(void) +{ + fmtinstall('T', runeTfmt); +} + +void +getsect(uchar *buf, int n) +{ + if(Bseek(b, n*2048, 0) != n*2048 || Bread(b, buf, 2048) != 2048) + sysfatal("reading block %ux", n); +} + +void +pathtable(Voldesc *v, int islittle) +{ + int i, j, n, sz, addr; + ulong (*word)(void*, int); + uchar x[2048], *p, *ep; + Path *pt; + + print(">>> entire %s path table\n", islittle ? "little" : "big"); + littleend(); + if(islittle) { + littleend(); + word = little; + }else{ + bigend(); + word = big; + } + sz = little(v->pathsize, 4); /* little, not word */ + addr = word(islittle ? v->lpathloc : v->mpathloc, 4); + j = 0; + n = 1; + while(sz > 0){ + getsect(x, addr); + p = x; + ep = x+sz; + if(ep > x+2048) + ep = x+2048; + for(i=0; p<ep; i++) { + pt = (Path*)p; + if(pt->namelen==0) + break; + print("0x%.4x %4d+%-4ld %P\n", n, j, p-x, pt); + n++; + p += 8+pt->namelen+(pt->namelen&1); + } + sz -= 2048; + addr++; + j++; + } +} + +void +dump(void *root) +{ + Voldesc *v; + Cdir *c; + long rootdirloc; + uchar x[2048]; + int i; + uchar *p; + + dumpbootvol(root); + v = (Voldesc*)root; + c = (Cdir*)v->rootdir; + rootdirloc = little(c->dloc, 4); + print(">>> sed 5q root directory\n"); + getsect(x, rootdirloc); + p = x; + for(i=0; i<5 && (p-x)<little(c->dlen, 4); i++) { + print("%D\n", p); + p += ((Cdir*)p)->len; + } + + pathtable(v, 1); + pathtable(v, 0); +} + +void +main(int argc, char **argv) +{ + uchar root[2048], jroot[2048]; + + if(argc != 2) + sysfatal("usage: %s file", argv[0]); + + b = Bopen(argv[1], OREAD); + if(b == nil) + sysfatal("bopen %r"); + + fmtinstall('L', BLfmt); + fmtinstall('B', BLfmt); + fmtinstall('N', Nfmt); + fmtinstall('D', Dfmt); + fmtinstall('P', Pfmt); + + getsect(root, 16); + ascii(); + dump(root); + + getsect(jroot, 17); + joliet(); + dump(jroot); + exits(0); +} diff --git a/sys/src/cmd/disk/sacfs/mkfile b/sys/src/cmd/disk/sacfs/mkfile new file mode 100755 index 000000000..3a6a9436b --- /dev/null +++ b/sys/src/cmd/disk/sacfs/mkfile @@ -0,0 +1,20 @@ +</$objtype/mkfile + +HFILES=sac.h\ + ssort.h\ + sacfs.h\ + +TARG=mksacfs\ + sacfs\ + +OFILES= + +PROGS=${TARG:%=$O.%} + +BIN=/$objtype/bin/disk + +< /sys/src/cmd/mkmany + +$O.mksacfs: sac.$O ssort6.$O + +$O.sacfs: unsac.$O diff --git a/sys/src/cmd/disk/sacfs/mksacfs.c b/sys/src/cmd/disk/sacfs/mksacfs.c new file mode 100755 index 000000000..c0c28dd57 --- /dev/null +++ b/sys/src/cmd/disk/sacfs/mksacfs.c @@ -0,0 +1,295 @@ +#include <u.h> +#include <libc.h> +#include "sac.h" +#include "sacfs.h" + +enum { + NCACHE = 1024, /* must be power of two */ + OffsetSize = 4, /* size of block offset */ +}; + +int warn(char *); +int seen(Dir *); +void usage(void); +void outwrite(void *buf, int n, long off); +void putl(void *p, uint v); +void *emalloc(int size); +void sacfs(char *root); +void sacfile(char *name, Dir *dir, SacDir *sd); +void sacdir(char *name, Dir *dir, SacDir *sd); + +int uflag=0; +int fflag=0; +long blocksize = 4*1024; + +struct Out +{ + int fd; + long size; +} out; + +typedef struct Cache Cache; +struct Cache +{ + Dir* cache; + int n; + int max; +} cache[NCACHE]; + +void +main(int argc, char *argv[]) +{ + char *s, *ss; + char *outfile = nil; + + ARGBEGIN { + case 'u': + uflag=1; + break; + case 'o': + outfile = ARGF(); + break; + case 'b': + s = ARGF(); + if(s) { + blocksize = strtoul(s, &ss, 0); + if(s == ss) + usage(); + if(*ss == 'k') + blocksize *= 1024; + } + if(blocksize < sizeof(SacDir)) + sysfatal("blocksize too small"); + break; + } ARGEND + + if(outfile == nil) { + sysfatal("still to do: need to create temp file"); + } else { + out.fd = create(outfile, OWRITE|OTRUNC, 0664); + if(out.fd < 0) + sysfatal("could not create file: %s: %r", outfile); + } + + if(argc==0) + sacfs("."); + else + sacfs(argv[0]); + + close(out.fd); + + exits(0); +} + +void +usage(void) +{ + fprint(2, "usage: %s [-u] [-b blocksize] -o output [root]\n", argv0); + exits("usage"); +} + +void +sacfs(char *root) +{ + Dir *dir; + long offset; + SacHeader hdr; + SacDir sd; + + dir = dirstat(root); + if(dir == nil) + sysfatal("could not stat root: %s: %r", root); + offset = out.size; + out.size += sizeof(SacHeader) + sizeof(SacDir); + memset(&hdr, 0, sizeof(hdr)); + putl(hdr.magic, Magic); + putl(hdr.blocksize, blocksize); + if(dir->mode & DMDIR) + sacdir(root, dir, &sd); + else + sacfile(root, dir, &sd); + putl(hdr.length, out.size); + outwrite(&hdr, sizeof(hdr), offset); + outwrite(&sd, sizeof(sd), offset+sizeof(SacHeader)); + free(dir); +} + +void +setsd(SacDir *sd, Dir *dir, long length, long blocks) +{ + static qid = 1; + + memset(sd, 0, sizeof(SacDir)); + strncpy(sd->name, dir->name, NAMELEN); + strncpy(sd->uid, dir->uid, NAMELEN); + strncpy(sd->gid, dir->gid, NAMELEN); + putl(sd->qid, qid++|(dir->mode&DMDIR)); + putl(sd->mode, dir->mode); + putl(sd->atime, dir->atime); + putl(sd->mtime, dir->mtime); + putl(sd->length, length); + putl(sd->blocks, blocks); +} + + +void +sacfile(char *name, Dir *dir, SacDir *sd) +{ + int fd, i, n, nn; + long nblock; + uchar *blocks; + uchar *buf, *cbuf; + long offset; + long block; + + fd = open(name, OREAD); + if(fd < 0) + sysfatal("could not open file: %s: %r", name); + nblock = (dir->length + blocksize-1)/blocksize; + blocks = emalloc((nblock+1)*OffsetSize); + buf = emalloc(blocksize); + cbuf = emalloc(blocksize); + offset = out.size; + out.size += (nblock+1)*OffsetSize; + for(i=0; i<nblock; i++) { + n = read(fd, buf, blocksize); + if(n < 0) + sysfatal("read failed: %s: %r", name); + if(n == 0) + sysfatal("unexpected eof: %s", name); + if(n < blocksize && i != nblock-1) + sysfatal("short read: %s: got %d", name, n); + block = out.size; + nn = sac(cbuf, buf, n); + if(nn < 0 || uflag) { + outwrite(buf, n, out.size); + out.size += n; + } else { + block = -block; + outwrite(cbuf, nn, out.size); + out.size += nn; + } + putl(blocks+i*OffsetSize, block); + } + putl(blocks+i*OffsetSize, out.size); + outwrite(blocks, (nblock+1)*OffsetSize, offset); + setsd(sd, dir, dir->length, offset); + close(fd); + free(buf); + free(cbuf); + free(blocks); +} + +void +sacdir(char *name, Dir *dir, SacDir *sd) +{ + Dir *dirs, *p; + int i, n, nn, per; + SacDir *sds; + int ndir, fd, nblock; + long offset, block; + uchar *blocks, *cbuf; + char file[512]; + + fd = open(name, OREAD); + if(fd < 0) + sysfatal("could not open directory: %s: %r", name); + ndir = dirreadall(fd, &dirs); + if(ndir < 0) + sysfatal("could not read directory: %s: %r", name); + close(fd); + per = blocksize/sizeof(SacDir); + nblock = (ndir+per-1)/per; + sds = emalloc(nblock*per*sizeof(SacDir)); + p = dirs; + for(i=0; i<ndir; i++,p++) { + sprint(file, "%s/%s", name, p->name); + if(p->mode & DMDIR) + sacdir(file, p, sds+i); + else + sacfile(file, p, sds+i); + } + free(dirs); + blocks = emalloc((nblock+1)*OffsetSize); + offset = out.size; + out.size += (nblock+1)*OffsetSize; + n = per*sizeof(SacDir); + cbuf = emalloc(n); + for(i=0; i<nblock; i++) { + block = out.size; + if(n > (ndir-i*per)*sizeof(SacDir)) + n = (ndir-i*per)*sizeof(SacDir); + nn = sac(cbuf, (uchar*)(sds+i*per), n); + if(nn < 0 || uflag) { + outwrite(sds+i*per, n, out.size); + out.size += n; + } else { + block = -block; + outwrite(cbuf, nn, out.size); + out.size += nn; + } + putl(blocks+i*OffsetSize, block); + } + free(cbuf); + putl(blocks+i*OffsetSize, out.size); + outwrite(blocks, (nblock+1)*OffsetSize, offset); + setsd(sd, dir, ndir, offset); + free(sds); + free(blocks); +} + +int +seen(Dir *dir) +{ + Dir *dp; + int i; + Cache *c; + + c = &cache[dir->qid.path&(NCACHE-1)]; + dp = c->cache; + for(i=0; i<c->n; i++, dp++) + if(dir->qid.path == dp->qid.path && + dir->type == dp->type && + dir->dev == dp->dev) + return 1; + if(c->n == c->max){ + c->cache = realloc(c->cache, (c->max+=20)*sizeof(Dir)); + if(cache == 0) + sysfatal("malloc failure"); + } + c->cache[c->n++] = *dir; + return 0; +} + +void +outwrite(void *buf, int n, long offset) +{ + if(seek(out.fd, offset, 0) < 0) + sysfatal("seek failed: %r"); + if(write(out.fd, buf, n) < n) + sysfatal("write failed: %r"); +} + +void +putl(void *p, uint v) +{ + uchar *a; + + a = p; + a[0] = v>>24; + a[1] = v>>16; + a[2] = v>>8; + a[3] = v; +} + +void * +emalloc(int size) +{ + void *p; + + p = malloc(size); + if(p == nil) + sysfatal("malloc failed"); + memset(p, 0, size); + return p; +} diff --git a/sys/src/cmd/disk/sacfs/sac.c b/sys/src/cmd/disk/sacfs/sac.c new file mode 100755 index 000000000..abe775a7a --- /dev/null +++ b/sys/src/cmd/disk/sacfs/sac.c @@ -0,0 +1,792 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include "sac.h" +#include "ssort.h" + +typedef struct Chain Chain; +typedef struct Chains Chains; +typedef struct Huff Huff; + +enum +{ + ZBase = 2, /* base of code to encode 0 runs */ + LitBase = ZBase-1, /* base of literal values */ + MaxLit = 256, + + MaxLeaf = MaxLit+LitBase, + + MaxHuffBits = 16, /* limit of bits in a huffman code */ + ChainMem = 2 * (MaxHuffBits - 1) * MaxHuffBits, +}; + +/* + * huffman code table + */ +struct Huff +{ + short bits; /* length of the code */ + ulong encode; /* the code */ +}; + +struct Chain +{ + ulong count; /* occurances of everything in the chain */ + ushort leaf; /* leaves to the left of chain, or leaf value */ + char col; /* ref count for collecting unused chains */ + char gen; /* need to generate chains for next lower level */ + Chain *up; /* Chain up in the lists */ +}; + +struct Chains +{ + Chain *lists[(MaxHuffBits - 1) * 2]; + ulong leafcount[MaxLeaf]; /* sorted list of leaf counts */ + ushort leafmap[MaxLeaf]; /* map to actual leaf number */ + int nleaf; /* number of leaves */ + Chain chains[ChainMem]; + Chain *echains; + Chain *free; + char col; + int nlists; +}; + +static jmp_buf errjmp; + +static uchar *bout; +static int bmax; +static int bpos; + +static ulong leafcount[MaxLeaf]; +static ulong nzero; +static ulong *hbuf; +static int hbpos; +static ulong nzero; +static ulong maxblocksym; + +static void henc(int); +static void hbflush(void); + +static void mkprecode(Huff*, ulong *, int, int); +static ulong sendtab(Huff *tab, int maxleaf, ushort *map); +static int elimBits(int b, ulong *bused, char *tmtf, int maxbits); +static void elimBit(int b, char *tmtf, int maxbits); +static void nextchain(Chains*, int); +static void hufftabinit(Huff*, int, ulong*, int); +static void leafsort(ulong*, ushort*, int, int); + +static void bitput(int, int); + +static int mtf(uchar*, int); +static void fatal(char*, ...); + +static ulong headerbits; +static ulong charmapbits; +static ulong hufftabbits; +static ulong databits; +static ulong nbits; +static ulong bits; + +int +sac(uchar *dst, uchar *src, int n) +{ + uchar front[256]; + ulong *perm, cmap[256]; + long i, c, rev; + + rev = 0; + + perm = nil; + + if(setjmp(errjmp)){ + free(perm); + if(rev){ + for(i = 0; i < n/2; i++){ + c = src[i]; + src[i] = src[n-i-1]; + src[n-i-1] = c; + } + } + return -1; + } + + /* + * set up the output buffer + */ + bout = dst; + bmax = n; + bpos = 0; + + perm = malloc((n+1)*sizeof *perm); + if(perm == nil) + return -1; + + hbuf = perm; + hbpos = 0; + + nbits = 0; + nzero = 0; + for(i = 0; i < MaxLeaf; i++) + leafcount[i] = 0; + + if(rev){ + for(i = 0; i < n/2; i++){ + c = src[i]; + src[i] = src[n-i-1]; + src[n-i-1] = c; + } + } + + i = ssortbyte(src, (int*)perm, nil, n); + + headerbits += 16; + bitput(i, 16); + + /* + * send the map of chars which occur in this block + */ + for(i = 0; i < 256; i++) + cmap[i] = 0; + for(i = 0; i < n; i++) + cmap[src[i]] = 1; + charmapbits += 1; + bitput(cmap[0] != 0, 1); + for(i = 0; i < 256; i = c){ + for(c = i + 1; c < 256 && (cmap[c] != 0) == (cmap[i] != 0); c++) + ; + charmapbits += 8; + bitput(c - i - 1, 8); + } + + /* + * initialize mtf state + */ + c = 0; + for(i = 0; i < 256; i++){ + if(cmap[i]){ + cmap[i] = c; + front[c] = i; + c++; + } + } + maxblocksym = c + LitBase; + + for(i = 0; i <= n; i++){ + c = perm[i] - 1; + if(c < 0) + continue; + henc(mtf(front, src[c])); + } + + hbflush(); + bitput(0, 24); + bitput(0, 8 - nbits); + + free(perm); + return bpos; +} + +static void +bitput(int c, int n) +{ + bits <<= n; + bits |= c; + for(nbits += n; nbits >= 8; nbits -= 8){ + c = bits << (32 - nbits); + + if(bpos >= bmax) + longjmp(errjmp, 1); + bout[bpos++] = c >> 24; + } +} + +static int +mtf(uchar *front, int c) +{ + int i, v; + + for(i = 0; front[i] != c; i++) + ; + for(v = i; i > 0; i--) + front[i] = front[i - 1]; + front[0] = c; + return v; +} + +/* + * encode a run of zeros + * convert to funny run length coding: + * add one, omit implicit top 1 bit. + */ +static void +zenc(void) +{ + int m; + + nzero++; + while(nzero != 1){ + m = nzero & 1; + hbuf[hbpos++] = m; + leafcount[m]++; + nzero >>= 1; + } + nzero = 0; +} + +/* + * encode one symbol + */ +static void +henc(int c) +{ + if(c == 0){ + nzero++; + return; + } + + if(nzero) + zenc(); + c += LitBase; + leafcount[c]++; + + hbuf[hbpos++] = c; +} + +static void +hbflush(void) +{ + Huff tab[MaxLeaf]; + int i, b, s; + + if(nzero) + zenc(); + if(hbpos == 0) + return; + + mkprecode(tab, leafcount, maxblocksym, MaxHuffBits); + + hufftabbits += sendtab(tab, maxblocksym, nil); + + /* + * send the data + */ + for(i = 0; i < hbpos; i++){ + s = hbuf[i]; + b = tab[s].bits; + if(b == 0) + fatal("bad tables %d", i); + databits += b; + bitput(tab[s].encode, b); + } +} + +static ulong +sendtab(Huff *tab, int maxleaf, ushort *map) +{ + ulong ccount[MaxHuffBits+1], bused[MaxHuffBits+1]; + Huff codetab[MaxHuffBits+1]; + char tmtf[MaxHuffBits+1]; + ulong bits; + int i, b, max, ttb, s, elim; + + /* + * make up the table table + * over cleverness: the data is known to be a huffman table + * check for fullness at each bit level, and wipe it out from + * the possibilities; when nothing exists except 0, stop. + */ + for(i = 0; i <= MaxHuffBits; i++){ + tmtf[i] = i; + ccount[i] = 0; + bused[i] = 0; + } + tmtf[0] = -1; + tmtf[MaxHuffBits] = 0; + + elim = 0; + for(i = 0; i < maxleaf && elim != MaxHuffBits; i++){ + b = i; + if(map) + b = map[b]; + b = tab[b].bits; + for(s = 0; tmtf[s] != b; s++) + if(s >= MaxHuffBits) + fatal("bitlength not found"); + ccount[s]++; + for(; s > 0; s--) + tmtf[s] = tmtf[s-1]; + tmtf[0] = b; + + elim += elimBits(b, bused, tmtf, MaxHuffBits); + } + if(elim != MaxHuffBits && elim != 0) + fatal("incomplete huffman table"); + + /* + * make up and send the table table + */ + max = 8; + mkprecode(codetab, ccount, MaxHuffBits+1, max); + for(i = 0; i <= max; i++){ + tmtf[i] = i; + bused[i] = 0; + } + tmtf[0] = -1; + tmtf[max] = 0; + elim = 0; + bits = 0; + for(i = 0; i <= MaxHuffBits && elim != max; i++){ + b = codetab[i].bits; + for(s = 0; tmtf[s] != b; s++) + if(s > max) + fatal("bitlength not found"); + ttb = 4; + while(max - elim < (1 << (ttb-1))) + ttb--; + bits += ttb; + bitput(s, ttb); + + elim += elimBits(b, bused, tmtf, max); + } + if(elim != max) + fatal("incomplete huffman table table"); + + /* + * send the table + */ + for(i = 0; i <= MaxHuffBits; i++){ + tmtf[i] = i; + bused[i] = 0; + } + tmtf[0] = -1; + tmtf[MaxHuffBits] = 0; + elim = 0; + for(i = 0; i < maxleaf && elim != MaxHuffBits; i++){ + b = i; + if(map) + b = map[b]; + b = tab[b].bits; + for(s = 0; tmtf[s] != b; s++) + if(s >= MaxHuffBits) + fatal("bitlength not found"); + ttb = codetab[s].bits; + if(ttb < 0) + fatal("bad huffman table table"); + bits += ttb; + bitput(codetab[s].encode, ttb); + for(; s > 0; s--) + tmtf[s] = tmtf[s-1]; + tmtf[0] = b; + + elim += elimBits(b, bused, tmtf, MaxHuffBits); + } + if(elim != MaxHuffBits && elim != 0) + fatal("incomplete huffman table"); + + return bits; +} + +static int +elimBits(int b, ulong *bused, char *tmtf, int maxbits) +{ + int bb, elim; + + if(b < 0) + return 0; + + elim = 0; + + /* + * increase bits counts for all descendants + */ + for(bb = b + 1; bb < maxbits; bb++){ + bused[bb] += 1 << (bb - b); + if(bused[bb] == (1 << bb)){ + elim++; + elimBit(bb, tmtf, maxbits); + } + } + + /* + * steal bits from parent & check for fullness + */ + for(; b >= 0; b--){ + bused[b]++; + if(bused[b] == (1 << b)){ + elim++; + elimBit(b, tmtf, maxbits); + } + if((bused[b] & 1) == 0) + break; + } + return elim; +} + +static void +elimBit(int b, char *tmtf, int maxbits) +{ + int bb; + + for(bb = 0; bb < maxbits; bb++) + if(tmtf[bb] == b) + break; + if(tmtf[bb] != b) + fatal("elim bit not found"); + while(++bb <= maxbits) + tmtf[bb - 1] = tmtf[bb]; +} + +/* + * fast?, in-place huffman codes + * + * A. Moffat, J. Katajainen, "In-Place Calculation of Minimum-Redundancy Codes", + * + * counts must be sorted, and may be aliased to bitlens + */ +static int +fmkprecode(ulong *bitcount, ulong *bitlen, ulong *counts, int n, int maxbits) +{ + int leaf, tree, treet, depth, nodes, internal; + + /* + * form the internal tree structure: + * [0, tree) are parent pointers, + * [tree, treet) are weights of internal nodes, + * [leaf, n) are weights of remaining leaves. + * + * note that at the end, there are n-1 internal nodes + */ + leaf = 0; + tree = 0; + for(treet = 0; treet != n - 1; treet++){ + if(leaf >= n || tree < treet && bitlen[tree] < counts[leaf]){ + bitlen[treet] = bitlen[tree]; + bitlen[tree++] = treet; + }else + bitlen[treet] = counts[leaf++]; + if(leaf >= n || tree < treet && bitlen[tree] < counts[leaf]){ + bitlen[treet] += bitlen[tree]; + bitlen[tree++] = treet; + }else + bitlen[treet] += counts[leaf++]; + } + if(tree != treet - 1) + fatal("bad fast mkprecode"); + + /* + * calculate depth of internal nodes + */ + bitlen[n - 2] = 0; + for(tree = n - 3; tree >= 0; tree--) + bitlen[tree] = bitlen[bitlen[tree]] + 1; + + /* + * calculate depth of leaves: + * at each depth, leaves(depth) = nodes(depth) - internal(depth) + * and nodes(depth+1) = 2 * internal(depth) + */ + leaf = n; + tree = n - 2; + depth = 0; + for(nodes = 1; nodes > 0; nodes = 2 * internal){ + internal = 0; + while(tree >= 0 && bitlen[tree] == depth){ + tree--; + internal++; + } + nodes -= internal; + if(depth < maxbits) + bitcount[depth] = nodes; + while(nodes-- > 0) + bitlen[--leaf] = depth; + depth++; + } + if(leaf != 0 || tree != -1) + fatal("bad leaves in fast mkprecode"); + + return depth - 1; +} + +/* + * fast, low space overhead algorithm for max depth huffman type codes + * + * J. Katajainen, A. Moffat and A. Turpin, "A fast and space-economical + * algorithm for length-limited coding," Proc. Intl. Symp. on Algorithms + * and Computation, Cairns, Australia, Dec. 1995, Lecture Notes in Computer + * Science, Vol 1004, J. Staples, P. Eades, N. Katoh, and A. Moffat, eds., + * pp 12-21, Springer Verlag, New York, 1995. + */ +static void +mkprecode(Huff *tab, ulong *count, int n, int maxbits) +{ + Chains cs; + Chain *c; + ulong bitcount[MaxHuffBits]; + int i, m, em, bits; + + /* + * set up the sorted list of leaves + */ + m = 0; + for(i = 0; i < n; i++){ + tab[i].bits = -1; + tab[i].encode = 0; + if(count[i] != 0){ + cs.leafcount[m] = count[i]; + cs.leafmap[m] = i; + m++; + } + } + if(m < 2){ + if(m != 0){ + m = cs.leafmap[0]; + tab[m].bits = 0; + tab[m].encode = 0; + } + return; + } + cs.nleaf = m; + leafsort(cs.leafcount, cs.leafmap, 0, m); + + /* + * try fast code + */ + bits = fmkprecode(bitcount, cs.leafcount, cs.leafcount, m, maxbits); + if(bits < maxbits){ + for(i = 0; i < m; i++) + tab[cs.leafmap[i]].bits = cs.leafcount[i]; + bitcount[0] = 0; + }else{ + for(i = 0; i < m; i++) + cs.leafcount[i] = count[cs.leafmap[i]]; + + /* + * set up free list + */ + cs.free = &cs.chains[2]; + cs.echains = &cs.chains[ChainMem]; + cs.col = 1; + + /* + * initialize chains for each list + */ + c = &cs.chains[0]; + c->count = cs.leafcount[0]; + c->leaf = 1; + c->col = cs.col; + c->up = nil; + c->gen = 0; + cs.chains[1] = cs.chains[0]; + cs.chains[1].leaf = 2; + cs.chains[1].count = cs.leafcount[1]; + for(i = 0; i < maxbits-1; i++){ + cs.lists[i * 2] = &cs.chains[0]; + cs.lists[i * 2 + 1] = &cs.chains[1]; + } + + cs.nlists = 2 * (maxbits - 1); + m = 2 * m - 2; + for(i = 2; i < m; i++) + nextchain(&cs, cs.nlists - 2); + + bits = 0; + bitcount[0] = cs.nleaf; + for(c = cs.lists[cs.nlists - 1]; c != nil; c = c->up){ + m = c->leaf; + bitcount[bits++] -= m; + bitcount[bits] = m; + } + m = 0; + for(i = bits; i >= 0; i--) + for(em = m + bitcount[i]; m < em; m++) + tab[cs.leafmap[m]].bits = i; + } + hufftabinit(tab, n, bitcount, bits); +} + +/* + * calculate the next chain on the list + * we can always toss out the old chain + */ +static void +nextchain(Chains *cs, int list) +{ + Chain *c, *oc; + int i, nleaf, sumc; + + oc = cs->lists[list + 1]; + cs->lists[list] = oc; + if(oc == nil) + return; + + /* + * make sure we have all chains needed to make sumc + * note it is possible to generate only one of these, + * use twice that value for sumc, and then generate + * the second if that preliminary sumc would be chosen. + * however, this appears to be slower on current tests + */ + if(oc->gen){ + nextchain(cs, list - 2); + nextchain(cs, list - 2); + oc->gen = 0; + } + + /* + * pick up the chain we're going to add; + * collect unused chains no free ones are left + */ + for(c = cs->free; ; c++){ + if(c >= cs->echains){ + cs->col++; + for(i = 0; i < cs->nlists; i++) + for(c = cs->lists[i]; c != nil; c = c->up) + c->col = cs->col; + c = cs->chains; + } + if(c->col != cs->col) + break; + } + + /* + * pick the cheapest of + * 1) the next package from the previous list + * 2) the next leaf + */ + nleaf = oc->leaf; + sumc = 0; + if(list > 0 && cs->lists[list-1] != nil) + sumc = cs->lists[list-2]->count + cs->lists[list-1]->count; + if(sumc != 0 && (nleaf >= cs->nleaf || cs->leafcount[nleaf] > sumc)){ + c->count = sumc; + c->leaf = oc->leaf; + c->up = cs->lists[list-1]; + c->gen = 1; + }else if(nleaf >= cs->nleaf){ + cs->lists[list + 1] = nil; + return; + }else{ + c->leaf = nleaf + 1; + c->count = cs->leafcount[nleaf]; + c->up = oc->up; + c->gen = 0; + } + cs->free = c + 1; + + cs->lists[list + 1] = c; + c->col = cs->col; +} + +static void +hufftabinit(Huff *tab, int n, ulong *bitcount, int nbits) +{ + ulong code, nc[MaxHuffBits]; + int i, bits; + + code = 0; + for(bits = 1; bits <= nbits; bits++){ + code = (code + bitcount[bits-1]) << 1; + nc[bits] = code; + } + + for(i = 0; i < n; i++){ + bits = tab[i].bits; + if(bits > 0) + tab[i].encode = nc[bits]++; + } +} + +static int +pivot(ulong *c, int a, int n) +{ + int j, pi, pj, pk; + + j = n/6; + pi = a + j; /* 1/6 */ + j += j; + pj = pi + j; /* 1/2 */ + pk = pj + j; /* 5/6 */ + if(c[pi] < c[pj]){ + if(c[pi] < c[pk]){ + if(c[pj] < c[pk]) + return pj; + return pk; + } + return pi; + } + if(c[pj] < c[pk]){ + if(c[pi] < c[pk]) + return pi; + return pk; + } + return pj; +} + +static void +leafsort(ulong *leafcount, ushort *leafmap, int a, int n) +{ + ulong t; + int j, pi, pj, pn; + + while(n > 1){ + if(n > 10){ + pi = pivot(leafcount, a, n); + }else + pi = a + (n>>1); + + t = leafcount[pi]; + leafcount[pi] = leafcount[a]; + leafcount[a] = t; + t = leafmap[pi]; + leafmap[pi] = leafmap[a]; + leafmap[a] = t; + pi = a; + pn = a + n; + pj = pn; + for(;;){ + do + pi++; + while(pi < pn && (leafcount[pi] < leafcount[a] || leafcount[pi] == leafcount[a] && leafmap[pi] > leafmap[a])); + do + pj--; + while(pj > a && (leafcount[pj] > leafcount[a] || leafcount[pj] == leafcount[a] && leafmap[pj] < leafmap[a])); + if(pj < pi) + break; + t = leafcount[pi]; + leafcount[pi] = leafcount[pj]; + leafcount[pj] = t; + t = leafmap[pi]; + leafmap[pi] = leafmap[pj]; + leafmap[pj] = t; + } + t = leafcount[a]; + leafcount[a] = leafcount[pj]; + leafcount[pj] = t; + t = leafmap[a]; + leafmap[a] = leafmap[pj]; + leafmap[pj] = t; + j = pj - a; + + n = n-j-1; + if(j >= n){ + leafsort(leafcount, leafmap, a, j); + a += j+1; + }else{ + leafsort(leafcount, leafmap, a + (j+1), n); + n = j; + } + } +} + +static void +fatal(char *fmt, ...) +{ + char buf[512]; + va_list arg; + + va_start(arg, fmt); + doprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + + longjmp(errjmp, 1); +} diff --git a/sys/src/cmd/disk/sacfs/sac.h b/sys/src/cmd/disk/sacfs/sac.h new file mode 100755 index 000000000..7caaa476c --- /dev/null +++ b/sys/src/cmd/disk/sacfs/sac.h @@ -0,0 +1,2 @@ +int sac(uchar *dst, uchar *src, int blocksize); +int unsac(uchar *dst, uchar *src, int n, int nsrc); diff --git a/sys/src/cmd/disk/sacfs/sacfs.c b/sys/src/cmd/disk/sacfs/sacfs.c new file mode 100755 index 000000000..0824143a4 --- /dev/null +++ b/sys/src/cmd/disk/sacfs/sacfs.c @@ -0,0 +1,882 @@ +#include <u.h> +#include <libc.h> +#include <auth.h> +#include <fcall.h> +#include "sac.h" +#include "sacfs.h" + +/* + * Rather than reading /adm/users, which is a lot of work for + * a toy program, we assume all groups have the form + * NNN:user:user: + * meaning that each user is the leader of his own group. + */ + +enum +{ + OPERM = 0x3, /* mask of all permission types in open mode */ + Nram = 512, + OffsetSize = 4, /* size in bytes of an offset */ + CacheSize = 20, +}; + +typedef struct Fid Fid; +typedef struct Path Path; +typedef struct Sac Sac; +typedef struct Cache Cache; + +struct Fid +{ + short busy; + short open; + int fid; + char *user; + Qid qid; + Sac *sac; + Fid *next; +}; + +struct Sac +{ + SacDir; + Path *path; +}; + +struct Path +{ + int ref; + Path *up; + long blocks; + int entry; + int nentry; +}; + +struct Cache +{ + long block; + ulong age; + uchar *data; +}; + +enum +{ + Pexec = 1, + Pwrite = 2, + Pread = 4, + Pother = 1, + Pgroup = 8, + Powner = 64, +}; + +Fid *fids; +uchar *data; +int mfd[2]; +char user[NAMELEN]; +char mdata[MAXMSG+MAXFDATA]; +Fcall rhdr; +Fcall thdr; +int blocksize; +Sac root; +Cache cache[CacheSize]; +ulong cacheage; + +Fid * newfid(int); +void sacstat(SacDir*, char*); +void error(char*); +void io(void); +void *erealloc(void*, ulong); +void *emalloc(ulong); +void usage(void); +int perm(Fid*, Sac*, int); +ulong getl(void *p); +void init(char*); +Sac *saccpy(Sac *s); +Sac *saclookup(Sac *s, char *name); +int sacdirread(Sac *s, char *p, long off, long cnt); +void loadblock(void *buf, uchar *offset, int blocksize); +void sacfree(Sac*); + +char *rflush(Fid*), *rnop(Fid*), *rsession(Fid*), + *rattach(Fid*), *rclone(Fid*), *rwalk(Fid*), + *rclwalk(Fid*), *ropen(Fid*), *rcreate(Fid*), + *rread(Fid*), *rwrite(Fid*), *rclunk(Fid*), + *rremove(Fid*), *rstat(Fid*), *rwstat(Fid*); + +char *(*fcalls[])(Fid*) = { + [Tflush] rflush, + [Tsession] rsession, + [Tnop] rnop, + [Tattach] rattach, + [Tclone] rclone, + [Twalk] rwalk, + [Tclwalk] rclwalk, + [Topen] ropen, + [Tcreate] rcreate, + [Tread] rread, + [Twrite] rwrite, + [Tclunk] rclunk, + [Tremove] rremove, + [Tstat] rstat, + [Twstat] rwstat, +}; + +char Eperm[] = "permission denied"; +char Enotdir[] = "not a directory"; +char Enoauth[] = "no authentication in ramfs"; +char Enotexist[] = "file does not exist"; +char Einuse[] = "file in use"; +char Eexist[] = "file exists"; +char Enotowner[] = "not owner"; +char Eisopen[] = "file already open for I/O"; +char Excl[] = "exclusive use file already open"; +char Ename[] = "illegal name"; +char Erdonly[] = "read only file system"; + +int debug; + +void +notifyf(void *a, char *s) +{ + USED(a); + if(strncmp(s, "interrupt", 9) == 0) + noted(NCONT); + noted(NDFLT); +} + +void +main(int argc, char *argv[]) +{ + char *defmnt; + int p[2]; + char buf[12]; + int fd; + int stdio = 0; + + defmnt = "/n/c:"; + ARGBEGIN{ + case 'd': + debug = 1; + break; + case 'i': + defmnt = 0; + stdio = 1; + mfd[0] = 0; + mfd[1] = 1; + break; + case 's': + defmnt = 0; + break; + case 'm': + defmnt = ARGF(); + break; + default: + usage(); + }ARGEND + + if(argc != 1) + usage(); + + init(argv[0]); + + if(pipe(p) < 0) + error("pipe failed"); + if(!stdio){ + mfd[0] = p[0]; + mfd[1] = p[0]; + if(defmnt == 0){ + fd = create("#s/sacfs", OWRITE, 0666); + if(fd < 0) + error("create of /srv/sacfs failed"); + sprint(buf, "%d", p[1]); + if(write(fd, buf, strlen(buf)) < 0) + error("writing /srv/sacfs"); + } + } + + if(debug) + fmtinstall('F', fcallconv); + switch(rfork(RFFDG|RFPROC|RFNAMEG|RFNOTEG)){ + case -1: + error("fork"); + case 0: + close(p[1]); + io(); + break; + default: + close(p[0]); /* don't deadlock if child fails */ + if(defmnt && mount(p[1], defmnt, MREPL|MCREATE, "") < 0) + error("mount failed"); + } + exits(0); +} + +char* +rnop(Fid *f) +{ + USED(f); + return 0; +} + +char* +rsession(Fid *unused) +{ + Fid *f; + + USED(unused); + + for(f = fids; f; f = f->next) + if(f->busy) + rclunk(f); + memset(thdr.authid, 0, sizeof(thdr.authid)); + memset(thdr.authdom, 0, sizeof(thdr.authdom)); + memset(thdr.chal, 0, sizeof(thdr.chal)); + return 0; +} + +char* +rflush(Fid *f) +{ + USED(f); + return 0; +} + +char* +rattach(Fid *f) +{ + /* no authentication! */ + f->busy = 1; + f->qid = (Qid){getl(root.qid), 0}; + f->sac = saccpy(&root); + thdr.qid = f->qid; + if(rhdr.uname[0]) + f->user = strdup(rhdr.uname); + else + f->user = "none"; + return 0; +} + +char* +rclone(Fid *f) +{ + Fid *nf; + + if(f->open) + return Eisopen; + if(f->busy == 0) + return Enotexist; + nf = newfid(rhdr.newfid); + nf->busy = 1; + nf->open = 0; + nf->qid = f->qid; + nf->sac = saccpy(f->sac); + nf->user = strdup(f->user); + return 0; +} + +char* +rwalk(Fid *f) +{ + char *name; + Sac *sac; + + if((f->qid.path & CHDIR) == 0) + return Enotdir; + if(f->busy == 0) + return Enotexist; + name = rhdr.name; + sac = f->sac; + if(strcmp(name, ".") == 0){ + thdr.qid = f->qid; + return 0; + } + if(!perm(f, sac, Pexec)) + return Eperm; + sac = saclookup(sac, name); + if(sac == nil) + return Enotexist; + f->sac = sac; + f->qid = (Qid){getl(sac->qid), 0}; + thdr.qid = f->qid; + return 0; +} + +char * +rclwalk(Fid *f) +{ + Fid *nf; + char *err; + + nf = newfid(rhdr.newfid); + nf->busy = 1; + nf->sac = saccpy(f->sac); + nf->user = strdup(f->user); + if(err = rwalk(nf)) + rclunk(nf); + return err; +} + +char * +ropen(Fid *f) +{ + int mode, trunc; + + if(f->open) + return Eisopen; + if(f->busy == 0) + return Enotexist; + mode = rhdr.mode; + if(f->qid.path & CHDIR){ + if(mode != OREAD) + return Eperm; + thdr.qid = f->qid; + return 0; + } + if(mode & ORCLOSE) + return Erdonly; + trunc = mode & OTRUNC; + mode &= OPERM; + if(mode==OWRITE || mode==ORDWR || trunc) + return Erdonly; + if(mode==OREAD) + if(!perm(f, f->sac, Pread)) + return Eperm; + if(mode==OEXEC) + if(!perm(f, f->sac, Pexec)) + return Eperm; + thdr.qid = f->qid; + f->open = 1; + return 0; +} + +char * +rcreate(Fid *f) +{ + if(f->open) + return Eisopen; + if(f->busy == 0) + return Enotexist; + return Erdonly; +} + +char* +rread(Fid *f) +{ + Sac *sac; + char *buf, *buf2; + long off; + int n, cnt, i, j; + uchar *blocks; + long length; + + if(f->busy == 0) + return Enotexist; + sac = f->sac; + thdr.count = 0; + off = rhdr.offset; + buf = thdr.data; + cnt = rhdr.count; + if(f->qid.path & CHDIR){ + cnt = (rhdr.count/DIRLEN)*DIRLEN; + if(off%DIRLEN) + return "i/o error"; + thdr.count = sacdirread(sac, buf, off, cnt); + return 0; + } + length = getl(sac->length); + if(off >= length) { + rhdr.count = 0; + return 0; + } + if(cnt > length-off) + cnt = length-off; + thdr.count = cnt; + if(cnt == 0) + return 0; + blocks = data + getl(sac->blocks); + buf2 = malloc(blocksize); + if(buf2 == nil) + sysfatal("malloc failed"); + while(cnt > 0) { + i = off/blocksize; + n = blocksize; + if(n > length-i*blocksize) + n = length-i*blocksize; + loadblock(buf2, blocks+i*OffsetSize, n); + j = off-i*blocksize; + n = blocksize-j; + if(n > cnt) + n = cnt; + memmove(buf, buf2+j, n); + cnt -= n; + off += n; + buf += n; + } + free(buf2); + return 0; +} + +char* +rwrite(Fid *f) +{ + if(f->busy == 0) + return Enotexist; + return Erdonly; +} + +char * +rclunk(Fid *f) +{ + f->busy = 0; + f->open = 0; + free(f->user); + sacfree(f->sac); + return 0; +} + +char * +rremove(Fid *f) +{ + f->busy = 0; + f->open = 0; + free(f->user); + sacfree(f->sac); + return Erdonly; +} + +char * +rstat(Fid *f) +{ + if(f->busy == 0) + return Enotexist; + sacstat(f->sac, thdr.stat); + return 0; +} + +char * +rwstat(Fid *f) +{ + if(f->busy == 0) + return Enotexist; + return Erdonly; +} + +Sac* +saccpy(Sac *s) +{ + Sac *ss; + + ss = emalloc(sizeof(Sac)); + *ss = *s; + if(ss->path) + ss->path->ref++; + return ss; +} + +Path * +pathalloc(Path *p, long blocks, int entry, int nentry) +{ + Path *pp = emalloc(sizeof(Path)); + + pp->ref = 1; + pp->blocks = blocks; + pp->entry = entry; + pp->nentry = nentry; + pp->up = p; + return pp; +} + +void +pathfree(Path *p) +{ + if(p == nil) + return; + p->ref--; + if(p->ref > 0) + return; + + pathfree(p->up); + free(p); +} + + +void +sacfree(Sac *s) +{ + pathfree(s->path); + free(s); +} + +void +sacstat(SacDir *s, char *buf) +{ + Dir dir; + + memmove(dir.name, s->name, NAMELEN); + dir.qid = (Qid){getl(s->qid), 0}; + dir.mode = getl(s->mode); + dir.length = getl(s->length); + if(dir.mode &CHDIR) + dir.length *= DIRLEN; + memmove(dir.uid, s->uid, NAMELEN); + memmove(dir.gid, s->gid, NAMELEN); + dir.atime = getl(s->atime); + dir.mtime = getl(s->mtime); + convD2M(&dir, buf); +} + +void +loadblock(void *buf, uchar *offset, int blocksize) +{ + long block, n; + ulong age; + int i, j; + + block = getl(offset); + if(block < 0) { + block = -block; + cacheage++; + // age has wraped + if(cacheage == 0) { + for(i=0; i<CacheSize; i++) + cache[i].age = 0; + } + j = 0; + age = cache[0].age; + for(i=0; i<CacheSize; i++) { + if(cache[i].age < age) { + age = cache[i].age; + j = i; + } + if(cache[i].block != block) + continue; + memmove(buf, cache[i].data, blocksize); + cache[i].age = cacheage; + return; + } + + n = getl(offset+OffsetSize); + if(n < 0) + n = -n; + n -= block; + if(unsac(buf, data+block, blocksize, n)<0) + sysfatal("unsac failed!"); + memmove(cache[j].data, buf, blocksize); + cache[j].age = cacheage; + cache[j].block = block; + } else { + memmove(buf, data+block, blocksize); + } +} + +Sac* +sacparent(Sac *s) +{ + uchar *blocks; + SacDir *buf; + int per, i, n; + Path *p; + + p = s->path; + if(p == nil || p->up == nil) { + pathfree(p); + *s = root; + return s; + } + p = p->up; + + blocks = data + p->blocks; + per = blocksize/sizeof(SacDir); + i = p->entry/per; + n = per; + if(n > p->nentry-i*per) + n = p->nentry-i*per; + buf = emalloc(per*sizeof(SacDir)); + loadblock(buf, blocks + i*OffsetSize, n*sizeof(SacDir)); + s->SacDir = buf[p->entry-i*per]; + free(buf); + p->ref++; + pathfree(s->path); + s->path = p; + return s; +} + +int +sacdirread(Sac *s, char *p, long off, long cnt) +{ + uchar *blocks; + SacDir *buf; + int iblock, per, i, j, ndir, n; + + blocks = data + getl(s->blocks); + per = blocksize/sizeof(SacDir); + ndir = getl(s->length); + off /= DIRLEN; + cnt /= DIRLEN; + if(off >= ndir) + return 0; + if(cnt > ndir-off) + cnt = ndir-off; + iblock = -1; + buf = emalloc(per*sizeof(SacDir)); + for(i=off; i<off+cnt; i++) { + j = i/per; + if(j != iblock) { + n = per; + if(n > ndir-j*per) + n = ndir-j*per; + loadblock(buf, blocks + j*OffsetSize, n*sizeof(SacDir)); + iblock = j; + } + sacstat(buf+i-j*per, p); + p += DIRLEN; + } + free(buf); + return cnt*DIRLEN; +} + +Sac* +saclookup(Sac *s, char *name) +{ + int ndir; + int top, bot, i, j, k, n, per; + uchar *blocks; + SacDir *buf; + int iblock; + SacDir *sd; + + if(strcmp(name, "..") == 0) + return sacparent(s); + blocks = data + getl(s->blocks); + per = blocksize/sizeof(SacDir); + ndir = getl(s->length); + buf = malloc(per*sizeof(SacDir)); + if(buf == nil) + sysfatal("malloc failed"); + iblock = -1; + + if(1) { + // linear search + for(i=0; i<ndir; i++) { + j = i/per; + if(j != iblock) { + n = per; + if(n > ndir-j*per) + n = ndir-j*per; + loadblock(buf, blocks + j*OffsetSize, n*sizeof(SacDir)); + iblock = j; + } + sd = buf+i-j*per; + k = strcmp(name, sd->name); + if(k == 0) { + s->path = pathalloc(s->path, getl(s->blocks), i, ndir); + s->SacDir = *sd; + free(buf); + return s; + } + } + free(buf); + return 0; + } + + // binary search + top = ndir; + bot = 0; + while(bot != top){ + i = (bot+top)>>1; + j = i/per; + if(j != iblock) { + n = per; + if(n > ndir-j*per) + n = ndir-j*per; + loadblock(buf, blocks + j*OffsetSize, n*sizeof(SacDir)); + iblock = j; + } + j *= per; + sd = buf+i-j; + k = strcmp(name, sd->name); + if(k == 0) { + s->path = pathalloc(s->path, getl(s->blocks), i, ndir); + s->SacDir = *sd; + free(buf); + } + if(k < 0) { + top = i; + sd = buf; + if(strcmp(name, sd->name) < 0) + top = j; + } else { + bot = i+1; + if(ndir-j < per) + i = ndir-j; + else + i = per; + sd = buf+i-1; + if(strcmp(name, sd->name) > 0) + bot = j+i; + } + } + return 0; +} + +Fid * +newfid(int fid) +{ + Fid *f, *ff; + + ff = 0; + for(f = fids; f; f = f->next) + if(f->fid == fid) + return f; + else if(!ff && !f->busy) + ff = f; + if(ff){ + ff->fid = fid; + return ff; + } + f = emalloc(sizeof *f); + memset(f, 0 , sizeof(Fid)); + f->fid = fid; + f->next = fids; + fids = f; + return f; +} + +void +io(void) +{ + char *err; + int n; + + for(;;){ + /* + * reading from a pipe or a network device + * will give an error after a few eof reads + * however, we cannot tell the difference + * between a zero-length read and an interrupt + * on the processes writing to us, + * so we wait for the error + */ + n = read(mfd[0], mdata, sizeof mdata); + if(n == 0) + continue; + if(n < 0) + error("mount read"); + if(convM2S(mdata, &rhdr, n) == 0) + continue; + + if(debug) + fprint(2, "sacfs:<-%F\n", &rhdr); + + thdr.data = mdata + MAXMSG; + if(!fcalls[rhdr.type]) + err = "bad fcall type"; + else + err = (*fcalls[rhdr.type])(newfid(rhdr.fid)); + if(err){ + thdr.type = Rerror; + strncpy(thdr.ename, err, ERRLEN); + }else{ + thdr.type = rhdr.type + 1; + thdr.fid = rhdr.fid; + } + thdr.tag = rhdr.tag; + if(debug) + fprint(2, "ramfs:->%F\n", &thdr);/**/ + n = convS2M(&thdr, mdata); + if(write(mfd[1], mdata, n) != n) + error("mount write"); + } +} + +int +perm(Fid *f, Sac *s, int p) +{ + ulong perm = getl(s->mode); + if((p*Pother) & perm) + return 1; + if(strcmp(f->user, s->gid)==0 && ((p*Pgroup) & perm)) + return 1; + if(strcmp(f->user, s->uid)==0 && ((p*Powner) & perm)) + return 1; + return 0; +} + +void +init(char *file) +{ + SacHeader *hdr; + Dir dir; + int fd; + int i; + uchar *p; + + notify(notifyf); + strcpy(user, getuser()); + + + if(dirstat(file, &dir) < 0) + error("bad file"); + data = emalloc(dir.length); + fd = open(file, OREAD); + if(fd < 0) + error("opening file"); + if(read(fd, data, dir.length) < dir.length) + error("reading file"); + hdr = (SacHeader*)data; + if(getl(hdr->magic) != Magic) + error("bad magic"); + if(getl(hdr->length) != (ulong)(dir.length)) + error("bad length"); + blocksize = getl(hdr->blocksize); + root.SacDir = *(SacDir*)(data + sizeof(SacHeader)); + p = malloc(CacheSize*blocksize); + if(p == nil) + error("allocating cache"); + for(i=0; i<CacheSize; i++) { + cache[i].data = p; + p += blocksize; + } +} + +void +error(char *s) +{ + fprint(2, "%s: %s: %r\n", argv0, s); + exits(s); +} + +void * +emalloc(ulong n) +{ + void *p; + + p = malloc(n); + if(!p) + error("out of memory"); + return p; +} + +void * +erealloc(void *p, ulong n) +{ + p = realloc(p, n); + if(!p) + error("out of memory"); + return p; +} + +void +usage(void) +{ + fprint(2, "usage: %s [-i infd outfd] [-s] [-m mountpoint] sacfsfile\n", argv0); + exits("usage"); +} + +ulong +getl(void *p) +{ + uchar *a = p; + + return (a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3]; +} + diff --git a/sys/src/cmd/disk/sacfs/sacfs.h b/sys/src/cmd/disk/sacfs/sacfs.h new file mode 100755 index 000000000..90c8b6ee8 --- /dev/null +++ b/sys/src/cmd/disk/sacfs/sacfs.h @@ -0,0 +1,29 @@ +typedef struct SacHeader SacHeader; +typedef struct SacDir SacDir; + +enum { + Magic = 0x5acf5, + NAMELEN = 28, +}; + +struct SacDir +{ + char name[NAMELEN]; + char uid[NAMELEN]; + char gid[NAMELEN]; + uchar qid[4]; + uchar mode[4]; + uchar atime[4]; + uchar mtime[4]; + uchar length[8]; + uchar blocks[8]; +}; + +struct SacHeader +{ + uchar magic[4]; + uchar length[8]; + uchar blocksize[4]; + uchar md5[16]; +}; + diff --git a/sys/src/cmd/disk/sacfs/ssort.h b/sys/src/cmd/disk/sacfs/ssort.h new file mode 100755 index 000000000..3f9193942 --- /dev/null +++ b/sys/src/cmd/disk/sacfs/ssort.h @@ -0,0 +1,2 @@ +int ssortbyte(uchar a[], int p[], int shared[], int n); +int ssort(int a[], int s[], int); diff --git a/sys/src/cmd/disk/sacfs/ssort6.c b/sys/src/cmd/disk/sacfs/ssort6.c new file mode 100755 index 000000000..a5032ec07 --- /dev/null +++ b/sys/src/cmd/disk/sacfs/ssort6.c @@ -0,0 +1,419 @@ +#include <u.h> +#include <libc.h> +#include "ssort.h" + +#define pred(i, h) ((t=(i)-(h))<0? t+n: t) +#define succ(i, h) ((t=(i)+(h))>=n? t-n: t) + +enum +{ + BUCK = ~(~0u>>1), /* high bit */ + MAXI = ~0u>>1, /* biggest int */ +}; + +typedef int Elem; +static void qsort2(Elem*, Elem*, int n); +static int ssortit(int a[], int p[], int s[], int q[], int n, int h, int *pe, int nbuck); +static void lift(int si, int q[], int i); +int sharedlen(int i, int j, int s[], int q[]); + +int +ssort(int a[], int s[], int n) +{ + int i, l; + int c, cc, ncc, lab, cum, nbuck; + int k; + int *p = 0; + int result = 0; + int *q = 0; + int *al; + int *pl; + +# define finish(r) if(1){result=r; goto out;}else + + for(k=0,i=0; i<n; i++) + if(a[i] > k) + k = a[i]; /* max element */ + k++; + if(k>n) + finish(2); + + nbuck = 0; + p = malloc(n*sizeof(int)); + if(p == 0) + finish(1); + + if(s) { /* shared lengths */ + q = malloc(((n+1)>>1)*sizeof(int)); + if(q == 0) + finish(1); + for(i=0; i<n; i++) + s[i] = q[i>>1] = MAXI; + q[i>>1] = MAXI; + } + + pl = p + n - k; + al = a; + memset(pl, -1, k*sizeof(int)); + + for(i=0; i<n; i++) { /* (1) link */ + l = a[i]; + al[i] = pl[l]; + pl[l] = i; + } + + for(i=0; i<k; i++) /* check input - no holes */ + if(pl[i]<0) + finish(2); + + + lab = 0; /* (2) create p and label a */ + cum = 0; + i = 0; + for(c = 0; c < k; c++){ + for(cc = pl[c]; cc != -1; cc = ncc){ + ncc = al[cc]; + al[cc] = lab; + cum++; + p[i++] = cc; + } + if(lab + 1 == cum) { + i--; + } else { + p[i-1] |= BUCK; + nbuck++; + } + if(s) { + s[lab] = 0; + lift(0, q, lab); + } + lab = cum; + } + + ssortit(a, p, s, q, n, 1, p+i, nbuck); + memcpy(a, p, n*sizeof(int)); + +out: + free(p); + free(q); + return result; +} + +/* + * calculate the suffix array for the bytes in buf, + * terminated by a unique end marker less than any character in buf + * returns the index of the identity permutation, + * and -1 if there was an error. + */ +int +ssortbyte(uchar buf[], int p[], int s[], int n) +{ + int *a, *q, buckets[256*256]; + int i, last, lastc, cum, c, cc, ncc, lab, id, nbuck; + + a = malloc((n+1)*sizeof(int)); + if(a == nil) + return -1; + + q = nil; + if(s) { /* shared lengths */ + q = malloc(((n+2)>>1)*sizeof(int)); + if(q == nil){ + free(a); + return -1; + } + for(i=0; i<n+1; i++) + s[i] = q[i>>1] = MAXI; + q[i>>1] = MAXI; + } + + memset(buckets, -1, sizeof(buckets)); + c = buf[n-1] << 8; + last = c; + for(i = n - 2; i >= 0; i--){ + c = (buf[i] << 8) | (c >> 8); + a[i] = buckets[c]; + buckets[c] = i; + } + + /* + * end of string comes before anything else + */ + a[n] = 0; + if(s) { + s[0] = 0; + lift(0, q, 0); + } + + lab = 1; + cum = 1; + i = 0; + lastc = 1; /* won't match c & 0xff00 for any c */ + nbuck = 0; + for(c = 0; c < 256*256; c++) { + /* + * last character is followed by unique end of string + */ + if(c == last) { + a[n-1] = lab; + if(s) { + s[lab] = 0; + lift(0, q, lab); + } + cum++; + lab++; + lastc = c & 0xff00; + } + + for(cc = buckets[c]; cc != -1; cc = ncc) { + ncc = a[cc]; + a[cc] = lab; + cum++; + p[i++] = cc; + } + if(lab == cum) + continue; + if(lab + 1 == cum) + i--; + else { + p[i - 1] |= BUCK; + nbuck++; + } + if(s) { + cc = (c & 0xff00) == lastc; + s[lab] = cc; + lift(cc, q, lab); + } + lastc = c & 0xff00; + lab = cum; + } + + id = ssortit(a, p, s, q, n+1, 2, p+i, nbuck); + free(a); + free(q); + return id; +} + +/* + * can get an interval for the shared lengths from [h,3h) by recording h + * rather than h + sharedlen(..) when relabelling. if so, no calls to lift are needed. + */ +static int +ssortit(int a[], int p[], int shared[], int q[], int n, int h, int *pe, int nbuck) +{ + int *s, *ss, *packing, *sorting; + int v, sv, vv, packed, lab, t, i; + + for(; h < n && p < pe; h=2*h) { + packing = p; + nbuck = 0; + + for(sorting = p; sorting < pe; sorting = s){ + /* + * find length of stuff to sort + */ + lab = a[*sorting]; + for(s = sorting; ; s++) { + sv = *s; + v = a[succ(sv & ~BUCK, h)]; + if(v & BUCK) + v = lab; + a[sv & ~BUCK] = v | BUCK; + if(sv & BUCK) + break; + } + *s++ &= ~BUCK; + nbuck++; + + qsort2(sorting, a, s - sorting); + + v = a[*sorting]; + a[*sorting] = lab; + packed = 0; + for(ss = sorting + 1; ss < s; ss++) { + sv = *ss; + vv = a[sv]; + if(vv == v) { + *packing++ = ss[-1]; + packed++; + } else { + if(packed) { + *packing++ = ss[-1] | BUCK; + } + lab += packed + 1; + if(shared) { + v = h + sharedlen(v&~BUCK, vv&~BUCK, shared, q); + shared[lab] = v; + lift(v, q, lab); + } + packed = 0; + v = vv; + } + a[sv] = lab; + } + if(packed) { + *packing++ = ss[-1] | BUCK; + } + } + pe = packing; + } + + /* + * reconstuct the permutation matrix + * return index of the entire string + */ + v = a[0]; + for(i = 0; i < n; i++) + p[a[i]] = i; + + return v; +} + +/* Propagate a new entry s[i], with value si, into q[]. */ +static void +lift(int si, int q[], int i) +{ + for(i >>= 1; q[i] > si; i &= ~-i) /* squash the low 1-bit */ + q[i] = si; +} + +/* + * Find in s[] the minimum value over the interval i<=k<=j, using + * tree q[] to do logarithmic, rather than linear search + */ +int +sharedlen(int i, int j, int s[], int q[]) +{ + int k, v; + int min = MAXI; + int max = 0; + + if(i > j) { /* swap i & j */ + i ^= j; + j ^= i; + i ^= j; + } + i++; + while(i <= j && min > max) { + k = i & -i; + if(i & 1) + v = s[i]; + else + v = q[i>>1]; + if(i+k > j+1) { + if(v > max) + max = v; + if(s[i] < min) + min = s[i]; + i++; + } else { + if(v < min) + min = v; + i += k; + } + } + return min; +} + +/* + * qsort specialized for sorting permutations based on successors + */ +static void +vecswap2(Elem *a, Elem *b, int n) +{ + while (n-- > 0) { + Elem t = *a; + *a++ = *b; + *b++ = t; + } +} + +#define swap2(a, b) { t = *(a); *(a) = *(b); *(b) = t; } +#define ptr2char(i, asucc) (asucc[*(i)]) + +static Elem* +med3(Elem *a, Elem *b, Elem *c, Elem *asucc) +{ + Elem va, vb, vc; + + if ((va=ptr2char(a, asucc)) == (vb=ptr2char(b, asucc))) + return a; + if ((vc=ptr2char(c, asucc)) == va || vc == vb) + return c; + return va < vb ? + (vb < vc ? b : (va < vc ? c : a)) + : (vb > vc ? b : (va < vc ? a : c)); +} + +static void +inssort(Elem *a, Elem *asucc, int n) +{ + Elem *pi, *pj, t; + + for (pi = a + 1; --n > 0; pi++) + for (pj = pi; pj > a; pj--) { + if(ptr2char(pj-1, asucc) <= ptr2char(pj, asucc)) + break; + swap2(pj, pj-1); + } +} + +static void +qsort2(Elem *a, Elem *asucc, int n) +{ + Elem d, r, partval; + Elem *pa, *pb, *pc, *pd, *pl, *pm, *pn, t; + + if (n < 15) { + inssort(a, asucc, n); + return; + } + pl = a; + pm = a + (n >> 1); + pn = a + (n-1); + if (n > 30) { /* On big arrays, pseudomedian of 9 */ + d = (n >> 3); + pl = med3(pl, pl+d, pl+2*d, asucc); + pm = med3(pm-d, pm, pm+d, asucc); + pn = med3(pn-2*d, pn-d, pn, asucc); + } + pm = med3(pl, pm, pn, asucc); + swap2(a, pm); + partval = ptr2char(a, asucc); + pa = pb = a + 1; + pc = pd = a + n-1; + for (;;) { + while (pb <= pc && (r = ptr2char(pb, asucc)-partval) <= 0) { + if (r == 0) { + swap2(pa, pb); + pa++; + } + pb++; + } + while (pb <= pc && (r = ptr2char(pc, asucc)-partval) >= 0) { + if (r == 0) { + swap2(pc, pd); + pd--; + } + pc--; + } + if (pb > pc) + break; + swap2(pb, pc); + pb++; + pc--; + } + pn = a + n; + r = pa-a; + if(pb-pa < r) + r = pb-pa; + vecswap2(a, pb-r, r); + r = pn-pd-1; + if(pd-pc < r) + r = pd-pc; + vecswap2(pb, pn-r, r); + if ((r = pb-pa) > 1) + qsort2(a, asucc, r); + if ((r = pd-pc) > 1) + qsort2(a + n-r, asucc, r); +} diff --git a/sys/src/cmd/disk/sacfs/unsac.c b/sys/src/cmd/disk/sacfs/unsac.c new file mode 100755 index 000000000..e3f1ab09c --- /dev/null +++ b/sys/src/cmd/disk/sacfs/unsac.c @@ -0,0 +1,620 @@ +#include <u.h> +#include <libc.h> +#include "sac.h" + +typedef struct Huff Huff; +typedef struct Mtf Mtf; +typedef struct Decode Decode; + +enum +{ + ZBase = 2, /* base of code to encode 0 runs */ + LitBase = ZBase-1, /* base of literal values */ + MaxLit = 256, + + MaxLeaf = MaxLit+LitBase, + MaxHuffBits = 16, /* max bits in a huffman code */ + MaxFlatbits = 5, /* max bits decoded in flat table */ + + CombLog = 4, + CombSpace = 1 << CombLog, /* mtf speedup indices spacing */ + CombMask = CombSpace - 1, +}; + +struct Mtf +{ + int maxcomb; /* index of last valid comb */ + uchar prev[MaxLit]; + uchar next[MaxLit]; + uchar comb[MaxLit / CombSpace + 1]; +}; + +struct Huff +{ + int maxbits; + int flatbits; + ulong flat[1<<MaxFlatbits]; + ulong maxcode[MaxHuffBits]; + ulong last[MaxHuffBits]; + ulong decode[MaxLeaf]; +}; + +struct Decode{ + Huff tab; + Mtf mtf; + int nbits; + ulong bits; + int nzero; + int base; + ulong maxblocksym; + + jmp_buf errjmp; + + uchar *src; /* input buffer */ + uchar *smax; /* limit */ +}; + +static void fatal(Decode *dec, char*); + +static int hdec(Decode*); +static void recvtab(Decode*, Huff*, int, ushort*); +static ulong bitget(Decode*, int); +static int mtf(uchar*, int); + +#define FORWARD 0 + +static void +mtflistinit(Mtf *m, uchar *front, int n) +{ + int last, me, f, i, comb; + + if(n == 0) + return; + + /* + * add all entries to free list + */ + last = MaxLit - 1; + for(i = 0; i < MaxLit; i++){ + m->prev[i] = last; + m->next[i] = i + 1; + last = i; + } + m->next[last] = 0; + f = 0; + + /* + * pull valid entries off free list and enter into mtf list + */ + comb = 0; + last = front[0]; + for(i = 0; i < n; i++){ + me = front[i]; + + f = m->next[me]; + m->prev[f] = m->prev[me]; + m->next[m->prev[f]] = f; + + m->next[last] = me; + m->prev[me] = last; + last = me; + if((i & CombMask) == 0) + m->comb[comb++] = me; + } + + /* + * pad out the list with dummies to the next comb, + * using free entries + */ + for(; i & CombMask; i++){ + me = f; + + f = m->next[me]; + m->prev[f] = m->prev[me]; + m->next[m->prev[f]] = f; + + m->next[last] = me; + m->prev[me] = last; + last = me; + } + me = front[0]; + m->next[last] = me; + m->prev[me] = last; + m->comb[comb] = me; + m->maxcomb = comb; +} + +static int +mtflist(Mtf *m, int pos) +{ + uchar *next, *prev, *mycomb; + int c, c0, pc, nc, off; + + if(pos == 0) + return m->comb[0]; + + next = m->next; + prev = m->prev; + mycomb = &m->comb[pos >> CombLog]; + off = pos & CombMask; + if(off >= CombSpace / 2){ + c = mycomb[1]; + for(; off < CombSpace; off++) + c = prev[c]; + }else{ + c = *mycomb; + for(; off; off--) + c = next[c]; + } + + nc = next[c]; + pc = prev[c]; + prev[nc] = pc; + next[pc] = nc; + + for(; mycomb > m->comb; mycomb--) + *mycomb = prev[*mycomb]; + c0 = *mycomb; + *mycomb = c; + mycomb[m->maxcomb] = c; + + next[c] = c0; + pc = prev[c0]; + prev[c] = pc; + prev[c0] = c; + next[pc] = c; + return c; +} + +static void +hdecblock(Decode *dec, ulong n, ulong I, uchar *buf, ulong *sums, ulong *prev) +{ + ulong i, nn, sum; + int m, z, zz, c; + + nn = I; + n--; + i = 0; +again: + for(; i < nn; i++){ + while((m = hdec(dec)) == 0 && i + dec->nzero < n) + ; + if(z = dec->nzero){ + dec->nzero = 0; + c = dec->mtf.comb[0]; + sum = sums[c]; + sums[c] = sum + z; + + z += i; + zz = z; + if(i < I && z > I){ + zz = I; + z++; + } + + zagain: + for(; i < zz; i++){ + buf[i] = c; + prev[i] = sum++; + } + if(i != z){ + zz = z; + nn = ++n; + i++; + goto zagain; + } + if(i == nn){ + if(i == n) + return; + nn = ++n; + i++; + } + } + + c = mtflist(&dec->mtf, m); + + buf[i] = c; + sum = sums[c]; + prev[i] = sum++; + sums[c] = sum; + + } + if(i == n) + return; + nn = ++n; + i++; + goto again; +} + +int +unsac(uchar *dst, uchar *src, int n, int nsrc) +{ + Decode *dec; + uchar *buf, *front; + ulong *prev, *sums; + ulong sum, i, I; + int m, j, c; + + dec = malloc(sizeof *dec); + buf = malloc(n+2); + prev = malloc((n+2) * sizeof *prev); + front = malloc(MaxLit * sizeof *front); + sums = malloc(MaxLit * sizeof *sums); + + if(dec == nil || buf == nil || prev == nil || front == nil || sums == nil || setjmp(dec->errjmp)){ + free(dec); + free(buf); + free(prev); + free(front); + free(sums); + return -1; + } + + dec->src = src; + dec->smax = src + nsrc; + + dec->nbits = 0; + dec->bits = 0; + dec->nzero = 0; + for(i = 0; i < MaxLit; i++) + front[i] = i; + + n++; + I = bitget(dec, 16); + if(I >= n) + fatal(dec, "corrupted input"); + + /* + * decode the character usage map + */ + for(i = 0; i < MaxLit; i++) + sums[i] = 0; + c = bitget(dec, 1); + for(i = 0; i < MaxLit; ){ + m = bitget(dec, 8) + 1; + while(m--){ + if(i >= MaxLit) + fatal(dec, "corrupted char map"); + front[i++] = c; + } + c = c ^ 1; + } + + /* + * initialize mtf state + */ + c = 0; + for(i = 0; i < MaxLit; i++) + if(front[i]) + front[c++] = i; + mtflistinit(&dec->mtf, front, c); + dec->maxblocksym = c + LitBase; + + /* + * huffman decoding, move to front decoding, + * along with character counting + */ + dec->base = 1; + recvtab(dec, &dec->tab, MaxLeaf, nil); + hdecblock(dec, n, I, buf, sums, prev); + + sum = 1; + for(i = 0; i < MaxLit; i++){ + c = sums[i]; + sums[i] = sum; + sum += c; + } + + i = 0; + for(j = n - 2; j >= 0; j--){ + if(i > n || i < 0 || i == I) + fatal(dec, "corrupted data"); + c = buf[i]; + dst[j] = c; + i = prev[i] + sums[c]; + } + + free(dec); + free(buf); + free(prev); + free(front); + free(sums); + return n; +} + +static ulong +bitget(Decode *dec, int nb) +{ + int c; + + while(dec->nbits < nb){ + if(dec->src >= dec->smax) + fatal(dec, "premature eof 1"); + c = *dec->src++; + dec->bits <<= 8; + dec->bits |= c; + dec->nbits += 8; + } + dec->nbits -= nb; + return (dec->bits >> dec->nbits) & ((1 << nb) - 1); +} + +static void +fillbits(Decode *dec) +{ + int c; + + while(dec->nbits < 24){ + if(dec->src >= dec->smax) + fatal(dec, "premature eof 2"); + c = *dec->src++; + dec->bits <<= 8; + dec->bits |= c; + dec->nbits += 8; + } +} + +/* + * decode one symbol + */ +static int +hdecsym(Decode *dec, Huff *h, int b) +{ + long c; + ulong bits; + int nbits; + + bits = dec->bits; + nbits = dec->nbits; + for(; (c = bits >> (nbits - b)) > h->maxcode[b]; b++) + ; + if(b > h->maxbits) + fatal(dec, "too many bits consumed"); + dec->nbits = nbits - b; + return h->decode[h->last[b] - c]; +} + +static int +hdec(Decode *dec) +{ + ulong c; + int nbits, nb; + + if(dec->nbits < dec->tab.maxbits) + fillbits(dec); + nbits = dec->nbits; + dec->bits &= (1 << nbits) - 1; + c = dec->tab.flat[dec->bits >> (nbits - dec->tab.flatbits)]; + nb = c & 0xff; + c >>= 8; + if(nb == 0xff) + c = hdecsym(dec, &dec->tab, c); + else + dec->nbits = nbits - nb; + + /* + * reverse funny run-length coding + */ + if(c < ZBase){ + dec->nzero += dec->base << c; + dec->base <<= 1; + return 0; + } + + dec->base = 1; + c -= LitBase; + return c; +} + +static void +hufftab(Decode *dec, Huff *h, char *hb, ulong *bitcount, int maxleaf, int maxbits, int flatbits) +{ + ulong c, mincode, code, nc[MaxHuffBits]; + int i, b, ec; + + h->maxbits = maxbits; + if(maxbits < 0) + return; + + code = 0; + c = 0; + for(b = 0; b <= maxbits; b++){ + h->last[b] = c; + c += bitcount[b]; + mincode = code << 1; + nc[b] = mincode; + code = mincode + bitcount[b]; + if(code > (1 << b)) + fatal(dec, "corrupted huffman table"); + h->maxcode[b] = code - 1; + h->last[b] += code - 1; + } + if(code != (1 << maxbits)) + fatal(dec, "huffman table not full"); + if(flatbits > maxbits) + flatbits = maxbits; + h->flatbits = flatbits; + + b = 1 << flatbits; + for(i = 0; i < b; i++) + h->flat[i] = ~0; + + /* + * initialize the flat table to include the minimum possible + * bit length for each code prefix + */ + for(b = maxbits; b > flatbits; b--){ + code = h->maxcode[b]; + if(code == -1) + break; + mincode = code + 1 - bitcount[b]; + mincode >>= b - flatbits; + code >>= b - flatbits; + for(; mincode <= code; mincode++) + h->flat[mincode] = (b << 8) | 0xff; + } + + for(i = 0; i < maxleaf; i++){ + b = hb[i]; + if(b == -1) + continue; + c = nc[b]++; + if(b <= flatbits){ + code = (i << 8) | b; + ec = (c + 1) << (flatbits - b); + if(ec > (1<<flatbits)) + fatal(dec, "flat code too big"); + for(c <<= (flatbits - b); c < ec; c++) + h->flat[c] = code; + }else{ + c = h->last[b] - c; + if(c >= maxleaf) + fatal(dec, "corrupted huffman table"); + h->decode[c] = i; + } + } +} + +static void +elimBit(int b, char *tmtf, int maxbits) +{ + int bb; + + for(bb = 0; bb < maxbits; bb++) + if(tmtf[bb] == b) + break; + while(++bb <= maxbits) + tmtf[bb - 1] = tmtf[bb]; +} + +static int +elimBits(int b, ulong *bused, char *tmtf, int maxbits) +{ + int bb, elim; + + if(b < 0) + return 0; + + elim = 0; + + /* + * increase bits counts for all descendants + */ + for(bb = b + 1; bb < maxbits; bb++){ + bused[bb] += 1 << (bb - b); + if(bused[bb] == (1 << bb)){ + elim++; + elimBit(bb, tmtf, maxbits); + } + } + + /* + * steal bits from parent & check for fullness + */ + for(; b >= 0; b--){ + bused[b]++; + if(bused[b] == (1 << b)){ + elim++; + elimBit(b, tmtf, maxbits); + } + if((bused[b] & 1) == 0) + break; + } + return elim; +} + +static void +recvtab(Decode *dec, Huff *tab, int maxleaf, ushort *map) +{ + ulong bitcount[MaxHuffBits+1], bused[MaxHuffBits+1]; + char tmtf[MaxHuffBits+1], *hb; + int i, b, ttb, m, maxbits, max, elim; + + hb = malloc(MaxLeaf * sizeof *hb); + if(hb == nil) + fatal(dec, "out of memory"); + + /* + * read the tables for the tables + */ + max = 8; + for(i = 0; i <= MaxHuffBits; i++){ + bitcount[i] = 0; + tmtf[i] = i; + bused[i] = 0; + } + tmtf[0] = -1; + tmtf[max] = 0; + elim = 0; + maxbits = -1; + for(i = 0; i <= MaxHuffBits && elim != max; i++){ + ttb = 4; + while(max - elim < (1 << (ttb-1))) + ttb--; + b = bitget(dec, ttb); + if(b > max - elim) + fatal(dec, "corrupted huffman table table"); + b = tmtf[b]; + hb[i] = b; + bitcount[b]++; + if(b > maxbits) + maxbits = b; + + elim += elimBits(b, bused, tmtf, max); + } + if(elim != max) + fatal(dec, "incomplete huffman table table"); + hufftab(dec, tab, hb, bitcount, i, maxbits, MaxFlatbits); + for(i = 0; i <= MaxHuffBits; i++){ + tmtf[i] = i; + bitcount[i] = 0; + bused[i] = 0; + } + tmtf[0] = -1; + tmtf[MaxHuffBits] = 0; + elim = 0; + maxbits = -1; + for(i = 0; i < maxleaf && elim != MaxHuffBits; i++){ + if(dec->nbits <= tab->maxbits) + fillbits(dec); + dec->bits &= (1 << dec->nbits) - 1; + m = tab->flat[dec->bits >> (dec->nbits - tab->flatbits)]; + b = m & 0xff; + m >>= 8; + if(b == 0xff) + m = hdecsym(dec, tab, m); + else + dec->nbits -= b; + b = tmtf[m]; + for(; m > 0; m--) + tmtf[m] = tmtf[m-1]; + tmtf[0] = b; + + if(b > MaxHuffBits) + fatal(dec, "bit length too big"); + m = i; + if(map != nil) + m = map[m]; + hb[m] = b; + bitcount[b]++; + if(b > maxbits) + maxbits = b; + elim += elimBits(b, bused, tmtf, MaxHuffBits); + } + if(elim != MaxHuffBits && elim != 0) + fatal(dec, "incomplete huffman table"); + if(map != nil) + for(; i < maxleaf; i++) + hb[map[i]] = -1; + + hufftab(dec, tab, hb, bitcount, i, maxbits, MaxFlatbits); + + free(hb); +} + +static void +fatal(Decode *dec, char *msg) +{ + print("%s: %s\n", argv0, msg); + longjmp(dec->errjmp, 1); +} |