![cinap_lenrek](/assets/img/avatar_default.png)
In a few places, we where using a fixed buffer of sizeof(Dir)+100 size for stat. This is not correct and fails if the name returned in stat is long. This results in being unable to seek to the end of file with a long filename. The kernel should do the same thing as dirfstat() from libc; handling the conversion and buffer allocation and returning a freeable Dir* pointer. For this, a new dirchanstat() function was added. The fstat syscall was not rewriting the name to the last path element; fix it. In addition, gracefully handle the mountfix case, reallocating the buffer to accomidate the required stat length plus size of the new name so dirsetname() does not fail.
406 lines
5.7 KiB
C
406 lines
5.7 KiB
C
/*
|
|
* sd loopback driver,
|
|
* copyright © 2009-10 erik quanstrom
|
|
*/
|
|
|
|
#include "u.h"
|
|
#include "../port/lib.h"
|
|
#include "mem.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
#include "io.h"
|
|
#include "../port/error.h"
|
|
#include "../port/sd.h"
|
|
#include "../port/netif.h"
|
|
|
|
extern char Echange[];
|
|
|
|
enum {
|
|
Maxpath = 256,
|
|
Devsectsize = 512,
|
|
};
|
|
|
|
typedef struct Ctlr Ctlr;
|
|
struct Ctlr {
|
|
QLock;
|
|
|
|
Ctlr *next;
|
|
SDunit *unit;
|
|
|
|
char path[Maxpath];
|
|
Chan *c;
|
|
|
|
uint vers;
|
|
uchar drivechange;
|
|
|
|
uvlong sectors;
|
|
uint sectsize;
|
|
};
|
|
|
|
static Lock ctlrlock;
|
|
static Ctlr *head;
|
|
static Ctlr *tail;
|
|
|
|
SDifc sdloopifc;
|
|
|
|
/* must call with c qlocked */
|
|
static void
|
|
identify(Ctlr *c, SDunit *u)
|
|
{
|
|
uvlong s, osectors;
|
|
Dir *dir;
|
|
|
|
osectors = c->sectors;
|
|
|
|
dir = dirchanstat(c->c);
|
|
s = dir->length / c->sectsize;
|
|
free(dir);
|
|
|
|
memset(u->inquiry, 0, sizeof u->inquiry);
|
|
u->inquiry[2] = 2;
|
|
u->inquiry[3] = 2;
|
|
u->inquiry[4] = sizeof u->inquiry - 4;
|
|
memmove(u->inquiry+8, c->path, 40);
|
|
|
|
if(osectors == 0 || osectors != s){
|
|
c->sectors = s;
|
|
c->drivechange = 1;
|
|
c->vers++;
|
|
}
|
|
}
|
|
|
|
static Ctlr*
|
|
ctlrlookup(char *path)
|
|
{
|
|
Ctlr *c;
|
|
|
|
lock(&ctlrlock);
|
|
for(c = head; c; c = c->next)
|
|
if(strcmp(c->path, path) == 0)
|
|
break;
|
|
unlock(&ctlrlock);
|
|
return c;
|
|
}
|
|
|
|
static Ctlr*
|
|
newctlr(char *path)
|
|
{
|
|
Ctlr *c;
|
|
|
|
if(ctlrlookup(path))
|
|
error(Eexist);
|
|
if((c = malloc(sizeof *c)) == nil)
|
|
error(Enomem);
|
|
if(waserror()){
|
|
free(c);
|
|
nexterror();
|
|
}
|
|
c->c = namec(path, Aopen, ORDWR, 0);
|
|
poperror();
|
|
kstrcpy(c->path, path, sizeof c->path);
|
|
lock(&ctlrlock);
|
|
if(head != nil)
|
|
tail->next = c;
|
|
else
|
|
head = c;
|
|
tail = c;
|
|
unlock(&ctlrlock);
|
|
return c;
|
|
}
|
|
|
|
static void
|
|
delctlr(Ctlr *c)
|
|
{
|
|
Ctlr *x, *prev;
|
|
|
|
lock(&ctlrlock);
|
|
|
|
for(prev = 0, x = head; x; prev = x, x = c->next)
|
|
if(strcmp(c->path, x->path) == 0)
|
|
break;
|
|
if(x == 0){
|
|
unlock(&ctlrlock);
|
|
error(Enonexist);
|
|
}
|
|
|
|
if(prev)
|
|
prev->next = x->next;
|
|
else
|
|
head = x->next;
|
|
if(x->next == nil)
|
|
tail = prev;
|
|
unlock(&ctlrlock);
|
|
|
|
if(x->c)
|
|
cclose(x->c);
|
|
free(x);
|
|
}
|
|
|
|
static SDev*
|
|
probe(char *path, SDev *s)
|
|
{
|
|
char *p;
|
|
uint sectsize;
|
|
Ctlr *c;
|
|
|
|
sectsize = 0;
|
|
if(p = strchr(path, '!')){
|
|
*p = 0;
|
|
sectsize = strtoul(p + 1, 0, 0);
|
|
}
|
|
c = newctlr(path);
|
|
c->sectsize = sectsize? sectsize: Devsectsize;
|
|
if(s == nil && (s = malloc(sizeof *s)) == nil)
|
|
return nil;
|
|
s->ctlr = c;
|
|
s->ifc = &sdloopifc;
|
|
s->nunit = 1;
|
|
return s;
|
|
}
|
|
|
|
static char *probef[32];
|
|
static int nprobe;
|
|
|
|
static int
|
|
pnpprobeid(char *s)
|
|
{
|
|
int id;
|
|
|
|
if(strlen(s) < 2)
|
|
return 0;
|
|
id = 'l';
|
|
if(s[1] == '!')
|
|
id = s[0];
|
|
return id;
|
|
}
|
|
|
|
static SDev*
|
|
pnp(void)
|
|
{
|
|
int i, id;
|
|
char *p;
|
|
SDev *h, *t, *s;
|
|
|
|
if((p = getconf("loopdev")) == 0)
|
|
return 0;
|
|
nprobe = tokenize(p, probef, nelem(probef));
|
|
h = t = 0;
|
|
for(i = 0; i < nprobe; i++){
|
|
id = pnpprobeid(probef[i]);
|
|
if(id == 0)
|
|
continue;
|
|
s = malloc(sizeof *s);
|
|
if(s == nil)
|
|
break;
|
|
s->ctlr = 0;
|
|
s->idno = id;
|
|
s->ifc = &sdloopifc;
|
|
s->nunit = 1;
|
|
|
|
if(h)
|
|
t->next = s;
|
|
else
|
|
h = s;
|
|
t = s;
|
|
}
|
|
return h;
|
|
}
|
|
|
|
static Ctlr*
|
|
pnpprobe(SDev *s)
|
|
{
|
|
char *p;
|
|
static int i;
|
|
|
|
if(i > nprobe)
|
|
return 0;
|
|
p = probef[i++];
|
|
if(strlen(p) < 2)
|
|
return 0;
|
|
if(p[1] == '!')
|
|
p += 2;
|
|
s = probe(p, s);
|
|
return s->ctlr;
|
|
}
|
|
|
|
|
|
static int
|
|
loopverify(SDunit *u)
|
|
{
|
|
SDev *s;
|
|
Ctlr *c;
|
|
|
|
s = u->dev;
|
|
c = s->ctlr;
|
|
if(c == nil){
|
|
if(waserror())
|
|
return 0;
|
|
s->ctlr = c = pnpprobe(s);
|
|
poperror();
|
|
}
|
|
c->drivechange = 1;
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
connect(SDunit *u, Ctlr *c)
|
|
{
|
|
qlock(c);
|
|
if(waserror()){
|
|
qunlock(c);
|
|
return -1;
|
|
}
|
|
identify(u->dev->ctlr, u);
|
|
qunlock(c);
|
|
poperror();
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
looponline(SDunit *u)
|
|
{
|
|
Ctlr *c;
|
|
int r;
|
|
|
|
c = u->dev->ctlr;
|
|
if(c->drivechange){
|
|
if(connect(u, c) == -1)
|
|
return 0;
|
|
r = 2;
|
|
c->drivechange = 0;
|
|
u->sectors = c->sectors;
|
|
u->secsize = c->sectsize;
|
|
} else
|
|
r = 1;
|
|
return r;
|
|
}
|
|
|
|
static long
|
|
loopbio(SDunit *u, int, int write, void *a, long count, uvlong lba)
|
|
{
|
|
uchar *data;
|
|
int n;
|
|
long (*rio)(Chan*, void*, long, vlong);
|
|
Ctlr *c;
|
|
|
|
c = u->dev->ctlr;
|
|
data = a;
|
|
if(write)
|
|
rio = devtab[c->c->type]->write;
|
|
else
|
|
rio = devtab[c->c->type]->read;
|
|
|
|
if(waserror()){
|
|
if(strcmp(up->errstr, Echange) == 0 ||
|
|
strstr(up->errstr, "device is down") != nil)
|
|
u->sectors = 0;
|
|
nexterror();
|
|
}
|
|
n = rio(c->c, data, c->sectsize * count, c->sectsize * lba);
|
|
poperror();
|
|
return n;
|
|
}
|
|
|
|
static int
|
|
looprio(SDreq *r)
|
|
{
|
|
int i, count, rw;
|
|
uvlong lba;
|
|
SDunit *u;
|
|
|
|
u = r->unit;
|
|
|
|
if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91)
|
|
return sdsetsense(r, SDok, 0, 0, 0);
|
|
|
|
if((i = sdfakescsi(r)) != SDnostatus)
|
|
return r->status = i;
|
|
if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus)
|
|
return i;
|
|
r->rlen = loopbio(u, r->lun, rw == SDwrite, r->data, count, lba);
|
|
return r->status = SDok;
|
|
}
|
|
|
|
static int
|
|
looprctl(SDunit *u, char *p, int l)
|
|
{
|
|
Ctlr *c;
|
|
char *e, *op;
|
|
|
|
if((c = u->dev->ctlr) == nil)
|
|
return 0;
|
|
e = p+l;
|
|
op = p;
|
|
|
|
p = seprint(p, e, "path\t%s\n", c->path);
|
|
p = seprint(p, e, "geometry %llud %d\n", c->sectors, c->sectsize);
|
|
return p - op;
|
|
}
|
|
|
|
static int
|
|
loopwctl(SDunit *, Cmdbuf *cmd)
|
|
{
|
|
cmderror(cmd, Ebadarg);
|
|
return 0;
|
|
}
|
|
|
|
static SDev*
|
|
loopprobew(DevConf *c)
|
|
{
|
|
char *p;
|
|
|
|
p = strchr(c->type, '/');
|
|
if(p == nil || strlen(p) > Maxpath - 1)
|
|
error(Ebadarg);
|
|
p++;
|
|
if(ctlrlookup(p))
|
|
error(Einuse);
|
|
return probe(p, 0);
|
|
}
|
|
|
|
static void
|
|
loopclear(SDev *s)
|
|
{
|
|
delctlr((Ctlr *)s->ctlr);
|
|
}
|
|
|
|
static char*
|
|
looprtopctl(SDev *s, char *p, char *e)
|
|
{
|
|
Ctlr *c;
|
|
|
|
c = s->ctlr;
|
|
return seprint(p, e, "%s loop %s\n", s->name, c? c->path: "");
|
|
}
|
|
|
|
static int
|
|
loopwtopctl(SDev *, Cmdbuf *cmd)
|
|
{
|
|
switch(cmd->nf){
|
|
default:
|
|
cmderror(cmd, Ebadarg);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
SDifc sdloopifc = {
|
|
"loop",
|
|
|
|
pnp,
|
|
nil, /* legacy */
|
|
nil, /* enable */
|
|
nil, /* disable */
|
|
|
|
loopverify,
|
|
looponline,
|
|
looprio,
|
|
looprctl,
|
|
loopwctl,
|
|
|
|
loopbio,
|
|
loopprobew, /* probe */
|
|
loopclear, /* clear */
|
|
looprtopctl,
|
|
loopwtopctl,
|
|
};
|