#include <sys/types.h> #include <sys/file.h> #include "btree.h" #include "ubermud.h" #include "store.h" #include "syssym.h" #include "externs.h" /* Copyright(C) 1990, Marcus J. Ranum, All Rights Reserved. This software may be freely used, modified, and redistributed, as long as this copyright message is left intact, and this software is not used to develop any commercial product, or used in any product that is provided on a pay-for-use basis. */ /* routines involved with storing data to and from disk (mostly called from the freeze/thaw code). support is included for InoFiles (files of base objects) as well as ChunkFiles (files of compiled programs and strings) In fact, keeping your paws OUT of this code is probably the smartest thing you'll do in a long time. COOL THINGS THAT CAN STILL BE DONE TO MAKE THIS FASTER:: 1) put a level of caching for hits in front of the b+tree routines using a wide hash table, say 5 deep, with updating as needed. this MIGHT speed things about 5%. 2) make chunkalloc() split large chunks into smaller chunks when the only avail. chunk is large. this results in some storage fragging but would probably be better in the long run. */ /* #define FREEZEDEBUG #define THAWDEBUG #define CHUNKDEBUG #define ALLOCDEBUG #define CACHEDEBUG */ /* the system base object repository */ static InoFile *sys_ino = (InoFile *)0; /* b+tree index of object names */ static BT_INDEX *sys_idx = (BT_INDEX *)0; /* repository for random chunks of other data */ static ChunkFile *sys_chunk = (ChunkFile *)0; /* system cache headers */ static CacheChain sys_cache[CACHEWIDTH]; /* return the length of an elem padded UP to the nearest word-boundary this is vitally necessary, or the b+tree will die a screaming death. if you run this under Sabre-C, you'll get errors, because the b+tree code will take stuff past the end of the element name. this is not a problem. */ static int elelen(elem) char *elem; { int l; l = strlen(ELENAM(elem)) + sizeof(long) + 1; return(l + (sizeof(unsigned long) - (l % sizeof(unsigned long)))); } /* free block comparison funct. passed to b+tree routines */ static int freecompare(f1,l1,f2,l2) FreeEnt *f1; int l1; FreeEnt *f2; int l2; { if(f1->offt == f2->offt && f1->siz == f2->siz) return(0); if(f1->siz != f2->siz) return(f1->siz - f2->siz); return(f1->offt - f2->offt); } /* element name comparison funct. passed to b+tree routines */ static int elecompare(e1,l1,e2,l2) char *e1; int l1; char *e2; int l2; { register char *p1; register char *p2; if(ELENUM(e1) != ELENUM(e2)) return(ELENUM(e1) - ELENUM(e2)); p1 = ELENAM(e1); p2 = ELENAM(e2); for(;*p1 == *p2 && *p1 != '\0' && *p2 != '\0'; p1++, p2++) ; if(*p1 != *p2) return(*p1 - *p2); return(0); } /* open a ChunkFile file and return a pointer to it */ static ChunkFile * chunkopen(file) char *file; { ChunkFile *ret; int nred; if((ret = (ChunkFile *)malloc(sizeof(ChunkFile))) == 0) return(0); if((ret->fd = open(file,O_RDWR|O_CREAT,0600)) < 0) { free((char *)ret); return(0); } nred = read(ret->fd,(char *)&(ret->sblk),sizeof(ret->sblk)); if(nred == 0) { /* empty - initialize */ ret->sblk.free = 0L; /* presently unused */ ret->sblk.high = (long)sizeof(ret->sblk); if((lseek(ret->fd,0L,0) != 0L)) { (void)close(ret->fd); free(ret); return(0); } nred = write(ret->fd,(char *)&ret->sblk,sizeof(ret->sblk)); } if(nred != sizeof(ret->sblk)) { (void)close(ret->fd); free(ret); return(0); } /* open free block index */ ret->fbt = bt_optopen( BT_PATH, "free_index", BT_OMODE, O_CREAT, BT_DTYPE, BT_USRDEF, freecompare, 0); if(ret->fbt == (BT_INDEX *)0) { (void)close(ret->fd); free(ret); return(0); } return(ret); } /* close a chunk file */ static void chunkclose(cf) ChunkFile *cf; { if(cf == (ChunkFile *)0) return; (void)close(cf->fd); (void)bt_close(cf->fbt); free((char *)cf); } /* open an ObjIno file and return a pointer to it */ static InoFile * inoopen(file) char *file; { InoFile *ret; int nred; if((ret = (InoFile *)malloc(sizeof(InoFile))) == 0) return(0); if((ret->fd = open(file,O_RDWR|O_CREAT,0600)) < 0) { free((char *)ret); return(0); } nred = read(ret->fd,(char *)&(ret->sblk),sizeof(ret->sblk)); if(nred == 0) { /* empty - initialize */ ret->sblk.free = 0L; ret->sblk.high = 1L; if((lseek(ret->fd,0L,0) != 0L)) { (void)close(ret->fd); free(ret); return(0); } nred = write(ret->fd,(char *)&ret->sblk,sizeof(ret->sblk)); } if(nred != sizeof(ret->sblk)) { (void)close(ret->fd); free(ret); return(0); } return(ret); } /* close an ObjIno file */ static void inoclose(ino) InoFile *ino; { if(ino == (InoFile *)0) return; (void)close(ino->fd); free((char *)ino); } /* read the requested ObjIno into buf */ static int inoread(inof,num,buf) InoFile *inof; long num; ObjIno *buf; { long o; o = (long)((num * sizeof(ObjIno)) + sizeof(inof->sblk)); if(lseek(inof->fd,o,0) != o) return(1); if(read(inof->fd,(char *)buf,sizeof(ObjIno)) != sizeof(ObjIno)) return(1); return(0); } /* write the requested ObjIno from buf */ static int inowrite(inof,num,buf) InoFile *inof; long num; ObjIno *buf; { long o; o = (long)((num * sizeof(ObjIno)) + sizeof(inof->sblk)); if(lseek(inof->fd,o,0) != o) return(1); if(write(inof->fd,(char *)buf,sizeof(ObjIno)) != sizeof(ObjIno)) return(1); return(0); } /* free an ObjIno */ static int inofree(nof,i) InoFile *nof; long i; { ObjIno ibuf; if(i <= 0 || i >= nof->sblk.high) return(1); if((inoread(nof,i,&ibuf)) != 0) return(1); ibuf.typ = TYP_NULL; ibuf.doff = nof->sblk.free; if((inowrite(nof,i,&ibuf)) != 0) return(1); nof->sblk.free = i; /* repair and re-write the header */ if(lseek(nof->fd,0L,0) != 0L) return(1); if(write(nof->fd,(char *)&(nof->sblk),sizeof(nof->sblk)) != sizeof(nof->sblk)) return(1); return(0); } /* allocate a new ObjIno and return its # */ static long inonew(nof) InoFile *nof; { long ret = (long)0; ObjIno ibuf; if(nof->sblk.free == (long)0) { ret = (long)nof->sblk.high; nof->sblk.high++; } else { /* read in the Ino at the head of the free list */ if((inoread(nof,nof->sblk.free,&ibuf)) != 0) return((long)0); /* real problems! */ if(ibuf.typ != TYP_NULL) { logf("WARNING ! bad free list!!\n",0); ret = (long)nof->sblk.high; nof->sblk.high++; } else { /* that will be the one to return */ ret = nof->sblk.free; nof->sblk.free = ibuf.doff; } } /* repair and re-write the header */ if(lseek(nof->fd,0L,0) != 0L) return((long)0); if(write(nof->fd,(char *)&(nof->sblk),sizeof(nof->sblk)) != sizeof(nof->sblk)) return((long)0); return(ret); } /* really allocate a chunk from a chunk file. only called from chunknew */ static long chunkallocate(cof,byts,realsiz) ChunkFile *cof; unsigned byts; unsigned *realsiz; { long ret = (long)0; ret = (long)cof->sblk.high; cof->sblk.high += byts; #ifdef CHUNKDEBUG printf("chunkallocate: %d bytes at %d, new high %d\n",byts,ret,cof->sblk.high); #endif *realsiz = byts; /* repair and re-write the header */ if(lseek(cof->fd,0L,0) != 0L) return((long)0); if(write(cof->fd,(char *)&(cof->sblk),sizeof(cof->sblk)) != sizeof(cof->sblk)) return((long)0); return(ret); } /* allocate a new chunk and return its offset, after first checking for a reasonable-sized free hunk in the free list. (the free list is maintained in a b+tree, because it was easy, and the ordering properties of the tree permit an interesting best-fit approach) */ static long chunknew(cof,byts,realsiz) ChunkFile *cof; unsigned byts; unsigned *realsiz; { FreeEnt fe; int btret; long bf; int jnk; fe.offt = (long)0; fe.siz = byts; /* scan tree. we had BETTER not find anything, since the offset we are looking for is at 0, and HAS to be less than anything of comparable size in the tree. */ btret = bt_find(cof->fbt,(char *)&fe,sizeof(fe),&bf); if(btret != BT_NF) return(chunkallocate(cof,byts,realsiz)); /* get the next entry, which, by definition, will be the best fit, since the size is the next highest. anything else (such as BT_ERR or BT_EOF) as a return value means we are out of luck. */ btret = bt_traverse(cof->fbt,BT_EOF,(char *)&fe,sizeof(fe),&jnk,&bf); if(btret != BT_OK) return(chunkallocate(cof,byts,realsiz)); #ifdef CHUNKDEBUG printf("chunknew: %d bytes at %d in free tree\n",fe.siz,fe.offt); #endif /* now, fe should have a nicely matching chunk of free disk. make sure there is not too much slop - to avoid fragging. */ if(fe.siz - byts > STORESLOP) return(chunkallocate(cof,byts,realsiz)); /* looks like we have a decent fit, now, so delete fe (it HAD BETTER BE THERE!) from the tree, and patch up a return value. */ if(bt_delete(cof->fbt,(char *)&fe,sizeof(fe)) != BT_OK) return(chunkallocate(cof,byts,realsiz)); /* fe's chunk may be bigger, remember */ *realsiz = fe.siz; #ifdef CHUNKDEBUG printf("chunknew: returning %d bytes(really %d) at %d\n",byts,fe.siz,fe.offt); #endif return(fe.offt); } /* free a chunk, by inserting it in the b+tree free list. if it doesn't work, it doesn't work. */ static void chunkfree(cof,off,byts) ChunkFile *cof; long off; unsigned byts; { FreeEnt fe; fe.offt = off; fe.siz = byts; (void)bt_insert(cof->fbt,(char *)&fe,sizeof(fe),off,1); #ifdef CHUNKDEBUG printf("chunkfree: free %d bytes at %d\n",byts,off); #endif } /* write a list header and its data separately (we don't know that it is contiguous in memory) when it is re-materialized it will be. */ static int freezelist(cof,off,l) ChunkFile *cof; long off; ObjList *l; { if(lseek(cof->fd,off,0) != off) return(1); /* write the header */ if(write(cof->fd,(char *)l,sizeof(ObjList)) != sizeof(ObjList)) return(1); #ifdef FREEZEDEBUG printf("freeze:wrote list header\n"); #endif /* write the data */ if(write(cof->fd,(char *)l->l_data,(int)(sizeof(long) * l->l_cnt)) != (int)(sizeof(long) * l->l_cnt)) return(1); #ifdef FREEZEDEBUG printf("freeze:wrote list data\n"); #endif return(0); } /* write a function to disk. */ static int freezeprog(cof,off,p) ChunkFile *cof; long off; Prog *p; { if(lseek(cof->fd,off,0) != off) return(1); /* unsetuid bit when copying functs, just in case */ p->p_mode = (p->p_mode & ~(PERM_SUID)); /* write the header */ if(write(cof->fd,(char *)p,sizeof(Prog)) != sizeof(Prog)) return(1); #ifdef FREEZEDEBUG printf("freeze:wrote func header\n"); #endif /* write the executable segment */ if(write(cof->fd,(char *)p->p_mem,(int)sizeof(int) * p->p_siz) != (int)sizeof(int) * p->p_siz) return(1); #ifdef FREEZEDEBUG printf("freeze:wrote func exec. code (%d)\n",sizeof(int) * p->p_siz); #endif /* write the string table */ if(write(cof->fd,(char *)p->p_str,p->s_siz) != p->s_siz) return(1); #ifdef FREEZEDEBUG printf("freeze:wrote func str. table (%d)\n",p->s_siz); #endif return(0); } /* open all the databases used */ db_open() { CacheChain *cp; CacheElem *ep; /* open i-block file */ if((sys_ino = inoopen("inodes")) == (InoFile *)0) { fatal("cannot open inodes: ",(char *)-1,"\n",0); return(1); } /* open chunk store */ if((sys_chunk = chunkopen("chunks")) == (ChunkFile *)0) { fatal("cannot open chunks: ",(char *)-1,"\n",0); return(1); } /* open primary index with a BIG cache */ sys_idx = bt_optopen( BT_PATH, "index", BT_OMODE, O_CREAT, BT_DTYPE, BT_USRDEF, elecompare, BT_CACHE, 12, 0); if(sys_idx == (BT_INDEX *)0) { fatal("cannot open index: ",(char *)-1,"\n",0); return(1); } /* init cache - zero everything */ for(cp = sys_cache; cp < sys_cache + CACHEWIDTH; cp++) { cp->ahead = (CacheElem *)0; cp->atail = (CacheElem *)0; cp->ohead = (CacheElem *)malloc(sizeof(CacheElem) * CACHEDEPTH); if(cp->ohead == (CacheElem *)0) return(1); for(ep = cp->ohead; ep < cp->ohead + CACHEDEPTH; ep++) { ep->next = (CacheElem *)0; if(ep != cp->ohead) { ep->prev = ep - 1; ep->prev->next = ep; } else ep->prev = (CacheElem *)0; ep->onum = (long)0; ep->ino.dsiz = 0; } cp->otail = cp->ohead + (CACHEDEPTH - 1); cp->ohead->prev = cp->otail->next = (CacheElem *)0; } return(0); } /* close all the databases */ void db_close() { /* WE DO NOT FREE THE CACHE! why ? because we have no idea where the pointers to the originally allocated memory are nowadays. fix this someday by allocating the whole cache in one call to malloc, and saving a pointer to it. */ if(sys_ino != (InoFile *)0) inoclose(sys_ino); if(sys_chunk != (ChunkFile *)0) chunkclose(sys_chunk); if(sys_idx != (BT_INDEX *)0) (void)bt_close(sys_idx); } /* return the base object # of an element, or 0 */ static long elegetbase(ele) char *ele; { long bob; switch(bt_find(sys_idx,ele,elelen(ele),&bob)) { case BT_NF: bob = (long)0; break; case BT_ERR: logf("elegetbase: btree error ", bt_errs[bt_errno(sys_idx)]," cannot find\n",0); bob = (long)0; break; } return(bob); } /* allocate a new base object and make an index entry for it. actually, index entries may already exist, but are overwritten by the new one. this is correct behaviour. */ static long elenewbase(ele) char *ele; { long ret; /* allocate a new base object Ino */ if((ret = inonew(sys_ino)) == (long)0) return(1); /* add the new entry to the index b+tree NOTE - the '1' flag implies an overwrite of existing entries. this means that existing base element pointers will be overwritten which is exactly what we want, of course, but if you modify this code, you have to be a bit careful of that. */ if(bt_insert(sys_idx,ele,elelen(ele),ret,1)== BT_ERR) { logf("elenewbase: btree error ",bt_errs[bt_errno(sys_idx)], " cannot insert\n",0); return(1); } #ifdef FREEZEDEBUG printf("freeze:add %d.%s as #%d in index\n",ELENUM(ele),ELENAM(ele),ret); #endif return(ret); } /* reset the cache - move all active objects back to the head of the old list. */ void cache_reset() { CacheChain *cp; for(cp = sys_cache; cp < sys_cache + CACHEWIDTH; cp++) { if(cp->ahead != 0) { #ifdef CACHEDEBUG printf("cache:reset chain %d\n",cp); #endif cp->atail->next = cp->ohead; #ifdef CACHEDEBUG printf("cache:tail of active is %d\n",cp->atail->next); #endif if(cp->ohead != (CacheElem *)0) cp->ohead->prev = cp->atail; cp->ohead = cp->ahead; #ifdef CACHEDEBUG printf("cache:new head of old is %d\n",cp->ohead); #endif cp->ahead = (CacheElem *)0; } } } /* remove an object from the old cache chain */ static void cache_dropold(cp,ep) CacheChain *cp; CacheElem *ep; { /* is this the list tail ? */ if(ep->next == (CacheElem *)0) cp->otail = ep->prev; else ep->next->prev = ep->prev; /* is this the list head ? */ if(ep->prev == (CacheElem *)0) cp->ohead = ep->next; else ep->prev->next = ep->next; #ifdef CACHEDEBUG printf("cache:dechained %d from chain %d\n",ep,cp); #endif } /* insert an element at the head of the active list for its chain */ static void cache_insactiv(num,ep) long num; CacheElem *ep; { CacheChain *cp; cp = sys_cache + (num % CACHEWIDTH); ep->next = cp->ahead; if(cp->ahead == (CacheElem *)0) cp->atail = ep; cp->ahead = ep; ep->prev = (CacheElem *)0; if(ep->next != (CacheElem *)0) ep->next->prev = ep; #ifdef CACHEDEBUG printf("cache:%d now on active chain\n",ep); #endif } /* search the cache and return the thing. If it is NOT there, return 0 - aflg is set iff the thing is in the currently active cache. */ static CacheElem * cache_search(num,aflg) long num; int *aflg; { CacheChain *cp; CacheElem *ep; *aflg = 0; /* pointer math - you gotta LOVE it */ cp = sys_cache + (num % CACHEWIDTH); #ifdef CACHEDEBUG printf("cache:searching cache holder %d\n",num % CACHEWIDTH); #endif ep = cp->ahead; #ifdef CACHEDEBUG printf("cache:scan active chain %d\n",cp->ahead); #endif /* first - search the active chain */ while(ep != (CacheElem *)0) { if(num == ep->onum) { /* hit ? quit! */ #ifdef CACHEDEBUG printf("cache:return %d from active chain\n",ep); #endif *aflg = 1; return(ep); } ep = ep->next; } #ifdef CACHEDEBUG printf("cache:scan old chain %d\n",cp->ahead); #endif /* second - search the old chain */ ep = cp->ohead; while(ep != (CacheElem *)0) { /* hits are trickier, here. we need to dechain it and re-chain it on the active list. */ if(num == ep->onum) { cache_dropold(cp,ep); cache_insactiv(num,ep); /* and return the thing */ #ifdef CACHEDEBUG printf("cache:return %d from old chain\n",ep); #endif return(ep); } ep = ep->next; } return((CacheElem *)0); } /* get an empty holder from the cache chain needed. */ static CacheElem * cache_getholder(num) long num; { CacheChain *cp; CacheElem *ep; /* pointer math - you gotta LOVE it */ cp = sys_cache + (num % CACHEWIDTH); if(cp->otail != (CacheElem *)0) { ep = cp->otail; #ifdef CACHEDEBUG printf("cache:flushing %d from chain\n",ep); #endif cache_dropold(cp,cp->otail); /* if there was cached data, free it */ if(ep->ino.dsiz != 0) { #ifdef ALLOCDEBUG printf("free %d bytes at %d\n",ep->ino.dsiz,ep->ino.op.c); #endif free(ep->ino.op.c); } } else { ep = (CacheElem *)malloc(sizeof(CacheElem)); if(ep == (CacheElem *)0) return((CacheElem *)0); #ifdef CACHEDEBUG printf("cache:out of holders in chain! allocated %d\n",ep); #endif ep->next = ep->prev = (CacheElem *)0; } ep->onum = (long)0; ep->ino.op.c = (char *)0; return(ep); } /* search the cache and return the materialized thing. if it is NOT in the cache, materialize it quietly. */ static ObjIno * cache_get(num) long num; { CacheElem *ep; int aflg; /* if the thing is already in the cache, life is good and pure. */ if((ep = cache_search(num,&aflg)) != (CacheElem *)0) return(&(ep->ino)); /* get an empty holder */ if((ep = cache_getholder(num)) == (CacheElem *)0) return((ObjIno *)0); /* that done, activate it */ cache_insactiv(num,ep); /* bring in the object's I-block */ if(inoread(sys_ino,num,&(ep->ino))) return((ObjIno *)0); #ifdef CACHEDEBUG printf("cache:materialize %d from disk\n",num); #endif /* allocate memory if dsize indicates there is some */ if(ep->ino.dsiz != 0) { char *p; int rv; if((p = malloc(ep->ino.dsiz)) == (char *)0) return((ObjIno *)0); #ifdef ALLOCDEBUG printf("malloced %d bytes at %d\n",ep->ino.dsiz,p); #endif #ifdef CACHEDEBUG printf("cache:read %d at %d\n",ep->ino.dsiz,ep->ino.doff); #endif if(lseek(sys_chunk->fd,ep->ino.doff,0) != ep->ino.doff) return((ObjIno *)0); if((rv = read(sys_chunk->fd,p,(int)ep->ino.dsiz)) != (int)ep->ino.dsiz) { logf("cache_get: read@",ltoa(ep->ino.doff), " failed:",(char *)-1," got ",itoa(rv),"\n",0); logf("cache_get: inode #",ltoa(num),"\n",0); return((ObjIno *)0); } /* implicit cast between char * and whatever here! */ ep->ino.op.c = p; /* some types may need some internal pointer frobbing because they contain absolute pointers. at this point we KNOW they are contiguous in memory. the rest is easy. */ if(ep->ino.typ == TYP_OLIST) { ep->ino.op.ol->l_data = (long *)&p[sizeof(ObjList)]; } else if(ep->ino.typ == TYP_FUNC) { /* executable segment */ ep->ino.op.p->p_mem = (int *)&p[sizeof(Prog)]; /* string table */ ep->ino.op.p->p_str = &p[sizeof(Prog) + (ep->ino.op.p->p_siz * sizeof(int))]; } } /* now the object is valid and can be used. */ ep->onum = num; #ifdef CACHEDEBUG printf("cache:return%d\n",ep); #endif return(&(ep->ino)); } /* put the thing back into the cache, updating an in-memory copy if there is any, freeing secondary storage, reassigning, etc, etc, etc. changed flags that there has been a change to the secondary data. If there has NOT been such a change, only the inode is updated. If there HAS been, then a little more leg-work is necessary. */ static int cache_put(num,ino,changed,typ,op) long num; ObjIno *ino; int changed; int typ; Oper op; { CacheElem *ep; int aflg; /* STAGE ONE: fix the in-core version */ /* if the thing is already in the cache, we must needs update it. */ if((ep = cache_search(num,&aflg)) != (CacheElem *)0) { Oper oop; /* this shuts up lint. */ oop.i = 0; /* free the old stuff if there is secondary core */ if(changed && ep->ino.dsiz > 0) { if(aflg) { /* put the pointer on the free list to be freed after the run is over */ #ifdef ALLOCDEBUG printf("put %d bytes at %d on tmp free list for later removal\n",ep->ino.dsiz,ep->ino.op.c); #endif tmpputonfree(ep->ino.op.c); } else { #ifdef ALLOCDEBUG printf("free %d bytes at %d\n",ep->ino.dsiz,ep->ino.op.c); #endif free(ep->ino.op.c); } } /* update the cached inode, saving its data pointer in case. */ oop = ep->ino.op; if(ino != &(ep->ino)) bcopy((char *)ino,(char *)&(ep->ino),sizeof(ObjIno)); if(changed) { /* copy the new thing into the old. this may allocate memory as needed! */ if(opercopy(op,typ,&(ep->ino.op),&(ep->ino.typ),&(ep->ino.dsiz))) return(ERR_OOM); } else { /* restore the original pointer! */ ep->ino.op = oop; } /* somewhat of a kludge */ if(typ == TYP_FUNC) { ep->ino.op.p->p_uid = ino->owner; ep->ino.op.p->p_mode = ino->mode; } } /* STAGE TWO: fix the on-disk version */ /* chapter the first: if the secondary data has changed... */ if(changed) { int ns; ns = operdatasize(typ,op); if(ns == 0 && ino->msiz > 0) { /* no need to allocate any at all - free it */ chunkfree(sys_chunk,ino->doff,ino->msiz); } else if(ns > 0 && (ino->msiz < ns || ino->msiz - ns > STORESLOP)) { long noff; unsigned rsiz; /* need to allocate more */ noff = chunknew(sys_chunk,(unsigned)ns,&rsiz); if(noff == (long)0) return(ERR_DBASE); /* don't free it until the allocation works */ if(ino->msiz > 0) chunkfree(sys_chunk,ino->doff,ino->msiz); ino->msiz = rsiz; ino->dsiz = ns; ino->doff = noff; } /* if the above 2 cases missed, there is enough room for any needed secondary storage already. Huzzah. do the store! */ if(typ == TYP_STR) { if(lseek(sys_chunk->fd,ino->doff,0) != ino->doff) return(ERR_DBASE); if(write(sys_chunk->fd,op.c,(int)ino->dsiz) != (int)ino->dsiz) return(ERR_DBASE); } else if(typ == TYP_OLIST) { if(freezelist(sys_chunk,ino->doff,op.ol)) return(ERR_DBASE); } else if(typ == TYP_FUNC) { op.p->p_uid = ino->owner; op.p->p_mode = ino->mode; if(freezeprog(sys_chunk,ino->doff,op.p)) return(ERR_DBASE); } else { /* fall through. just update the inode. */ ino->op = op; } ino->typ = typ; } /* chapter the second: write out the inode... */ return(inowrite(sys_ino,num,ino)); } /* this is brutal - MUST be a long, because of alignment - if it's a char, the compiler may not word align it, and you are in a world of pain. an alternate form would use bzero, I guess. */ static char * eleroot(enm) long enm; { static long xx[2]; xx[0] = enm; *((char *)(&xx[1])) = '\0'; return((char *)xx); } /* check permissions on an object, by operator. */ permitted(op,owner,mode,uid,euid) int op; long owner; int mode; long uid; long euid; { /* wiz */ if(uid == (long)0 || euid == (long)0) return(1); /* world */ if(op == PERM_WRITE && (mode & PERM_WWRIT)) return(1); if(op == PERM_READ && (mode & PERM_WREAD)) return(1); /* indirect */ if(euid == owner && uid != euid) { if(op == PERM_WRITE && (mode & PERM_IWRIT)) return(1); if(op == PERM_READ && (mode & PERM_IREAD)) return(1); } /* owner */ if(euid == owner && uid == euid) { if(op == PERM_WRITE && (mode & PERM_OWRIT)) return(1); if(op == PERM_READ && (mode & PERM_OREAD)) return(1); } return(0); } /* add a reference count, and add it to the index */ static int eleaddref(bob,ele) long bob; char *ele; { ObjIno *ino; Oper junk; /* increment link count */ if((ino = cache_get(bob)) == (ObjIno *)0) return(ERR_DBASE); (ino->refcnt)++; junk.i = 0; if(cache_put(bob,ino,0,TYP_NULL,junk) != 0) return(ERR_DBASE); /* add an index entry */ if(bt_insert(sys_idx,ele,elelen(ele),bob,1) == BT_ERR) { logf("eleaddref: btree error ",bt_errs[bt_errno(sys_idx)], " cannot insert\n",0); return(ERR_DBASE); } #ifdef FREEZEDEBUG printf("freeze:added ref %d.%s->%d, refcnt = %d\n",ELENUM(ele),ELENAM(ele),bob,ino->refcnt); #endif return(0); } /* decrement a reference count, and kill it from the index if appropriate */ static int eledropref(bob,ele) long bob; char *ele; { ObjIno *ino; #ifdef FREEZEDEBUG printf("eledropref: %d.%s base object %d\n",ELENUM(ele),ELENAM(ele),bob); #endif /* first delete index entry */ if(bt_delete(sys_idx,ele,elelen(ele)) == BT_ERR) { logf("eledropref: btree error ",bt_errs[bt_errno(sys_idx)], " cannot delete reference\n",0); return(ERR_DBASE); } /* decrement link count */ if((ino = cache_get(bob)) == (ObjIno *)0) return(ERR_DBASE); (ino->refcnt)--; /* nothing linked to it anymore, free it completely */ if(ino->refcnt <= 0) { CacheElem *ep; int aflg; #ifdef FREEZEDEBUG printf("free base object %d\n",bob); #endif /* free any secondary storage */ if(ino->dsiz > 0) { chunkfree(sys_chunk,ino->doff,ino->msiz); ino->dsiz = 0; ino->msiz = 0; } /* free the inode */ if(inofree(sys_ino,bob)) return(ERR_DBASE); /* make sure it is hors de combat */ if((ep = cache_search(bob,&aflg)) != (CacheElem *)0) ep->onum = (long)0; } else { Oper junk; junk.i = 0; if(cache_put(bob,ino,0,TYP_NULL,junk) != 0) return(ERR_DBASE); } #ifdef FREEZEDEBUG printf("freeze:decr ref %d.%s->%d, refcnt = %d\n",ELENUM(ele),ELENAM(ele),bob,ino->refcnt); #endif return(0); } /* chill a non-system var. */ disk_freeze(ele,typ,data,op,uid,euid) char *ele; int typ; Oper data; int op; long uid; long euid; { ObjIno i; ObjIno *ci; ObjIno *newip; long bob; #ifdef FREEZEDEBUG printf("freeze:elem %d.%s\n",ELENUM(ele),ELENAM(ele)); #endif if(*ELENAM(ele) == '_' && uid != (long)0 && euid != (long)0) return(ERR_PERM); bob = elegetbase(ele); if(bob == (long)0) { /* if the assign is to NULL, we are done */ if(typ == TYP_NULL) return(0); /* before permitting someone to add a variable to a base object, make sure they have write permission on it. */ if(uid != (long)0 && euid != (long)0) { if((bob = elegetbase(eleroot(ELENUM(ele)))) == (long)0) { #ifdef FREEZEDEBUG printf("freeze:no root obj %d\n",ELENUM(ele)); #endif return(ERR_NOTHERE); } if((ci = cache_get(bob)) == (ObjIno *)0) return(ERR_DBASE); /* check perms */ if(!permitted(PERM_WRITE,ci->owner,ci->mode,uid,euid)) { #ifdef FREEZEDEBUG printf("freeze:perm create in %d denied\n",ELENUM(ele)); #endif return(ERR_NOTOWN); } } /* if the assign is to ELEM, add a ref, and the index */ if(typ == TYP_ELEM) { long txx; if((txx = elegetbase(data.c)) == (long)0) { #ifdef FREEZEDEBUG printf("freeze:link %d.%s:not found\n",ELENUM(ele),ELENAM(ele)); #endif return(ERR_NOTHERE); } return(eleaddref(txx,ele)); } /* permission granted */ if((bob = elenewbase(ele)) == (long)0) return(ERR_DBASE); /* initialize new object */ i.mode = PERM_DEFAULT; i.owner = euid; i.refcnt = 1; i.dsiz = i.msiz = 0; newip = &i; } else { /* permissions checks - sigh - waste some time */ if((newip = ci = cache_get(bob)) == (ObjIno *)0) return(ERR_DBASE); if(!permitted(PERM_WRITE,ci->owner,ci->mode,uid,euid)) { #ifdef FREEZEDEBUG printf("freeze:modify %d.%s denied\n",ELENUM(ele),ELENAM(ele)); #endif return(ERR_PERM); } /* if the assign is to NULL, drop a ref, and the index */ if(typ == TYP_NULL) return(eledropref(bob,ele)); /* adding a reference */ if(typ == TYP_ELEM) { long txx; if((txx = elegetbase(data.c)) == (long)0) { #ifdef FREEZEDEBUG printf("freeze:link %d.%s:not found\n",ELENUM(ele),ELENAM(ele)); #endif return(ERR_NOTHERE); } return(eleaddref(txx,ele)); } /* this is in the case where the object exists, BUT there is more than one reference to it. a reference must be dropped, and a new base object created, then the index modified. */ if(op == OP_EASGN) { if((ci = cache_get(bob)) == (ObjIno *)0) return(ERR_DBASE); if(ci->refcnt > 1) { if(eledropref(bob,ele)) return(ERR_DBASE); #ifdef FREEZEDEBUG printf("freeze:decr ref %d.%s->%d, refcnt = %d\n",ELENUM(ele),ELENAM(ele),bob,ci->refcnt); #endif /* allocate a new base object and modify index */ if((bob = elenewbase(ele)) == (long)0) return(ERR_DBASE); i.mode = PERM_DEFAULT; i.owner = euid; i.refcnt = 1; i.dsiz = i.msiz = 0; newip = &i; } } #ifdef FREEZEDEBUG else { printf("freeze:assigning to *%d.%s->%d\n",ELENUM(ele),ELENAM(ele),bob); } #endif } #ifdef FREEZEDEBUG printf("freeze:updated elem %d\n",bob); #endif return(cache_put(bob,newip,1,typ,data)); } /* restore a non-system variable from disk. */ disk_thaw(ele,typ,data,uid,euid) char *ele; int *typ; Oper *data; long uid; long euid; { ObjIno *ci; long bob; /* search the primary index for the object by name */ switch(bt_find(sys_idx,ele,elelen(ele),&bob)) { case BT_NF: #ifdef THAWDEBUG printf("thaw:index empty for %d.%s\n",ELENUM(ele),ELENAM(ele)); #endif /* not there. oops */ *typ = TYP_NULL; data->i = 0; return(ERR_NOTHERE); case BT_ERR: /* internal error - BAD news */ logf("disk_thaw: btree error ",bt_errs[bt_errno(sys_idx)], " cannot find\n",0); return(ERR_DBASE); } #ifdef THAWDEBUG printf("thaw:index gives object #%d\n",bob); #endif if((ci = cache_get(bob)) == (ObjIno *)0) return(ERR_DBASE); if(!permitted(PERM_READ,ci->owner,ci->mode,uid,euid)) { #ifdef THAWDEBUG printf("thaw:#%d mode %d,owner %d,perm denied\n",bob,ci->mode,ci->owner); #endif return(ERR_PERM); } *typ = ci->typ; *data = ci->op; /* somewhat of a kludge */ if(*typ == TYP_FUNC) { data->p->p_uid = ci->owner; data->p->p_mode = ci->mode; } return(0); } /* this obtains an allocates a new base object holder, incrementing the system's notion of how many have been allocated so far. the system's idea is in a long, so when maxlong things have been allocated, the database is in a world of hurtin. we assume, further, that if there is NO #0.0, we should create one. this will utterly annihilate the database, if something mangles #0.0, as already existing base objects will get re-allocated. OW! */ void blt_objectnew(m,argc,firstarg,result,uid,euid) Machine *m; int argc; int firstarg; int result; long *uid; long *euid; { ObjIno ibuf; long bob; long nin; int errv; /* errv is used similarly in the other blt_ store functions. since all the errors in objectnew are disk-related, is is kind of redundant here, but is left in to ease eventual changes in code. */ /* this is absolutely awful, but necessary */ switch(bt_find(sys_idx,eleroot((long)0),elelen(eleroot((long)0)),&bob)) { case BT_NF: bob = (long)0; break; case BT_ERR: logf("baseobjnew: btree error ", bt_errs[bt_errno(sys_idx)]," cannot insert\n",0); bob = (long)0; break; } /* now, increment it! (thus we will never hit 0!) */ bob++; /* replace it in the index */ if(bt_insert(sys_idx,eleroot((long)0),elelen(eleroot((long)0)),bob,1)== BT_ERR) { logf("baseobjnew: btree error ",bt_errs[bt_errno(sys_idx)], " cannot insert\n",0); errv = ERR_DBASE; goto fail; } /* allocate a new base object Ino */ if((nin = inonew(sys_ino)) == (long)0) { errv = ERR_DBASE; goto fail; } ibuf.mode = PERM_DEFAULT; ibuf.owner = *euid; ibuf.refcnt = 1; ibuf.typ = TYP_BOBJHO; ibuf.dsiz = 0; ibuf.msiz = 0; ibuf.doff = (long)0; ibuf.op.l = (long)0; ibuf.refcnt = 1; /* initialize it and save it */ if((inowrite(sys_ino,nin,&ibuf)) != 0) { errv = ERR_DBASE; goto fail; } /* make an entry in the index. */ if(bt_insert(sys_idx,eleroot(bob),elelen(eleroot(bob)),nin,1)== BT_ERR) { logf("baseobjnew: btree error ",bt_errs[bt_errno(sys_idx)], " cannot insert\n",0); errv = ERR_DBASE; goto fail; } #ifdef FREEZEDEBUG printf("created new obj #%d, owner %d, mode %o\n",bob,*uid,ibuf.mode); #endif m->m_mem[result].typ = TYP_OBJ; m->m_mem[result].oper.l = bob; return; fail: m->m_mem[result].typ = TYP_NULL; m->m_mem[result].oper.i = errv; return; } /* change the ownership of an object or base object. (some operator overloading never hurt anyone) */ void blt_chown(m,argc,firstarg,result,uid,euid) Machine *m; int argc; int firstarg; int result; long *uid; long *euid; { ObjIno *ip; SysSym *sp; long bob; int errv; /* type check the cthoogies out of it */ if(argc < 2 || (m->m_mem[firstarg].typ != TYP_ELEM && m->m_mem[firstarg].typ != TYP_OBJ) || m->m_mem[firstarg + 1].typ != TYP_OBJ) { #ifdef FREEZEDEBUG printf("chown: bad args\n"); #endif errv = ERR_BADARG; goto fail; } if(m->m_mem[firstarg].typ == TYP_ELEM) { if(ELENUM(m->m_mem[firstarg].oper.c) == (long)0) { sp = sys_getsym(m->m_mem[firstarg].oper.c); if(sp == (SysSym *)0) { errv = ERR_NOTHERE; goto fail; } ip = &(sp->ino); bob = 0; } else { bob = elegetbase(m->m_mem[firstarg].oper.c); if(bob == (long)0) { #ifdef FREEZEDEBUG printf("chown: nonexistent object\n"); #endif errv = ERR_NOTHERE; goto fail; } if((ip = cache_get(bob)) == (ObjIno *)0) { errv = ERR_DBASE; goto fail; } } } else { bob = elegetbase(eleroot(m->m_mem[firstarg].oper.l)); if(bob == (long)0) { #ifdef FREEZEDEBUG printf("chown: nonexistent object\n"); #endif errv = ERR_NOTHERE; goto fail; } if((ip = cache_get(bob)) == (ObjIno *)0) { errv = ERR_DBASE; goto fail; } } /* check perms */ if(*uid != (long)0 && *euid != (long)0 && ip->owner != *euid) { #ifdef FREEZEDEBUG printf("chown: permission denied\n"); #endif errv = ERR_NOTOWN; goto fail; } /* really f***ing ugly. - MUST patch it in the func */ if(ip->typ == TYP_FUNC) { ip->op.p->p_uid = ip->owner; ip->op.p->p_mode = ip->mode; } /* ok, do it */ ip->owner = m->m_mem[firstarg + 1].oper.l; ip->mode = (ip->mode & ~(PERM_SUID)); if(m->m_mem[firstarg].typ != TYP_ELEM || ELENUM(m->m_mem[firstarg].oper.c) != (long)0) { Oper junk; junk.i = 0; if(cache_put(bob,ip,0,TYP_NULL,junk)) { errv = ERR_DBASE; goto fail; } } m->m_mem[result].oper.i = 0; m->m_mem[result].typ = TYP_NUM; #ifdef FREEZEDEBUG printf("chown: base object %d belongs to %d\n",bob,ip->owner); #endif return; fail: m->m_mem[result].typ = TYP_NULL; m->m_mem[result].oper.i = errv; return; } /* change the permissions bits of an object. */ void blt_chmod(m,argc,firstarg,result,uid,euid) Machine *m; int argc; int firstarg; int result; long *uid; long *euid; { ObjIno *ip; SysSym *sp; int errv; long bob; char *mp; int nm = 0; /* type check the cthoogies out of it */ if(argc < 2 || (m->m_mem[firstarg].typ != TYP_ELEM && m->m_mem[firstarg].typ != TYP_OBJ) || m->m_mem[firstarg + 1].typ != TYP_STR) { #ifdef FREEZEDEBUG printf("chmod: bad argument\n"); #endif errv = ERR_BADARG; goto fail; } if(m->m_mem[firstarg].typ == TYP_ELEM) { if(ELENUM(m->m_mem[firstarg].oper.c) == (long)0) { sp = sys_getsym(m->m_mem[firstarg].oper.c); if(sp == (SysSym *)0) { errv = ERR_NOTHERE; goto fail; } ip = &(sp->ino); bob = 0; } else { bob = elegetbase(m->m_mem[firstarg].oper.c); if(bob == (long)0) { #ifdef FREEZEDEBUG printf("chmod: no such object\n"); #endif errv = ERR_NOTHERE; goto fail; } if((ip = cache_get(bob)) == (ObjIno *)0) { errv = ERR_DBASE; goto fail; } } } else { bob = elegetbase(eleroot(m->m_mem[firstarg].oper.l)); if(bob == (long)0) { errv = ERR_NOTHERE; goto fail; } if((ip = cache_get(bob)) == (ObjIno *)0) { errv = ERR_DBASE; goto fail; } } /* check perms */ if(*uid != (long)0 && *euid != (long)0 && ip->owner != *euid && ip->owner != *uid) { #ifdef FREEZEDEBUG printf("chmod: base object %d perm denied\n",bob); #endif errv = ERR_NOTOWN; goto fail; } #ifdef FREEZEDEBUG printf("chmod: base object %d mode is %o\n",bob,ip->mode); #endif mp = m->m_mem[firstarg + 1].oper.c; while(mp && *mp) { switch(*mp++) { case 'O': if(*mp++ != ':') break; for(;*mp != '\0' && *mp != ' ' && *mp != '\t' && *mp != ','; mp++) { if(*mp == 'w') nm |= PERM_OWRIT; else if(*mp == 'r') nm |= PERM_OREAD; } break; case 'I': if(*mp++ != ':') break; for(;*mp != '\0' && *mp != ' ' && *mp != '\t' && *mp != ','; mp++) { if(*mp == 'w') nm |= PERM_IWRIT; else if(*mp == 'r') nm |= PERM_IREAD; } break; case 'W': if(*mp++ != ':') break; for(;*mp != '\0' && *mp != ' ' && *mp != '\t' && *mp != ','; mp++) { if(*mp == 'w') nm |= PERM_WWRIT; else if(*mp == 'r') nm |= PERM_WREAD; else if(*mp == 's') nm |= PERM_SUID; else if(*mp == 'c') nm |= PERM_CHAIN; else if(*mp == 'b') nm |= PERM_BLOCK; } break; } } ip->mode = nm; /* really f***ing ugly. - MUST patch it in the func */ if(ip->typ == TYP_FUNC) ip->op.p->p_mode = nm; /* if disk-based, write it */ if(m->m_mem[firstarg].typ != TYP_ELEM || ELENUM(m->m_mem[firstarg].oper.c) != (long)0) { Oper junk; junk.i = 0; if(cache_put(bob,ip,0,TYP_NULL,junk)) { errv = ERR_DBASE; goto fail; } } #ifdef FREEZEDEBUG printf("chmod: base object %d new mode is %o\n",bob,nm); #endif m->m_mem[result].oper.i = 0; m->m_mem[result].typ = TYP_NUM; return; fail: m->m_mem[result].typ = TYP_NULL; m->m_mem[result].oper.i = errv; return; } /* destroy a base object. */ void blt_objectdestroy(m,argc,firstarg,result,uid,euid) Machine *m; int argc; int firstarg; int result; long *uid; long *euid; { static char *idlbuf = (char *)0; int errv; ObjIno *ip; long bob; long jnk; CacheElem *ep; int aflg; if(idlbuf == (char *)0) { idlbuf = malloc(MAXIDENTLEN * 2); if(idlbuf == (char *)0) { logf("alloc idlbuf in objectdestroy failed !\n",0); errv = ERR_OOM; goto fail; } } /* type check the cthoogies out of it */ if(argc != 1 || m->m_mem[firstarg].typ != TYP_OBJ) { #ifdef FREEZEDEBUG printf("objectdestroy: bad argument\n"); #endif errv = ERR_BADARG; goto fail; } if(m->m_mem[firstarg].oper.l == (long)0) { #ifdef FREEZEDEBUG printf("objectdestroy: some joker tried to destroy #0\n"); #endif errv = ERR_BADARG; goto fail; } bob = elegetbase(eleroot(m->m_mem[firstarg].oper.l)); if(bob == (long)0) { errv = ERR_NOTHERE; goto fail; } if((ip = cache_get(bob)) == (ObjIno *)0) { errv = ERR_DBASE; goto fail; } #define ONLYWIZCANDESTROY #ifdef ONLYWIZCANDESTROY if(*uid != (long)0 && *euid != (long)0) { errv = ERR_PERM; goto fail; } #else if(!permitted(PERM_WRITE,ip->owner,ip->mode,*uid,*euid)) { #ifdef THAWDEBUG printf("objectdestroy:#%d mode %d,owner %d,perm denied\n", bob,ip->mode,ip->owner); #endif errv = ERR_PERM; goto fail; } #endif bob = m->m_mem[firstarg].oper.l; /* prepare to attack with chainsaw! */ if(bt_find(sys_idx,eleroot(bob),elelen(eleroot(bob)),&jnk) != BT_OK) { #ifdef THAWDEBUG printf("objectdestroy:#%d root not found\n",bob); #endif errv = ERR_DBASE; goto fail; } /* first delete index entry of root object */ if(bt_delete(sys_idx,eleroot(bob),elelen(eleroot(bob))) == BT_ERR) { logf("objectdestroy: btree error ", bt_errs[bt_errno(sys_idx)]," cannot delete\n",0); errv = ERR_DBASE; goto fail; } /* free root object's ino */ if(inofree(sys_ino,jnk)) { errv = ERR_DBASE; goto fail; } if((ep = cache_search(jnk,&aflg)) != (CacheElem *)0) ep->onum = (long)0; #ifdef THAWDEBUG printf("objectdestroy:#%d root %d freed\n",bob,jnk); #endif while(1) { int x; int bv; bv = bt_traverse(sys_idx,BT_EOF,idlbuf,MAXIDENTLEN * 2,&x,&jnk); if(bv == BT_ERR) { logf("b+tree error: objectdestroy traverse\n",0); errv = ERR_DBASE; goto fail; } if(bv != BT_OK) break; if(ELENUM(idlbuf) != bob) break; #ifdef FREEZEDEBUG printf("objectdestroy: elem %d.%s\n",ELENUM(idlbuf),ELENAM(idlbuf)); #endif if(eledropref(jnk,idlbuf)) { errv = ERR_DBASE; goto fail; } } m->m_mem[result].oper.i = 0; m->m_mem[result].typ = TYP_NUM; return; fail: m->m_mem[result].typ = TYP_NULL; m->m_mem[result].oper.i = errv; } /* list the elements in a base object */ void blt_objectelements(m,argc,firstarg,result,uid,euid) Machine *m; int argc; int firstarg; int result; long *uid; long *euid; { static char *idlbuf = (char *)0; int errv; ObjIno *ip; long bob; long jnk; if(idlbuf == (char *)0) { idlbuf = malloc(MAXIDENTLEN * 2); if(idlbuf == (char *)0) { logf("alloc idlbuf in objectelements failed !\n",0); errv = ERR_OOM; goto fail; } } if(argc != 1 || m->m_mem[firstarg].typ != TYP_OBJ) { #ifdef FREEZEDEBUG printf("objectelements: bad argument\n"); #endif errv = ERR_BADARG; goto fail; } if(m->m_mem[firstarg].oper.l == (long)0) { #ifdef FREEZEDEBUG printf("objectelements: some idiot tried to list #0\n"); #endif errv = ERR_BADARG; goto fail; } bob = elegetbase(eleroot(m->m_mem[firstarg].oper.l)); if(bob == (long)0) { errv = ERR_NOTHERE; goto fail; } if((ip = cache_get(bob)) == (ObjIno *)0) { errv = ERR_DBASE; goto fail; } if(!permitted(PERM_READ,ip->owner,ip->mode,*uid,*euid)) { #ifdef THAWDEBUG printf("objectelements:#%d mode %d,owner %d,perm denied\n", bob,ip->mode,ip->owner); #endif errv = ERR_PERM; goto fail; } bob = m->m_mem[firstarg].oper.l; if(bt_find(sys_idx,eleroot(bob),elelen(eleroot(bob)),&jnk) != BT_OK) { #ifdef THAWDEBUG printf("objectelements:#%d root not found\n",bob); #endif errv = ERR_DBASE; goto fail; } iobtell(*uid,"elements in #",ltoa(bob),":",0); while(1) { int x; int bv; bv = bt_traverse(sys_idx,BT_EOF,idlbuf,MAXIDENTLEN * 2,&x,&jnk); if(bv == BT_ERR) { logf("b+tree error: objectdestroy traverse\n",0); errv = ERR_DBASE; goto fail; } if(bv != BT_OK) break; if(ELENUM(idlbuf) != bob) break; #ifdef FREEZEDEBUG printf("objectelement: elem %d.%s\n",ELENUM(idlbuf),ELENAM(idlbuf)); #endif iobtell(*uid," .",ELENAM(idlbuf),0); } iobtell(*uid,"\n",0); m->m_mem[result].oper.i = 0; m->m_mem[result].typ = TYP_NUM; return; fail: m->m_mem[result].typ = TYP_NULL; m->m_mem[result].oper.i = errv; } /* return the ownership of an object */ void blt_objectowner(m,argc,firstarg,result,uid,euid) Machine *m; int argc; int firstarg; int result; long *uid; long *euid; { ObjIno *ip; SysSym *sp; int errv; long bob; /* type check the cthoogies out of it */ if(argc != 1 || (m->m_mem[firstarg].typ != TYP_ELEM && m->m_mem[firstarg].typ != TYP_OBJ)) { #ifdef FREEZEDEBUG printf("objectowner: bad argument\n"); #endif errv = ERR_BADARG; goto fail; } if(m->m_mem[firstarg].typ == TYP_ELEM) { if(ELENUM(m->m_mem[firstarg].oper.c) == (long)0) { sp = sys_getsym(m->m_mem[firstarg].oper.c); if(sp == (SysSym *)0) { errv = ERR_NOTHERE; goto fail; } ip = &(sp->ino); bob = 0; } else { bob = elegetbase(m->m_mem[firstarg].oper.c); if(bob == (long)0) { #ifdef FREEZEDEBUG printf("objectowner: no such object\n"); #endif errv = ERR_NOTHERE; goto fail; } if((ip = cache_get(bob)) == (ObjIno *)0) { errv = ERR_DBASE; goto fail; } } } else { bob = elegetbase(eleroot(m->m_mem[firstarg].oper.l)); if(bob == (long)0) { errv = ERR_NOTHERE; goto fail; } if((ip = cache_get(bob)) == (ObjIno *)0) { errv = ERR_DBASE; goto fail; } } m->m_mem[result].oper.l = ip->owner; m->m_mem[result].typ = TYP_OBJ; #ifdef FREEZEDEBUG printf("objectowner: base object %d belongs to %d\n",bob,ip->owner); #endif return; fail: m->m_mem[result].typ = TYP_NULL; m->m_mem[result].oper.i = errv; return; }