247 lines
4.2 KiB
C
247 lines
4.2 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include "iotrack.h"
|
|
#include "dat.h"
|
|
#include "fns.h"
|
|
|
|
#define FIDMOD 127 /* prime */
|
|
|
|
static Xfs *xhead;
|
|
static Xfile *xfiles[FIDMOD], *freelist;
|
|
static MLock xlock, xlocks[FIDMOD], freelock;
|
|
|
|
Xfs *
|
|
getxfs(char *user, char *name)
|
|
{
|
|
Xfs *xf, *fxf;
|
|
Dir *dir;
|
|
Qid dqid;
|
|
char *p, *q;
|
|
vlong offset;
|
|
int fd, omode;
|
|
|
|
USED(user);
|
|
if(name==nil || name[0]==0)
|
|
name = deffile;
|
|
if(name == nil){
|
|
errno = Enofilsys;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* If the name passed is of the form 'name:offset' then
|
|
* offset is used to prime xf->offset. This allows accessing
|
|
* a FAT-based filesystem anywhere within a partition.
|
|
* Typical use would be to mount a filesystem in the presence
|
|
* of a boot manager programme at the beginning of the disc.
|
|
*/
|
|
offset = 0;
|
|
if(p = strrchr(name, ':')){
|
|
*p++ = 0;
|
|
offset = strtoll(p, &q, 0);
|
|
chat("name %s, offset %lld\n", p, offset);
|
|
if(offset < 0 || p == q){
|
|
errno = Enofilsys;
|
|
return 0;
|
|
}
|
|
offset *= Sectorsize;
|
|
}
|
|
|
|
if(readonly)
|
|
omode = OREAD;
|
|
else
|
|
omode = ORDWR;
|
|
fd = open(name, omode);
|
|
|
|
if(fd < 0 && omode==ORDWR){
|
|
omode = OREAD;
|
|
fd = open(name, omode);
|
|
}
|
|
|
|
if(fd < 0){
|
|
chat("can't open %s: %r\n", name);
|
|
errno = Eerrstr;
|
|
return 0;
|
|
}
|
|
dir = dirfstat(fd);
|
|
if(dir == nil){
|
|
errno = Eio;
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
dqid = dir->qid;
|
|
free(dir);
|
|
mlock(&xlock);
|
|
for(fxf=0,xf=xhead; xf; xf=xf->next){
|
|
if(xf->ref == 0){
|
|
if(fxf == 0)
|
|
fxf = xf;
|
|
continue;
|
|
}
|
|
if(!eqqid(xf->qid, dqid))
|
|
continue;
|
|
if(strcmp(xf->name, name) != 0 || xf->dev < 0)
|
|
continue;
|
|
if(devcheck(xf) < 0) /* look for media change */
|
|
continue;
|
|
if(offset && xf->offset != offset)
|
|
continue;
|
|
chat("incref \"%s\", dev=%d...", xf->name, xf->dev);
|
|
++xf->ref;
|
|
unmlock(&xlock);
|
|
close(fd);
|
|
return xf;
|
|
}
|
|
if(fxf == nil){
|
|
fxf = malloc(sizeof(Xfs));
|
|
if(fxf == nil){
|
|
unmlock(&xlock);
|
|
close(fd);
|
|
errno = Enomem;
|
|
return nil;
|
|
}
|
|
fxf->next = xhead;
|
|
xhead = fxf;
|
|
}
|
|
chat("alloc \"%s\", dev=%d...", name, fd);
|
|
fxf->name = strdup(name);
|
|
fxf->ref = 1;
|
|
fxf->qid = dqid;
|
|
fxf->dev = fd;
|
|
fxf->fmt = 0;
|
|
fxf->offset = offset;
|
|
fxf->ptr = nil;
|
|
fxf->isfat32 = 0;
|
|
fxf->omode = omode;
|
|
unmlock(&xlock);
|
|
return fxf;
|
|
}
|
|
|
|
void
|
|
refxfs(Xfs *xf, int delta)
|
|
{
|
|
mlock(&xlock);
|
|
xf->ref += delta;
|
|
if(xf->ref == 0){
|
|
chat("free \"%s\", dev=%d...", xf->name, xf->dev);
|
|
free(xf->name);
|
|
free(xf->ptr);
|
|
purgebuf(xf);
|
|
if(xf->dev >= 0){
|
|
close(xf->dev);
|
|
xf->dev = -1;
|
|
}
|
|
}
|
|
unmlock(&xlock);
|
|
}
|
|
|
|
Xfile *
|
|
xfile(int fid, int flag)
|
|
{
|
|
Xfile **hp, *f, *pf;
|
|
int k;
|
|
|
|
k = ((ulong)fid) % FIDMOD;
|
|
hp = &xfiles[k];
|
|
mlock(&xlocks[k]);
|
|
pf = nil;
|
|
for(f=*hp; f; f=f->next){
|
|
if(f->fid == fid)
|
|
break;
|
|
pf = f;
|
|
}
|
|
if(f && pf){
|
|
pf->next = f->next;
|
|
f->next = *hp;
|
|
*hp = f;
|
|
}
|
|
switch(flag){
|
|
default:
|
|
panic("xfile");
|
|
case Asis:
|
|
unmlock(&xlocks[k]);
|
|
return (f && f->xf && f->xf->dev < 0) ? nil : f;
|
|
case Clean:
|
|
break;
|
|
case Clunk:
|
|
if(f){
|
|
*hp = f->next;
|
|
unmlock(&xlocks[k]);
|
|
clean(f);
|
|
mlock(&freelock);
|
|
f->next = freelist;
|
|
freelist = f;
|
|
unmlock(&freelock);
|
|
} else
|
|
unmlock(&xlocks[k]);
|
|
return nil;
|
|
}
|
|
unmlock(&xlocks[k]);
|
|
if(f)
|
|
return clean(f);
|
|
mlock(&freelock);
|
|
if(f = freelist){ /* assign = */
|
|
freelist = f->next;
|
|
unmlock(&freelock);
|
|
} else {
|
|
unmlock(&freelock);
|
|
f = malloc(sizeof(Xfile));
|
|
if(f == nil){
|
|
errno = Enomem;
|
|
return nil;
|
|
}
|
|
}
|
|
mlock(&xlocks[k]);
|
|
f->next = *hp;
|
|
*hp = f;
|
|
unmlock(&xlocks[k]);
|
|
f->fid = fid;
|
|
f->flags = 0;
|
|
f->qid = (Qid){0,0,0};
|
|
f->xf = nil;
|
|
f->ptr = nil;
|
|
return f;
|
|
}
|
|
|
|
Xfile *
|
|
clean(Xfile *f)
|
|
{
|
|
if(f->ptr){
|
|
free(f->ptr);
|
|
f->ptr = nil;
|
|
}
|
|
if(f->xf){
|
|
refxfs(f->xf, -1);
|
|
f->xf = nil;
|
|
}
|
|
f->flags = 0;
|
|
f->qid = (Qid){0,0,0};
|
|
return f;
|
|
}
|
|
|
|
/*
|
|
* the file at <addr, offset> has moved
|
|
* relocate the dos entries of all fids in the same file
|
|
*/
|
|
void
|
|
dosptrreloc(Xfile *f, Dosptr *dp, vlong addr, ulong offset)
|
|
{
|
|
int i;
|
|
Xfile *p;
|
|
Dosptr *xdp;
|
|
|
|
for(i=0; i < FIDMOD; i++){
|
|
for(p = xfiles[i]; p != nil; p = p->next){
|
|
xdp = p->ptr;
|
|
if(p != f && p->xf == f->xf
|
|
&& xdp != nil && xdp->addr == addr && xdp->offset == offset){
|
|
memmove(xdp, dp, sizeof(Dosptr));
|
|
xdp->p = nil;
|
|
xdp->d = nil;
|
|
p->qid.path = QIDPATH(xdp);
|
|
}
|
|
}
|
|
}
|
|
}
|