/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 2002-2012 AT&T Intellectual Property * * and is licensed under the * * Eclipse Public License, Version 1.0 * * by AT&T Intellectual Property * * * * A copy of the License is available at * * http://www.eclipse.org/org/documents/epl-v10.html * * (with md5 checksum b35adb5213ca9657e911e9befb180842) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * Glenn Fowler * * David Korn * * * ***********************************************************************/ #pragma prototyped /* * omf archive format method */ #include #include #include #ifndef EILSEQ #define EILSEQ EINVAL #endif #define FILENAME "U_w_i_n.o" typedef struct _arfile_ { Ardirent_t st; char *alias; Dtlink_t link; int flags; size_t toffset; char suffix[4]; } Arfile_t; typedef struct State_s /* method state */ { Dt_t *dict; void *addr; Arfile_t lib; Arfile_t *next; int state; } State_t; struct Table { struct Table *next; size_t offset; size_t disp; }; static int namcomp(Dt_t *dp, Void_t *left, Void_t *right, Dtdisc_t *dsp) { char *l= (char*)left; char *r= (char*)right; char *suffix; if(suffix=strrchr(l,'.')) return(memcmp(l,r,suffix+1-l)); return(strcmp(l,r)); } static int offcomp(Dt_t *dp, Void_t *left, Void_t *right, Dtdisc_t *dsp) { off_t l= *((off_t*)left); off_t r= *((off_t*)right); if (l < r) return -1; if (l > r) return 1; return 0; } static Dtdisc_t namdisc = { offsetof(Arfile_t,st.name),-1, offsetof(Arfile_t,link), 0, 0, namcomp }; static Dtdisc_t offdisc = { offsetof(Arfile_t,st.offset), sizeof(off_t), offsetof(Arfile_t,link), 0, 0, offcomp }; static unsigned char *readint(unsigned char *cp,int *i, int big) { if(big) { *i = (*cp) | (cp[1]<<8) | (cp[2]<<16) | (cp[3]<<24); return(cp+4); } *i = (*cp) | (cp[1]<<8); return(cp+2); } static unsigned char *readindex(unsigned char *cp, int *size) { if(*cp&0x80) { *size = (*cp&0xf7)<<8 | cp[1]; return(cp+2); } *size = *cp++; return(cp); } #define round(a,b) (((a)+(b)-1) &~ ((b)-1)) static unsigned int is_omf(int fd) { unsigned char buff[4]; if(read(fd,buff,4) != 4) return(0); if(*buff==OMF_THEADR) return(buff[3]); lseek(fd,(off_t)-4,SEEK_CUR); errno = EILSEQ; return(0); } static Arfile_t *ar_getnode(Ardir_t *ar, const char *name) { const char *cp; Arfile_t *fp; Dt_t *dp = ((State_t*)ar->data)->dict; if(cp = strrchr(name,'/')) name = cp+1; fp = (Arfile_t*)dtmatch(dp,(void*)name); if(!fp) { size_t len = strlen(name)+5; if(!(fp = newof(0,Arfile_t,1,len))) return(0); fp->st.name = (char*)(fp+1); memcpy((char*)fp->st.name,name,len); if(!strrchr(fp->st.name,'.')) { fp->st.name[len-5] = '.'; fp->st.name[len-4] = 0; } fp->suffix[0] = 'o'; fp->suffix[1] = 0; fp->st.mtime = ar->st.st_mtime; fp->st.mode = ar->st.st_mode&(S_IRWXU|S_IRWXG|S_IRWXO); fp->st.uid = ar->st.st_uid; fp->st.gid = ar->st.st_gid; dtinsert(dp,fp); } return(fp); } /* * closef */ static int omfclose(Ardir_t* ar) { State_t *sp = (State_t*)ar->data; Dt_t *dp = sp?sp->dict:0; if(sp && dp && sp->state) { /* update modification times */ Arfile_t *fp; for(fp=(Arfile_t*)dtfirst(dp); fp; fp = (Arfile_t*)dtnext(dp,fp)) { if(fp->flags && fp->toffset>0 && lseek(ar->fd,(off_t)fp->toffset,SEEK_SET)>0) { write(ar->fd,(void*)&fp->st.mtime,sizeof(time_t)); } } } if (sp && sp->addr) free(sp->addr); if (dp) dtclose(dp); if(sp) free(sp); return 0; } static size_t displacement(struct Table *tp,size_t addr) { size_t disp = 0; while(tp) { disp = tp->disp; if(addr>tp->offset) break; tp = tp->next; } return(disp); } static void omfload(Ardir_t *ar,unsigned char *base, unsigned char *last, struct Table *tp, int dmars) { State_t *sp = (State_t*)ar->data; unsigned char *cp=base, *end=last; int *ip; Arfile_t *fp; int len; struct Table *tpnext; if(dmars) { /* string table at the top */ while(*cp) cp += strlen((char*)cp)+1; while(cp[4]==0) cp++; } else { readint(cp,&len,1); end = base + len; } for(ip=(int*)cp; (unsigned char*)(ip+6) < end; ip+=7) { if(dmars) len = *ip; else readint((unsigned char*)ip,&len,1); if(base+len >= last) break; if(!(fp = ar_getnode(ar,(char*)base+len))) return; fp->st.mtime = (time_t)ip[1]; fp->st.mode = (mode_t)ip[2]; fp->st.uid = (uid_t)ip[3]; fp->st.gid = (gid_t)ip[4]; memcpy(fp->suffix,&ip[5],sizeof(fp->suffix)); fp->toffset = ((char*)&ip[1])-(char*)(sp->addr); fp->toffset += displacement(tp,((unsigned char*)&ip[1])-base); if(ip[6]) fp->alias = (char*)base+ip[6]; } while(tp) { tpnext = tp->next; free((void*)tp); tp = tpnext; } } /* * openf */ static int omfopen(Ardir_t* ar, char* buf, size_t size) { State_t* state; int pagesize; unsigned char *addr, *addrstart, *addrmax, *begin,*cp, *base=0, *last; char name[PATH_MAX]; Arfile_t *fp; struct Table *tp=0, *tpnew; int dmars=0,n,type,special=0,len; if (ar->fd>=0 && (size<=0 || *((unsigned char*)buf) != OMF_LIBHDR)) return -1; if(!(state = newof(0,State_t,1,0))) return -1; ar->data = (void*)state; if(!(state->dict = dtopen(&namdisc,Dtoset))) goto nope; if(ar->fd<0) return 0; size = (size_t)ar->st.st_size; if(!(addr = (unsigned char*)malloc(size))) goto nope; addrstart = addr; state->addr = (void*)addr; if(lseek(ar->fd,(off_t)0, SEEK_SET)<0) goto nope; if(read(ar->fd,(void*)addr,size) < 0) goto nope; addrmax=addr+size; addr = readint(addr+1,&n,0); pagesize = n+3; while((addr+=n) < addrmax) { if(*addr==OMF_LIBDHD) return(0); cp = addr; type = *addr; addr = readint(addr+1,&n,0); if((type&~1)==OMF_MODEND) { if(special) { if(base) omfload(ar,base,last,tp,dmars); special = 0; } else { if(!(fp = ar_getnode(ar,name))) return(0); fp->st.offset = begin-addrstart; fp->st.size = addr+n-begin; } addr +=n; addr = addrstart + round(addr-addrstart,pagesize); n = 0; } else if(type==OMF_THEADR) { if(memcmp(addr+1,FILENAME,sizeof(FILENAME)-2)) { begin = addr+1; len = *addr; if(addr[1]=='.' && addr[2]=='\\') { len -=2; begin +=2; } memcpy(name,begin,len); name[len] = 0; begin = cp; } else special = 1; } else if((type&~1)==OMF_LEDATA && special) { int offset; cp = readindex(addr,&len); cp= readint(cp,&offset,type&1); dmars = (type&1); len = (n-1) - (cp-addr); if(!(tpnew = newof(0,struct Table,1,0))) return(0); tpnew->next = tp; tp = tpnew; if(!base) base = last = cp; else { tp->disp = cp - (base+offset); memcpy(base+offset,cp,len); } tp->offset = offset; last += len; } } nope: omfclose(ar); return -1; } /* * nextf */ static Ardirent_t* omfnext(Ardir_t* ar) { State_t *ap = (State_t*)ar->data; Arfile_t *fp; if(ap->next) ap->next = (Arfile_t*)dtnext(ap->dict,ap->next); else { dtdisc(ap->dict, &offdisc, 0); ap->next = (Arfile_t*)dtfirst(ap->dict); } if(!(fp = ap->next)) return(0); if(*fp->suffix) { char *last = strrchr(fp->st.name,'.'); if(last) last++; else last = fp->st.name+strlen(fp->st.name); memcpy((void*)last,(void*)fp->suffix,sizeof(fp->suffix)); } if(!(ar->flags&ARDIR_FORCE) && fp->alias) fp->st.name = fp->alias; return(&fp->st); } /* * returns -1 for error * returns 0 for ignore * returns ARDIR_CREATE add * returns ARDIR_REPLACE replace * returns ARDIR_DELETE delete */ int omfinsert(Ardir_t *ar, const char *name, int op) { char *suffix,fname[256]; Dt_t *dp = ((State_t*)ar->data)->dict; int m,n,fd; Arfile_t *fp; struct stat statb; int ret = -1; if(suffix=strrchr(name,'.')) n = (const char*)++suffix - name; else n = strlen(name); if((fd=open(name,O_RDONLY|O_cloexec))>=0) { if(fstat(fd,&statb)>=0 && (m=is_omf(fd))) { read(fd,(void*)fname,m); fname[m] = 0; m = memcmp((void*)fname,(void*)name,n); ret = ARDIR_CREATE; } close(fd); } fp = (Arfile_t*)dtmatch(dp,(void*)fname); if(ret<0 && !((op&ARDIR_DELETE) && fp)) return -1; if(fp) { if(op&ARDIR_DELETE) { dtdelete(dp,(Void_t*)fp); return ARDIR_DELETE; } if(fp->st.mtime>= statb.st_mtime && (op&ARDIR_NEWER)) return 0 ; ret = ARDIR_REPLACE; } else { if(op&ARDIR_DELETE) return -1 ; if(!(fp = (Arfile_t*)ar_getnode(ar,fname))) return -1 ; } fp->st.mode = statb.st_mode&(S_IRWXU|S_IRWXG|S_IRWXO); fp->st.uid = statb.st_uid; fp->st.gid = statb.st_gid; fp->st.mtime = statb.st_mtime; fp->st.size = (size_t)statb.st_size; fp->st.offset = ++ar->st.st_size; if(m) fp->alias = (char*)name; if(suffix) memcpy(fp->suffix,suffix,sizeof(fp->suffix)); return(ret); } const char *omfspecial(Ardir_t *ar) { return(FILENAME); } /* * changef */ static int omfchange(Ardir_t* ar, Ardirent_t* ent) { State_t *sp = (State_t*)ar->data; Arfile_t *fp = (Arfile_t*)ent; fp->flags = 1; sp->state = 1; return 0; } Ardirmeth_t ar_omf = { "omf", "omf archive", omfopen, omfnext, omfchange, omfinsert, omfspecial, omfclose, ar_omf_next };