commit e1dddc053287874e82e2b67f95ccee7d7bc63e22 from: wkj date: Thu Jun 17 01:46:29 2004 UTC Import proto file parser for dump9660. commit - 778df25e996f8344a917fc5d3cf1b962ab728ada commit + e1dddc053287874e82e2b67f95ccee7d7bc63e22 blob - /dev/null blob + a887866e567d50827c27d71a4cba4310ca1aafb5 (mode 644) --- /dev/null +++ include/disk.h @@ -0,0 +1,65 @@ +#pragma src "/sys/src/libdisk" +#pragma lib "libdisk.a" + +/* SCSI interface */ +typedef struct Scsi Scsi; +struct Scsi { + QLock lk; + char* inquire; + int rawfd; + int nchange; + ulong changetime; +}; + +enum { + Sread = 0, + Swrite, + Snone, +}; + +char* scsierror(int, int); +int scsicmd(Scsi*, uchar*, int, void*, int, int); +int scsi(Scsi*, uchar*, int, void*, int, int); +Scsi* openscsi(char*); +int scsiready(Scsi*); + +extern int scsiverbose; + +/* disk partition interface */ +typedef struct Disk Disk; +struct Disk { + char *prefix; + char *part; + int fd; + int wfd; + int ctlfd; + int rdonly; + int type; + + vlong secs; + vlong secsize; + vlong size; + vlong offset; /* within larger disk, perhaps */ + int width; /* of disk size in bytes as decimal string */ + int c; + int h; + int s; + int chssrc; +}; + +Disk* opendisk(char*, int, int); + +enum { + Tfile = 0, + Tsd, + Tfloppy, + + Gpart = 0, /* partition info source */ + Gdisk, + Gguess, +}; + +/* proto file parsing */ +typedef void Protoenum(char *new, char *old, Dir *d, void *a); +typedef void Protowarn(char *msg, void *a); +int rdproto(char*, char*, Protoenum*, Protowarn*, void*); blob - /dev/null blob + bb34b65020ad02133c5183f3ac9b68c348ec5ad6 (mode 644) --- /dev/null +++ src/libdisk/disk.c @@ -0,0 +1,350 @@ +#include +#include +#include +#include +#include + +static Disk* +mkwidth(Disk *disk) +{ + char buf[40]; + + sprint(buf, "%lld", disk->size); + disk->width = strlen(buf); + return disk; +} + +/* + * Discover the disk geometry by various sleazeful means. + * + * First, if there is a partition table in sector 0, + * see if all the partitions have the same end head + * and sector; if so, we'll assume that that's the + * right count. + * + * If that fails, we'll try looking at the geometry that the ATA + * driver supplied, if any, and translate that as a + * BIOS might. + * + * If that too fails, which should only happen on a SCSI + * disk with no currently defined partitions, we'll try + * various common (h, s) pairs used by BIOSes when faking + * the geometries. + */ +typedef struct Table Table; +typedef struct Tentry Tentry; +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 */ + uchar xsize[4]; /* size in sectors */ +}; +enum { + Toffset = 446, /* offset of partition table in sector */ + Magic0 = 0x55, + Magic1 = 0xAA, + NTentry = 4, +}; +struct Table { + Tentry entry[NTentry]; + uchar magic[2]; +}; +static int +partitiongeometry(Disk *disk) +{ + char *rawname; + int i, h, rawfd, s; + uchar buf[512]; + Table *t; + + t = (Table*)(buf + Toffset); + + /* + * look for an MBR first in the /dev/sdXX/data partition, otherwise + * attempt to fall back on the current partition. + */ + rawname = malloc(strlen(disk->prefix) + 5); /* prefix + "data" + nul */ + if(rawname == nil) + return -1; + + strcpy(rawname, disk->prefix); + strcat(rawname, "data"); + rawfd = open(rawname, OREAD); + free(rawname); + if(rawfd >= 0 + && seek(rawfd, 0, 0) >= 0 + && readn(rawfd, buf, 512) == 512 + && t->magic[0] == Magic0 + && t->magic[1] == Magic1) { + close(rawfd); + } else { + if(rawfd >= 0) + close(rawfd); + if(seek(disk->fd, 0, 0) < 0 + || readn(disk->fd, buf, 512) != 512 + || t->magic[0] != Magic0 + || t->magic[1] != Magic1) { + return -1; + } + } + + h = s = -1; + for(i=0; ientry[i].type == 0) + continue; + + t->entry[i].ends &= 63; + if(h == -1) { + h = t->entry[i].endh; + s = t->entry[i].ends; + } else { + /* + * Only accept the partition info if every + * partition is consistent. + */ + if(h != t->entry[i].endh || s != t->entry[i].ends) + return -1; + } + } + + if(h == -1) + return -1; + + disk->h = h+1; /* heads count from 0 */ + disk->s = s; /* sectors count from 1 */ + disk->c = disk->secs / (disk->h*disk->s); + disk->chssrc = Gpart; + return 0; +} + +/* + * If there is ATA geometry, use it, perhaps massaged. + */ +static int +drivergeometry(Disk *disk) +{ + int m; + + if(disk->c == 0 || disk->h == 0 || disk->s == 0) + return -1; + + disk->chssrc = Gdisk; + if(disk->c < 1024) + return 0; + + switch(disk->h) { + case 15: + disk->h = 255; + disk->c /= 17; + return 0; + + default: + for(m = 2; m*disk->h < 256; m *= 2) { + if(disk->c/m < 1024) { + disk->c /= m; + disk->h *= m; + return 0; + } + } + + /* set to 255, 63 and be done with it */ + disk->h = 255; + disk->s = 63; + disk->c = disk->secs / (disk->h * disk->s); + return 0; + } + return -1; /* not reached */ +} + +/* + * There's no ATA geometry and no partitions. + * Our guess is as good as anyone's. + */ +static struct { + int h; + int s; +} guess[] = { + 64, 32, + 64, 63, + 128, 63, + 255, 63, +}; +static int +guessgeometry(Disk *disk) +{ + int i; + long c; + + disk->chssrc = Gguess; + c = 1024; + for(i=0; i= disk->secs) { + disk->h = guess[i].h; + disk->s = guess[i].s; + disk->c = disk->secs / (disk->h * disk->s); + return 0; + } + + /* use maximum values */ + disk->h = 255; + disk->s = 63; + disk->c = disk->secs / (disk->h * disk->s); + return 0; +} + +static void +findgeometry(Disk *disk) +{ + if(partitiongeometry(disk) < 0 + && drivergeometry(disk) < 0 + && guessgeometry(disk) < 0) { /* can't happen */ + print("we're completely confused about your disk; sorry\n"); + assert(0); + } +} + +static Disk* +openfile(Disk *disk) +{ + Dir *d; + + if((d = dirfstat(disk->fd)) == nil){ + free(disk); + return nil; + } + + disk->secsize = 512; + disk->size = d->length; + disk->secs = disk->size / disk->secsize; + disk->offset = 0; + free(d); + + findgeometry(disk); + return mkwidth(disk); +} + +static Disk* +opensd(Disk *disk) +{ + Biobuf b; + char *p, *f[10]; + int nf; + + Binit(&b, disk->ctlfd, OREAD); + while(p = Brdline(&b, '\n')) { + p[Blinelen(&b)-1] = '\0'; + nf = tokenize(p, f, nelem(f)); + if(nf >= 3 && strcmp(f[0], "geometry") == 0) { + disk->secsize = strtoll(f[2], 0, 0); + if(nf >= 6) { + disk->c = strtol(f[3], 0, 0); + disk->h = strtol(f[4], 0, 0); + disk->s = strtol(f[5], 0, 0); + } + } + if(nf >= 4 && strcmp(f[0], "part") == 0 && strcmp(f[1], disk->part) == 0) { + disk->offset = strtoll(f[2], 0, 0); + disk->secs = strtoll(f[3], 0, 0) - disk->offset; + } + } + + + disk->size = disk->secs * disk->secsize; + if(disk->size <= 0) { + strcpy(disk->part, ""); + disk->type = Tfile; + return openfile(disk); + } + + findgeometry(disk); + return mkwidth(disk); +} + +Disk* +opendisk(char *disk, int rdonly, int noctl) +{ + char *p, *q; + Disk *d; + + d = malloc(sizeof(*d)); + if(d == nil) + return nil; + + d->fd = d->wfd = d->ctlfd = -1; + d->rdonly = rdonly; + + d->fd = open(disk, OREAD); + if(d->fd < 0) { + werrstr("cannot open disk file"); + free(d); + return nil; + } + + if(rdonly == 0) { + d->wfd = open(disk, OWRITE); + if(d->wfd < 0) + d->rdonly = 1; + } + + if(noctl) + return openfile(d); + + p = malloc(strlen(disk) + 4); /* 4: slop for "ctl\0" */ + if(p == nil) { + close(d->wfd); + close(d->fd); + free(d); + return nil; + } + strcpy(p, disk); + + /* check for floppy(3) disk */ + if(strlen(p) >= 7) { + q = p+strlen(p)-7; + if(q[0] == 'f' && q[1] == 'd' && isdigit(q[2]) && strcmp(q+3, "disk") == 0) { + strcpy(q+3, "ctl"); + if((d->ctlfd = open(p, ORDWR)) >= 0) { + *q = '\0'; + d->prefix = p; + d->type = Tfloppy; + return openfile(d); + } + } + } + + /* attempt to find sd(3) disk or partition */ + if(q = strrchr(p, '/')) + q++; + else + q = p; + + strcpy(q, "ctl"); + if((d->ctlfd = open(p, ORDWR)) >= 0) { + *q = '\0'; + d->prefix = p; + d->type = Tsd; + d->part = strdup(disk+(q-p)); + if(d->part == nil){ + close(d->ctlfd); + close(d->wfd); + close(d->fd); + free(p); + free(d); + return nil; + } + return opensd(d); + } + + *q = '\0'; + d->prefix = p; + /* assume we just have a normal file */ + d->type = Tfile; + return openfile(d); +} + blob - /dev/null blob + af49af6bf71986f0f8eb606900475a910b809719 (mode 644) --- /dev/null +++ src/libdisk/mkfile @@ -0,0 +1,19 @@ +<$PLAN9/src/mkhdr + +OFILES=\ + disk.$O\ + proto.$O\ + scsi.$O\ + +HFILES=$PLAN9/include/disk.h +LIB=libdisk.a + +UPDATE=\ + mkfile\ + $HFILES\ + ${OFILES:%.$O=%.c}\ + +<$PLAN9/src/mksyslib + +$O.test: test.$O $LIB + $LD -o $target test.$O $LIB blob - /dev/null blob + 8c94ddcadf2ad1eaab8766f241c403add1340aad (mode 644) --- /dev/null +++ src/libdisk/proto.c @@ -0,0 +1,513 @@ +#include +#include +#include +#include +#include +#include + +enum { + LEN = 8*1024, + HUNKS = 128, +}; + +#undef warn +#define warn protowarn + +#undef getmode +#define getmode protogetmode + +typedef struct File File; +struct File{ + char *new; + char *elem; + char *old; + char *uid; + char *gid; + ulong mode; +}; + +typedef void Mkfserr(char*, void*); +typedef void Mkfsenum(char*, char*, Dir*, void*); + +typedef struct Name Name; +struct Name { + int n; + char *s; +}; + +typedef struct Mkaux Mkaux; +struct Mkaux { + Mkfserr *warn; + Mkfsenum *mkenum; + char *root; + char *proto; + jmp_buf jmp; + Biobuf *b; + + Name oldfile; + Name fullname; + int lineno; + int indent; + + void *a; +}; + +static void domkfs(Mkaux *mkaux, File *me, int level); + +static int copyfile(Mkaux*, File*, Dir*, int); +static void freefile(File*); +static File* getfile(Mkaux*, File*); +static char* getmode(Mkaux*, char*, ulong*); +static char* getname(Mkaux*, char*, char**); +static char* getpath(Mkaux*, char*); +static int mkfile(Mkaux*, File*); +static char* mkpath(Mkaux*, char*, char*); +static void mktree(Mkaux*, File*, int); +static void setnames(Mkaux*, File*); +static void skipdir(Mkaux*); +static void warn(Mkaux*, char *, ...); + +//static void +//mprint(char *new, char *old, Dir *d, void*) +//{ +// print("%s %s %D\n", new, old, d); +//} + +int +rdproto(char *proto, char *root, Mkfsenum *mkenum, Mkfserr *mkerr, void *a) +{ + Mkaux mx, *m; + File file; + int rv; + + m = &mx; + memset(&mx, 0, sizeof mx); + if(root == nil) + root = "/"; + + m->root = root; + m->warn = mkerr; + m->mkenum = mkenum; + m->a = a; + m->proto = proto; + m->lineno = 0; + m->indent = 0; + if((m->b = Bopen(proto, OREAD)) == nil) { + werrstr("open '%s': %r", proto); + return -1; + } + + memset(&file, 0, sizeof file); + file.new = ""; + file.old = nil; + + *(&rv) = 0; + if(setjmp(m->jmp) == 0) + domkfs(m, &file, -1); + else + rv = -1; + free(m->oldfile.s); + free(m->fullname.s); + return rv; +} + +static void* +emalloc(Mkaux *mkaux, ulong n) +{ + void *v; + + v = malloc(n); + if(v == nil) + longjmp(mkaux->jmp, 1); /* memory leak */ + memset(v, 0, n); + return v; +} + +static char* +estrdup(Mkaux *mkaux, char *s) +{ + s = strdup(s); + if(s == nil) + longjmp(mkaux->jmp, 1); /* memory leak */ + return s; +} + +static void +domkfs(Mkaux *mkaux, File *me, int level) +{ + File *child; + int rec; + + child = getfile(mkaux, me); + if(!child) + return; + if((child->elem[0] == '+' || child->elem[0] == '*') && child->elem[1] == '\0'){ + rec = child->elem[0] == '+'; + free(child->new); + child->new = estrdup(mkaux, me->new); + setnames(mkaux, child); + mktree(mkaux, child, rec); + freefile(child); + child = getfile(mkaux, me); + } + while(child && mkaux->indent > level){ + if(mkfile(mkaux, child)) + domkfs(mkaux, child, mkaux->indent); + freefile(child); + child = getfile(mkaux, me); + } + if(child){ + freefile(child); + Bseek(mkaux->b, -Blinelen(mkaux->b), 1); + mkaux->lineno--; + } +} + +static void +mktree(Mkaux *mkaux, File *me, int rec) +{ + File child; + Dir *d; + int i, n, fd; + + fd = open(mkaux->oldfile.s, OREAD); + if(fd < 0){ + warn(mkaux, "can't open %s: %r", mkaux->oldfile.s); + return; + } + + child = *me; + while((n = dirread(fd, &d)) > 0){ + for(i = 0; i < n; i++){ + child.new = mkpath(mkaux, me->new, d[i].name); + if(me->old) + child.old = mkpath(mkaux, me->old, d[i].name); + child.elem = d[i].name; + setnames(mkaux, &child); + if((!(d[i].mode&DMDIR) || rec) && copyfile(mkaux, &child, &d[i], 1) && rec) + mktree(mkaux, &child, rec); + free(child.new); + if(child.old) + free(child.old); + } + } + close(fd); +} + +static int +mkfile(Mkaux *mkaux, File *f) +{ + Dir *d; + + if((d = dirstat(mkaux->oldfile.s)) == nil){ + warn(mkaux, "can't stat file %s: %r", mkaux->oldfile.s); + skipdir(mkaux); + return 0; + } + return copyfile(mkaux, f, d, 0); +} + +enum { + SLOP = 30 +}; + +static void +setname(Mkaux *mkaux, Name *name, char *s1, char *s2) +{ + int l; + + l = strlen(s1)+strlen(s2)+1; + if(name->n < l+SLOP/2) { + free(name->s); + name->s = emalloc(mkaux, l+SLOP); + name->n = l+SLOP; + } + snprint(name->s, name->n, "%s%s%s", s1, s1[0]==0 || s1[strlen(s1)-1]!='/' ? "/" : "", s2); +} + +static int +copyfile(Mkaux *mkaux, File *f, Dir *d, int permonly) +{ + Dir *nd; + ulong xmode; + char *p; + + setname(mkaux, &mkaux->fullname, mkaux->root, f->old ? f->old : f->new); + /* + * Extra stat here is inefficient but accounts for binds. + */ + if((nd = dirstat(mkaux->fullname.s)) != nil) + d = nd; + + d->name = f->elem; + if(d->type != 'M'){ + d->uid = "sys"; + d->gid = "sys"; + xmode = (d->mode >> 6) & 7; + d->mode |= xmode | (xmode << 3); + } + if(strcmp(f->uid, "-") != 0) + d->uid = f->uid; + if(strcmp(f->gid, "-") != 0) + d->gid = f->gid; + if(f->mode != ~0){ + if(permonly) + d->mode = (d->mode & ~0666) | (f->mode & 0666); + else if((d->mode&DMDIR) != (f->mode&DMDIR)) + warn(mkaux, "inconsistent mode for %s", f->new); + else + d->mode = f->mode; + } + + if(p = strrchr(f->new, '/')) + d->name = p+1; + else + d->name = f->new; + + mkaux->mkenum(f->new, mkaux->fullname.s, d, mkaux->a); + xmode = d->mode; + free(nd); + return (xmode&DMDIR) != 0; +} + +static char * +mkpath(Mkaux *mkaux, char *prefix, char *elem) +{ + char *p; + int n; + + n = strlen(prefix) + strlen(elem) + 2; + p = emalloc(mkaux, n); + strcpy(p, prefix); + strcat(p, "/"); + strcat(p, elem); + return p; +} + +static void +setnames(Mkaux *mkaux, File *f) +{ + + if(f->old){ + if(f->old[0] == '/') + setname(mkaux, &mkaux->oldfile, f->old, ""); + else + setname(mkaux, &mkaux->oldfile, mkaux->root, f->old); + } else + setname(mkaux, &mkaux->oldfile, mkaux->root, f->new); +} + +static 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 + */ +static void +skipdir(Mkaux *mkaux) +{ + char *p, c; + int level; + + if(mkaux->indent < 0) + return; + level = mkaux->indent; + for(;;){ + mkaux->indent = 0; + p = Brdline(mkaux->b, '\n'); + mkaux->lineno++; + if(!p){ + mkaux->indent = -1; + return; + } + while((c = *p++) != '\n') + if(c == ' ') + mkaux->indent++; + else if(c == '\t') + mkaux->indent += 8; + else + break; + if(mkaux->indent <= level){ + Bseek(mkaux->b, -Blinelen(mkaux->b), 1); + mkaux->lineno--; + return; + } + } +} + +static File* +getfile(Mkaux *mkaux, File *old) +{ + File *f; + char *elem; + char *p; + int c; + + if(mkaux->indent < 0) + return 0; +loop: + mkaux->indent = 0; + p = Brdline(mkaux->b, '\n'); + mkaux->lineno++; + if(!p){ + mkaux->indent = -1; + return 0; + } + while((c = *p++) != '\n') + if(c == ' ') + mkaux->indent++; + else if(c == '\t') + mkaux->indent += 8; + else + break; + if(c == '\n' || c == '#') + goto loop; + p--; + f = emalloc(mkaux, sizeof *f); + p = getname(mkaux, p, &elem); + if(p == nil) + return nil; + + f->new = mkpath(mkaux, old->new, elem); + free(elem); + f->elem = utfrrune(f->new, L'/') + 1; + p = getmode(mkaux, p, &f->mode); + p = getname(mkaux, p, &f->uid); /* LEAK */ + if(p == nil) + return nil; + + if(!*f->uid) + strcpy(f->uid, "-"); + p = getname(mkaux, p, &f->gid); /* LEAK */ + if(p == nil) + return nil; + + if(!*f->gid) + strcpy(f->gid, "-"); + f->old = getpath(mkaux, p); + if(f->old && strcmp(f->old, "-") == 0){ + free(f->old); + f->old = 0; + } + setnames(mkaux, f); + + return f; +} + +static char* +getpath(Mkaux *mkaux, 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(mkaux, n + 1); + memcpy(new, p, n); + new[n] = 0; + return new; +} + +static char* +getname(Mkaux *mkaux, char *p, char **buf) +{ + char *s, *start; + int c; + + while((c = *p) == ' ' || c == '\t') + p++; + + start = p; + while((c = *p) != '\n' && c != ' ' && c != '\t') + p++; + + *buf = malloc(p+2-start); /* +2: need at least 2 bytes; might strcpy "-" into buf */ + if(*buf == nil) + return nil; + memmove(*buf, start, p-start); + + (*buf)[p-start] = '\0'; + + if(**buf == '$'){ + s = getenv(*buf+1); + if(s == 0){ + warn(mkaux, "can't read environment variable %s", *buf+1); + skipdir(mkaux); + free(*buf); + return nil; + } + free(*buf); + *buf = s; + } + return p; +} + +static char* +getmode(Mkaux *mkaux, char *p, ulong *xmode) +{ + char *buf, *s; + ulong m; + + *xmode = ~0; + p = getname(mkaux, 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(mkaux, "bad mode specification %s", buf); + free(buf); + return p; + } + *xmode = m | strtoul(s, 0, 8); + free(buf); + return p; +} + +static void +warn(Mkaux *mkaux, char *fmt, ...) +{ + char buf[256]; + va_list va; + + va_start(va, fmt); + vseprint(buf, buf+sizeof(buf), fmt, va); + va_end(va); + + if(mkaux->warn) + mkaux->warn(buf, mkaux->a); + else + fprint(2, "warning: %s\n", buf); +} blob - /dev/null blob + ccab244ce882f82e74b90d90894995210728e729 (mode 644) --- /dev/null +++ src/libdisk/scsi.c @@ -0,0 +1,327 @@ +/* + * Now thread-safe. + * + * The codeqlock guarantees that once codes != nil, that pointer will never + * change nor become invalid. + * + * The QLock in the Scsi structure moderates access to the raw device. + * We should probably export some of the already-locked routines, but + * there hasn't been a need. + */ + +#include +#include +#include + +int scsiverbose; + +#define codefile "/sys/lib/scsicodes" + +static char *codes; +static QLock codeqlock; + +static void +getcodes(void) +{ + Dir *d; + int n, fd; + + if(codes != nil) + return; + + qlock(&codeqlock); + if(codes != nil) { + qunlock(&codeqlock); + return; + } + + if((d = dirstat(codefile)) == nil || (fd = open(codefile, OREAD)) < 0) { + qunlock(&codeqlock); + return; + } + + codes = malloc(1+d->length+1); + if(codes == nil) { + close(fd); + qunlock(&codeqlock); + free(d); + return; + } + + codes[0] = '\n'; /* for searches */ + n = readn(fd, codes+1, d->length); + close(fd); + free(d); + + if(n < 0) { + free(codes); + codes = nil; + qunlock(&codeqlock); + return; + } + codes[n] = '\0'; + qunlock(&codeqlock); +} + +char* +scsierror(int asc, int ascq) +{ + char *p, *q; + static char search[32]; + static char buf[128]; + + getcodes(); + + if(codes) { + sprint(search, "\n%.2ux%.2ux ", asc, ascq); + if(p = strstr(codes, search)) { + p += 6; + if((q = strchr(p, '\n')) == nil) + q = p+strlen(p); + snprint(buf, sizeof buf, "%.*s", (int)(q-p), p); + return buf; + } + + sprint(search, "\n%.2ux00", asc); + if(p = strstr(codes, search)) { + p += 6; + if((q = strchr(p, '\n')) == nil) + q = p+strlen(p); + snprint(buf, sizeof buf, "(ascq #%.2ux) %.*s", ascq, (int)(q-p), p); + return buf; + } + } + + sprint(buf, "scsi #%.2ux %.2ux", asc, ascq); + return buf; +} + + +static int +_scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io, int dolock) +{ + uchar resp[16]; + int n; + long status; + + if(dolock) + qlock(&s->lk); + if(write(s->rawfd, cmd, ccount) != ccount) { + werrstr("cmd write: %r"); + if(dolock) + qunlock(&s->lk); + return -1; + } + + switch(io){ + case Sread: + n = read(s->rawfd, data, dcount); + if(n < 0 && scsiverbose) + fprint(2, "dat read: %r: cmd 0x%2.2uX\n", cmd[0]); + break; + case Swrite: + n = write(s->rawfd, data, dcount); + if(n != dcount && scsiverbose) + fprint(2, "dat write: %r: cmd 0x%2.2uX\n", cmd[0]); + break; + default: + case Snone: + n = write(s->rawfd, resp, 0); + if(n != 0 && scsiverbose) + fprint(2, "none write: %r: cmd 0x%2.2uX\n", cmd[0]); + break; + } + + memset(resp, 0, sizeof(resp)); + if(read(s->rawfd, resp, sizeof(resp)) < 0) { + werrstr("resp read: %r\n"); + if(dolock) + qunlock(&s->lk); + return -1; + } + if(dolock) + qunlock(&s->lk); + + resp[sizeof(resp)-1] = '\0'; + status = atoi((char*)resp); + if(status == 0) + return n; + + werrstr("cmd %2.2uX: status %luX dcount %d n %d", cmd[0], status, dcount, n); + return -1; +} + +int +scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io) +{ + return _scsicmd(s, cmd, ccount, data, dcount, io, 1); +} + +static int +_scsiready(Scsi *s, int dolock) +{ + uchar cmd[6], resp[16]; + int status, i; + + if(dolock) + qlock(&s->lk); + for(i=0; i<3; i++) { + memset(cmd, 0, sizeof(cmd)); + cmd[0] = 0x00; /* unit ready */ + if(write(s->rawfd, cmd, sizeof(cmd)) != sizeof(cmd)) { + if(scsiverbose) + fprint(2, "ur cmd write: %r\n"); + goto bad; + } + write(s->rawfd, resp, 0); + if(read(s->rawfd, resp, sizeof(resp)) < 0) { + if(scsiverbose) + fprint(2, "ur resp read: %r\n"); + goto bad; + } + resp[sizeof(resp)-1] = '\0'; + status = atoi((char*)resp); + if(status == 0 || status == 0x02) { + if(dolock) + qunlock(&s->lk); + return 0; + } + if(scsiverbose) + fprint(2, "target: bad status: %x\n", status); + bad:; + } + if(dolock) + qunlock(&s->lk); + return -1; +} + +int +scsiready(Scsi *s) +{ + return _scsiready(s, 1); +} + +int +scsi(Scsi *s, uchar *cmd, int ccount, void *v, int dcount, int io) +{ + uchar req[6], sense[255], *data; + int tries, code, key, n; + char *p; + + data = v; + SET(key); SET(code); + qlock(&s->lk); + for(tries=0; tries<2; tries++) { + n = _scsicmd(s, cmd, ccount, data, dcount, io, 0); + if(n >= 0) { + qunlock(&s->lk); + return n; + } + + /* + * request sense + */ + memset(req, 0, sizeof(req)); + req[0] = 0x03; + req[4] = sizeof(sense); + memset(sense, 0xFF, sizeof(sense)); + if((n=_scsicmd(s, req, sizeof(req), sense, sizeof(sense), Sread, 0)) < 14) + if(scsiverbose) + fprint(2, "reqsense scsicmd %d: %r\n", n); + + if(_scsiready(s, 0) < 0) + if(scsiverbose) + fprint(2, "unit not ready\n"); + + key = sense[2]; + code = sense[12]; + if(code == 0x17 || code == 0x18) { /* recovered errors */ + qunlock(&s->lk); + return dcount; + } + if(code == 0x28 && cmd[0] == 0x43) { /* get info and media changed */ + s->nchange++; + s->changetime = time(0); + continue; + } + } + + /* drive not ready, or medium not present */ + if(cmd[0] == 0x43 && key == 2 && (code == 0x3a || code == 0x04)) { + s->changetime = 0; + qunlock(&s->lk); + return -1; + } + qunlock(&s->lk); + + if(cmd[0] == 0x43 && key == 5 && code == 0x24) /* blank media */ + return -1; + + p = scsierror(code, sense[13]); + + werrstr("cmd #%.2ux: %s", cmd[0], p); + + if(scsiverbose) + fprint(2, "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n", cmd[0], key, code, sense[13], p); + +// if(key == 0) +// return dcount; + return -1; +} + +Scsi* +openscsi(char *dev) +{ + Scsi *s; + int rawfd, ctlfd, l, n; + char *name, *p, buf[512]; + + l = strlen(dev)+1+3+1; + name = malloc(l); + if(name == nil) + return nil; + + snprint(name, l, "%s/raw", dev); + if((rawfd = open(name, ORDWR)) < 0) { + free(name); + return nil; + } + + snprint(name, l, "%s/ctl", dev); + if((ctlfd = open(name, ORDWR)) < 0) { + free(name); + Error: + close(rawfd); + return nil; + } + free(name); + + n = readn(ctlfd, buf, sizeof buf); + close(ctlfd); + if(n <= 0) + goto Error; + + if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil) + goto Error; + *p = '\0'; + + if((p = strdup(buf+8)) == nil) + goto Error; + + s = malloc(sizeof(*s)); + if(s == nil) { + Error1: + free(p); + goto Error; + } + memset(s, 0, sizeof(*s)); + + s->rawfd = rawfd; + s->inquire = p; + s->changetime = time(0); + + if(scsiready(s) < 0) + goto Error1; + + return s; +} blob - /dev/null blob + eed3713d6d5c984a903fe99a3dc5504847d6db89 (mode 644) --- /dev/null +++ src/libdisk/test.c @@ -0,0 +1,14 @@ +#include +#include +#include + +char *src[] = { "part", "disk", "guess" }; +void +main(int argc, char **argv) +{ + Disk *disk; + + assert(argc == 2); + disk = opendisk(argv[1], 0, 0); + print("%d %d %d from %s\n", disk->c, disk->h, disk->s, src[disk->chssrc]); +}