299 lines
5.3 KiB
C
299 lines
5.3 KiB
C
#include "stdinc.h"
|
|
|
|
#include "9.h"
|
|
|
|
static struct {
|
|
QLock lock;
|
|
|
|
Fid* free;
|
|
int nfree;
|
|
int inuse;
|
|
} fbox;
|
|
|
|
static void
|
|
fidLock(Fid* fid, int flags)
|
|
{
|
|
if(flags & FidFWlock){
|
|
wlock(&fid->lock);
|
|
fid->flags = flags;
|
|
}
|
|
else
|
|
rlock(&fid->lock);
|
|
|
|
/*
|
|
* Callers of file* routines are expected to lock fsys->fs->elk
|
|
* before making any calls in order to make sure the epoch doesn't
|
|
* change underfoot. With the exception of Tversion and Tattach,
|
|
* that implies all 9P functions need to lock on entry and unlock
|
|
* on exit. Fortunately, the general case is the 9P functions do
|
|
* fidGet on entry and fidPut on exit, so this is a convenient place
|
|
* to do the locking.
|
|
* No fsys->fs->elk lock is required if the fid is being created
|
|
* (Tauth, Tattach and Twalk). FidFCreate is always accompanied by
|
|
* FidFWlock so the setting and testing of FidFCreate here and in
|
|
* fidUnlock below is always done under fid->lock.
|
|
* A side effect is that fidFree is called with the fid locked, and
|
|
* must call fidUnlock only after it has disposed of any File
|
|
* resources still held.
|
|
*/
|
|
if(!(flags & FidFCreate))
|
|
fsysFsRlock(fid->fsys);
|
|
}
|
|
|
|
static void
|
|
fidUnlock(Fid* fid)
|
|
{
|
|
if(!(fid->flags & FidFCreate))
|
|
fsysFsRUnlock(fid->fsys);
|
|
if(fid->flags & FidFWlock){
|
|
fid->flags = 0;
|
|
wunlock(&fid->lock);
|
|
return;
|
|
}
|
|
runlock(&fid->lock);
|
|
}
|
|
|
|
static Fid*
|
|
fidAlloc(void)
|
|
{
|
|
Fid *fid;
|
|
|
|
qlock(&fbox.lock);
|
|
if(fbox.nfree > 0){
|
|
fid = fbox.free;
|
|
fbox.free = fid->hash;
|
|
fbox.nfree--;
|
|
}
|
|
else{
|
|
fid = vtmallocz(sizeof(Fid));
|
|
}
|
|
fbox.inuse++;
|
|
qunlock(&fbox.lock);
|
|
|
|
fid->con = nil;
|
|
fid->fidno = NOFID;
|
|
fid->ref = 0;
|
|
fid->flags = 0;
|
|
fid->open = FidOCreate;
|
|
assert(fid->fsys == nil);
|
|
assert(fid->file == nil);
|
|
fid->qid = (Qid){0, 0, 0};
|
|
assert(fid->uid == nil);
|
|
assert(fid->uname == nil);
|
|
assert(fid->db == nil);
|
|
assert(fid->excl == nil);
|
|
assert(fid->rpc == nil);
|
|
assert(fid->cuname == nil);
|
|
fid->hash = fid->next = fid->prev = nil;
|
|
|
|
return fid;
|
|
}
|
|
|
|
static void
|
|
fidFree(Fid* fid)
|
|
{
|
|
if(fid->file != nil){
|
|
fileDecRef(fid->file);
|
|
fid->file = nil;
|
|
}
|
|
if(fid->db != nil){
|
|
dirBufFree(fid->db);
|
|
fid->db = nil;
|
|
}
|
|
fidUnlock(fid);
|
|
|
|
if(fid->uid != nil){
|
|
vtfree(fid->uid);
|
|
fid->uid = nil;
|
|
}
|
|
if(fid->uname != nil){
|
|
vtfree(fid->uname);
|
|
fid->uname = nil;
|
|
}
|
|
if(fid->excl != nil)
|
|
exclFree(fid);
|
|
if(fid->rpc != nil){
|
|
close(fid->rpc->afd);
|
|
auth_freerpc(fid->rpc);
|
|
fid->rpc = nil;
|
|
}
|
|
if(fid->fsys != nil){
|
|
fsysPut(fid->fsys);
|
|
fid->fsys = nil;
|
|
}
|
|
if(fid->cuname != nil){
|
|
vtfree(fid->cuname);
|
|
fid->cuname = nil;
|
|
}
|
|
|
|
qlock(&fbox.lock);
|
|
fbox.inuse--;
|
|
if(fbox.nfree < 10){
|
|
fid->hash = fbox.free;
|
|
fbox.free = fid;
|
|
fbox.nfree++;
|
|
}
|
|
else{
|
|
vtfree(fid);
|
|
}
|
|
qunlock(&fbox.lock);
|
|
}
|
|
|
|
static void
|
|
fidUnHash(Fid* fid)
|
|
{
|
|
Fid *fp, **hash;
|
|
|
|
assert(fid->ref == 0);
|
|
|
|
hash = &fid->con->fidhash[fid->fidno % NFidHash];
|
|
for(fp = *hash; fp != nil; fp = fp->hash){
|
|
if(fp == fid){
|
|
*hash = fp->hash;
|
|
break;
|
|
}
|
|
hash = &fp->hash;
|
|
}
|
|
assert(fp == fid);
|
|
|
|
if(fid->prev != nil)
|
|
fid->prev->next = fid->next;
|
|
else
|
|
fid->con->fhead = fid->next;
|
|
if(fid->next != nil)
|
|
fid->next->prev = fid->prev;
|
|
else
|
|
fid->con->ftail = fid->prev;
|
|
fid->prev = fid->next = nil;
|
|
|
|
fid->con->nfid--;
|
|
}
|
|
|
|
Fid*
|
|
fidGet(Con* con, u32int fidno, int flags)
|
|
{
|
|
Fid *fid, **hash;
|
|
|
|
if(fidno == NOFID)
|
|
return nil;
|
|
|
|
hash = &con->fidhash[fidno % NFidHash];
|
|
qlock(&con->fidlock);
|
|
for(fid = *hash; fid != nil; fid = fid->hash){
|
|
if(fid->fidno != fidno)
|
|
continue;
|
|
|
|
/*
|
|
* Already in use is an error
|
|
* when called from attach, clone or walk.
|
|
*/
|
|
if(flags & FidFCreate){
|
|
qunlock(&con->fidlock);
|
|
werrstr("%s: fid 0x%ud in use", argv0, fidno);
|
|
return nil;
|
|
}
|
|
fid->ref++;
|
|
qunlock(&con->fidlock);
|
|
|
|
fidLock(fid, flags);
|
|
if((fid->open & FidOCreate) || fid->fidno == NOFID){
|
|
fidPut(fid);
|
|
werrstr("%s: fid invalid", argv0);
|
|
return nil;
|
|
}
|
|
return fid;
|
|
}
|
|
|
|
if((flags & FidFCreate) && (fid = fidAlloc()) != nil){
|
|
assert(flags & FidFWlock);
|
|
fid->con = con;
|
|
fid->fidno = fidno;
|
|
fid->ref = 1;
|
|
|
|
fid->hash = *hash;
|
|
*hash = fid;
|
|
if(con->ftail != nil){
|
|
fid->prev = con->ftail;
|
|
con->ftail->next = fid;
|
|
}
|
|
else{
|
|
con->fhead = fid;
|
|
fid->prev = nil;
|
|
}
|
|
con->ftail = fid;
|
|
fid->next = nil;
|
|
|
|
con->nfid++;
|
|
qunlock(&con->fidlock);
|
|
|
|
/*
|
|
* The FidOCreate flag is used to prevent any
|
|
* accidental access to the Fid between unlocking the
|
|
* hash and acquiring the Fid lock for return.
|
|
*/
|
|
fidLock(fid, flags);
|
|
fid->open &= ~FidOCreate;
|
|
return fid;
|
|
}
|
|
qunlock(&con->fidlock);
|
|
|
|
werrstr("%s: fid not found", argv0);
|
|
return nil;
|
|
}
|
|
|
|
void
|
|
fidPut(Fid* fid)
|
|
{
|
|
qlock(&fid->con->fidlock);
|
|
assert(fid->ref > 0);
|
|
fid->ref--;
|
|
qunlock(&fid->con->fidlock);
|
|
|
|
if(fid->ref == 0 && fid->fidno == NOFID){
|
|
fidFree(fid);
|
|
return;
|
|
}
|
|
fidUnlock(fid);
|
|
}
|
|
|
|
void
|
|
fidClunk(Fid* fid)
|
|
{
|
|
assert(fid->flags & FidFWlock);
|
|
|
|
qlock(&fid->con->fidlock);
|
|
assert(fid->ref > 0);
|
|
fid->ref--;
|
|
fidUnHash(fid);
|
|
fid->fidno = NOFID;
|
|
qunlock(&fid->con->fidlock);
|
|
|
|
if(fid->ref > 0){
|
|
/* not reached - fidUnHash requires ref == 0 */
|
|
fidUnlock(fid);
|
|
return;
|
|
}
|
|
fidFree(fid);
|
|
}
|
|
|
|
void
|
|
fidClunkAll(Con* con)
|
|
{
|
|
Fid *fid;
|
|
u32int fidno;
|
|
|
|
qlock(&con->fidlock);
|
|
while(con->fhead != nil){
|
|
fidno = con->fhead->fidno;
|
|
qunlock(&con->fidlock);
|
|
if((fid = fidGet(con, fidno, FidFWlock)) != nil)
|
|
fidClunk(fid);
|
|
qlock(&con->fidlock);
|
|
}
|
|
qunlock(&con->fidlock);
|
|
}
|
|
|
|
void
|
|
fidInit(void)
|
|
{
|
|
}
|