aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/fossil/9fid.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/fossil/9fid.c')
-rw-r--r--src/cmd/fossil/9fid.c304
1 files changed, 304 insertions, 0 deletions
diff --git a/src/cmd/fossil/9fid.c b/src/cmd/fossil/9fid.c
new file mode 100644
index 00000000..ea7de5ab
--- /dev/null
+++ b/src/cmd/fossil/9fid.c
@@ -0,0 +1,304 @@
+#include "stdinc.h"
+
+#include "9.h"
+
+static struct {
+ VtLock* lock;
+
+ Fid* free;
+ int nfree;
+ int inuse;
+} fbox;
+
+static void
+fidLock(Fid* fid, int flags)
+{
+ if(flags & FidFWlock){
+ vtLock(fid->lock);
+ fid->flags = flags;
+ }
+ else
+ vtRLock(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;
+ vtUnlock(fid->lock);
+ return;
+ }
+ vtRUnlock(fid->lock);
+}
+
+static Fid*
+fidAlloc(void)
+{
+ Fid *fid;
+
+ vtLock(fbox.lock);
+ if(fbox.nfree > 0){
+ fid = fbox.free;
+ fbox.free = fid->hash;
+ fbox.nfree--;
+ }
+ else{
+ fid = vtMemAllocZ(sizeof(Fid));
+ fid->lock = vtLockAlloc();
+ fid->alock = vtLockAlloc();
+ }
+ fbox.inuse++;
+ vtUnlock(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){
+ vtMemFree(fid->uid);
+ fid->uid = nil;
+ }
+ if(fid->uname != nil){
+ vtMemFree(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){
+ vtMemFree(fid->cuname);
+ fid->cuname = nil;
+ }
+
+ vtLock(fbox.lock);
+ fbox.inuse--;
+ if(fbox.nfree < 10){
+ fid->hash = fbox.free;
+ fbox.free = fid;
+ fbox.nfree++;
+ }
+ else{
+ vtLockFree(fid->alock);
+ vtLockFree(fid->lock);
+ vtMemFree(fid);
+ }
+ vtUnlock(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];
+ vtLock(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){
+ vtUnlock(con->fidlock);
+ vtSetError("%s: fid 0x%ud in use", argv0, fidno);
+ return nil;
+ }
+ fid->ref++;
+ vtUnlock(con->fidlock);
+
+ fidLock(fid, flags);
+ if((fid->open & FidOCreate) || fid->fidno == NOFID){
+ fidPut(fid);
+ vtSetError("%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++;
+ vtUnlock(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;
+ }
+ vtUnlock(con->fidlock);
+
+ vtSetError("%s: fid not found", argv0);
+ return nil;
+}
+
+void
+fidPut(Fid* fid)
+{
+ vtLock(fid->con->fidlock);
+ assert(fid->ref > 0);
+ fid->ref--;
+ vtUnlock(fid->con->fidlock);
+
+ if(fid->ref == 0 && fid->fidno == NOFID){
+ fidFree(fid);
+ return;
+ }
+ fidUnlock(fid);
+}
+
+void
+fidClunk(Fid* fid)
+{
+ assert(fid->flags & FidFWlock);
+
+ vtLock(fid->con->fidlock);
+ assert(fid->ref > 0);
+ fid->ref--;
+ fidUnHash(fid);
+ fid->fidno = NOFID;
+ vtUnlock(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;
+
+ vtLock(con->fidlock);
+ while(con->fhead != nil){
+ fidno = con->fhead->fidno;
+ vtUnlock(con->fidlock);
+ if((fid = fidGet(con, fidno, FidFWlock)) != nil)
+ fidClunk(fid);
+ vtLock(con->fidlock);
+ }
+ vtUnlock(con->fidlock);
+}
+
+void
+fidInit(void)
+{
+ fbox.lock = vtLockAlloc();
+}