|  | #include "stdinc.h" | 
|  |  | 
|  | typedef struct MetaChunk MetaChunk; | 
|  |  | 
|  | struct MetaChunk { | 
|  | ushort offset; | 
|  | ushort size; | 
|  | ushort index; | 
|  | }; | 
|  |  | 
|  | static int stringUnpack(char **s, uchar **p, int *n); | 
|  | static int meCmp(MetaEntry*, char *s); | 
|  | static int meCmpOld(MetaEntry*, char *s); | 
|  |  | 
|  |  | 
|  |  | 
|  | static char EBadMeta[] = "corrupted meta data"; | 
|  | static char ENoFile[] = "file does not exist"; | 
|  |  | 
|  | /* | 
|  | * integer conversion routines | 
|  | */ | 
|  | #define	U8GET(p)	((p)[0]) | 
|  | #define	U16GET(p)	(((p)[0]<<8)|(p)[1]) | 
|  | #define	U32GET(p)	(((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|(p)[3]) | 
|  | #define	U48GET(p)	(((uvlong)U16GET(p)<<32)|(uvlong)U32GET((p)+2)) | 
|  | #define	U64GET(p)	(((uvlong)U32GET(p)<<32)|(uvlong)U32GET((p)+4)) | 
|  |  | 
|  | #define	U8PUT(p,v)	(p)[0]=(v) | 
|  | #define	U16PUT(p,v)	(p)[0]=(v)>>8;(p)[1]=(v) | 
|  | #define	U32PUT(p,v)	(p)[0]=((v)>>24)&0xFF;(p)[1]=((v)>>16)&0xFF;(p)[2]=((v)>>8)&0xFF;(p)[3]=(v)&0xFF | 
|  | #define	U48PUT(p,v,t32)	t32=(v)>>32;U16PUT(p,t32);t32=(v);U32PUT((p)+2,t32) | 
|  | #define	U64PUT(p,v,t32)	t32=(v)>>32;U32PUT(p,t32);t32=(v);U32PUT((p)+4,t32) | 
|  |  | 
|  | static int | 
|  | stringUnpack(char **s, uchar **p, int *n) | 
|  | { | 
|  | int nn; | 
|  |  | 
|  | if(*n < 2) | 
|  | return 0; | 
|  |  | 
|  | nn = U16GET(*p); | 
|  | *p += 2; | 
|  | *n -= 2; | 
|  | if(nn > *n) | 
|  | return 0; | 
|  | *s = vtmalloc(nn+1); | 
|  | memmove(*s, *p, nn); | 
|  | (*s)[nn] = 0; | 
|  | *p += nn; | 
|  | *n -= nn; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static int | 
|  | stringPack(char *s, uchar *p) | 
|  | { | 
|  | int n; | 
|  |  | 
|  | n = strlen(s); | 
|  | U16PUT(p, n); | 
|  | memmove(p+2, s, n); | 
|  | return n+2; | 
|  | } | 
|  |  | 
|  | int | 
|  | mbSearch(MetaBlock *mb, char *elem, int *ri, MetaEntry *me) | 
|  | { | 
|  | int i; | 
|  | int b, t, x; | 
|  | if(0)fprint(2, "mbSearch %s\n", elem); | 
|  |  | 
|  | /* binary search within block */ | 
|  | b = 0; | 
|  | t = mb->nindex; | 
|  | while(b < t){ | 
|  | i = (b+t)>>1; | 
|  | meUnpack(me, mb, i); | 
|  |  | 
|  | if(mb->botch) | 
|  | x = meCmpOld(me, elem); | 
|  | else | 
|  | x = meCmp(me, elem); | 
|  |  | 
|  | if(x == 0){ | 
|  | *ri = i; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if(x < 0) | 
|  | b = i+1; | 
|  | else /* x > 0 */ | 
|  | t = i; | 
|  | } | 
|  |  | 
|  | assert(b == t); | 
|  |  | 
|  | *ri = b;	/* b is the index to insert this entry */ | 
|  | memset(me, 0, sizeof(*me)); | 
|  |  | 
|  | werrstr(ENoFile); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | mbInit(MetaBlock *mb, uchar *p, int n, int ne) | 
|  | { | 
|  | memset(p, 0, n); | 
|  | mb->maxsize = n; | 
|  | mb->maxindex = ne; | 
|  | mb->nindex = 0; | 
|  | mb->free = 0; | 
|  | mb->size = MetaHeaderSize + ne*MetaIndexSize; | 
|  | mb->buf = p; | 
|  | mb->botch = 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | mbUnpack(MetaBlock *mb, uchar *p, int n) | 
|  | { | 
|  | u32int magic; | 
|  | int i; | 
|  | int eo, en, omin; | 
|  | uchar *q; | 
|  |  | 
|  | mb->maxsize = n; | 
|  | mb->buf = p; | 
|  |  | 
|  | if(n == 0){ | 
|  | memset(mb, 0, sizeof(MetaBlock)); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | magic = U32GET(p); | 
|  | if(magic != MetaMagic && magic != MetaMagic-1) | 
|  | goto Err; | 
|  | mb->size = U16GET(p+4); | 
|  | mb->free = U16GET(p+6); | 
|  | mb->maxindex = U16GET(p+8); | 
|  | mb->nindex = U16GET(p+10); | 
|  | mb->botch = magic != MetaMagic; | 
|  | if(mb->size > n) | 
|  | goto Err; | 
|  |  | 
|  | omin = MetaHeaderSize + mb->maxindex*MetaIndexSize; | 
|  | if(n < omin) | 
|  | goto Err; | 
|  |  | 
|  |  | 
|  | p += MetaHeaderSize; | 
|  |  | 
|  | /* check the index table - ensures that meUnpack and meCmp never fail */ | 
|  | for(i=0; i<mb->nindex; i++){ | 
|  | eo = U16GET(p); | 
|  | en = U16GET(p+2); | 
|  | if(eo < omin || eo+en > mb->size || en < 8) | 
|  | goto Err; | 
|  | q = mb->buf + eo; | 
|  | if(U32GET(q) != DirMagic) | 
|  | goto Err; | 
|  | p += 4; | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | Err: | 
|  | werrstr(EBadMeta); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | mbPack(MetaBlock *mb) | 
|  | { | 
|  | uchar *p; | 
|  |  | 
|  | p = mb->buf; | 
|  |  | 
|  | assert(!mb->botch); | 
|  |  | 
|  | U32PUT(p, MetaMagic); | 
|  | U16PUT(p+4, mb->size); | 
|  | U16PUT(p+6, mb->free); | 
|  | U16PUT(p+8, mb->maxindex); | 
|  | U16PUT(p+10, mb->nindex); | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | mbDelete(MetaBlock *mb, int i) | 
|  | { | 
|  | uchar *p; | 
|  | int n; | 
|  | MetaEntry me; | 
|  |  | 
|  | assert(i < mb->nindex); | 
|  | meUnpack(&me, mb, i); | 
|  | memset(me.p, 0, me.size); | 
|  |  | 
|  | if(me.p - mb->buf + me.size == mb->size) | 
|  | mb->size -= me.size; | 
|  | else | 
|  | mb->free += me.size; | 
|  |  | 
|  | p = mb->buf + MetaHeaderSize + i*MetaIndexSize; | 
|  | n = (mb->nindex-i-1)*MetaIndexSize; | 
|  | memmove(p, p+MetaIndexSize, n); | 
|  | memset(p+n, 0, MetaIndexSize); | 
|  | mb->nindex--; | 
|  | } | 
|  |  | 
|  | void | 
|  | mbInsert(MetaBlock *mb, int i, MetaEntry *me) | 
|  | { | 
|  | uchar *p; | 
|  | int o, n; | 
|  |  | 
|  | assert(mb->nindex < mb->maxindex); | 
|  |  | 
|  | o = me->p - mb->buf; | 
|  | n = me->size; | 
|  | if(o+n > mb->size){ | 
|  | mb->free -= mb->size - o; | 
|  | mb->size = o + n; | 
|  | }else | 
|  | mb->free -= n; | 
|  |  | 
|  | p = mb->buf + MetaHeaderSize + i*MetaIndexSize; | 
|  | n = (mb->nindex-i)*MetaIndexSize; | 
|  | memmove(p+MetaIndexSize, p, n); | 
|  | U16PUT(p, me->p - mb->buf); | 
|  | U16PUT(p+2, me->size); | 
|  | mb->nindex++; | 
|  | } | 
|  |  | 
|  | int | 
|  | mbResize(MetaBlock *mb, MetaEntry *me, int n) | 
|  | { | 
|  | uchar *p, *ep; | 
|  |  | 
|  | /* easy case */ | 
|  | if(n <= me->size){ | 
|  | me->size = n; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* try and expand entry */ | 
|  |  | 
|  | p = me->p + me->size; | 
|  | ep = mb->buf + mb->maxsize; | 
|  | while(p < ep && *p == 0) | 
|  | p++; | 
|  | if(n <= p - me->p){ | 
|  | me->size = n; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | p = mbAlloc(mb, n); | 
|  | if(p != nil){ | 
|  | me->p = p; | 
|  | me->size = n; | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | meUnpack(MetaEntry *me, MetaBlock *mb, int i) | 
|  | { | 
|  | uchar *p; | 
|  | int eo, en; | 
|  |  | 
|  | assert(i >= 0 && i < mb->nindex); | 
|  |  | 
|  | p = mb->buf + MetaHeaderSize + i*MetaIndexSize; | 
|  | eo = U16GET(p); | 
|  | en = U16GET(p+2); | 
|  |  | 
|  | me->p = mb->buf + eo; | 
|  | me->size = en; | 
|  |  | 
|  | /* checked by mbUnpack */ | 
|  | assert(me->size >= 8); | 
|  | } | 
|  |  | 
|  | /* assumes a small amount of checking has been done in mbEntry */ | 
|  | static int | 
|  | meCmp(MetaEntry *me, char *s) | 
|  | { | 
|  | int n; | 
|  | uchar *p; | 
|  |  | 
|  | p = me->p; | 
|  |  | 
|  | /* skip magic & version */ | 
|  | p += 6; | 
|  | n = U16GET(p); | 
|  | p += 2; | 
|  |  | 
|  | if(n > me->size - 8) | 
|  | n = me->size - 8; | 
|  |  | 
|  | while(n > 0){ | 
|  | if(*s == 0) | 
|  | return 1; | 
|  | if(*p < (uchar)*s) | 
|  | return -1; | 
|  | if(*p > (uchar)*s) | 
|  | return 1; | 
|  | p++; | 
|  | s++; | 
|  | n--; | 
|  | } | 
|  | return -(*s != 0); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * This is the old and broken meCmp. | 
|  | * This cmp routine reverse the sense of the comparison | 
|  | * when one string is a prefix of the other. | 
|  | * In other words, it put "ab" after "abc" rather | 
|  | * than before.  This behaviour is ok; binary search | 
|  | * and sort still work.  However, it is goes against | 
|  | * the usual convention. | 
|  | */ | 
|  | static int | 
|  | meCmpOld(MetaEntry *me, char *s) | 
|  | { | 
|  | int n; | 
|  | uchar *p; | 
|  |  | 
|  | p = me->p; | 
|  |  | 
|  | /* skip magic & version */ | 
|  | p += 6; | 
|  | n = U16GET(p); | 
|  | p += 2; | 
|  |  | 
|  | if(n > me->size - 8) | 
|  | n = me->size - 8; | 
|  |  | 
|  | while(n > 0){ | 
|  | if(*s == 0) | 
|  | return -1; | 
|  | if(*p < (uchar)*s) | 
|  | return -1; | 
|  | if(*p > (uchar)*s) | 
|  | return 1; | 
|  | p++; | 
|  | s++; | 
|  | n--; | 
|  | } | 
|  | return *s != 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | offsetCmp(const void *s0, const void *s1) | 
|  | { | 
|  | MetaChunk *mc0, *mc1; | 
|  |  | 
|  | mc0 = (MetaChunk*)s0; | 
|  | mc1 = (MetaChunk*)s1; | 
|  | if(mc0->offset < mc1->offset) | 
|  | return -1; | 
|  | if(mc0->offset > mc1->offset) | 
|  | return 1; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static MetaChunk * | 
|  | metaChunks(MetaBlock *mb) | 
|  | { | 
|  | MetaChunk *mc; | 
|  | int oo, o, n, i; | 
|  | uchar *p; | 
|  |  | 
|  | mc = vtmalloc(mb->nindex*sizeof(MetaChunk)); | 
|  | p = mb->buf + MetaHeaderSize; | 
|  | for(i = 0; i<mb->nindex; i++){ | 
|  | mc[i].offset = U16GET(p); | 
|  | mc[i].size = U16GET(p+2); | 
|  | mc[i].index = i; | 
|  | p += MetaIndexSize; | 
|  | } | 
|  |  | 
|  | qsort(mc, mb->nindex, sizeof(MetaChunk), offsetCmp); | 
|  |  | 
|  | /* check block looks ok */ | 
|  | oo = MetaHeaderSize + mb->maxindex*MetaIndexSize; | 
|  | o = oo; | 
|  | n = 0; | 
|  | for(i=0; i<mb->nindex; i++){ | 
|  | o = mc[i].offset; | 
|  | n = mc[i].size; | 
|  | if(o < oo) | 
|  | goto Err; | 
|  | oo += n; | 
|  | } | 
|  | if(o+n > mb->size) | 
|  | goto Err; | 
|  | if(mb->size - oo != mb->free) | 
|  | goto Err; | 
|  |  | 
|  | return mc; | 
|  | Err: | 
|  | fprint(2, "metaChunks failed!\n"); | 
|  | oo = MetaHeaderSize + mb->maxindex*MetaIndexSize; | 
|  | for(i=0; i<mb->nindex; i++){ | 
|  | fprint(2, "\t%d: %d %d\n", i, mc[i].offset, mc[i].offset + mc[i].size); | 
|  | oo += mc[i].size; | 
|  | } | 
|  | fprint(2, "\tused=%d size=%d free=%d free2=%d\n", oo, mb->size, mb->free, mb->size - oo); | 
|  | werrstr(EBadMeta); | 
|  | vtfree(mc); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | static void | 
|  | mbCompact(MetaBlock *mb, MetaChunk *mc) | 
|  | { | 
|  | int oo, o, n, i; | 
|  |  | 
|  | oo = MetaHeaderSize + mb->maxindex*MetaIndexSize; | 
|  |  | 
|  | for(i=0; i<mb->nindex; i++){ | 
|  | o = mc[i].offset; | 
|  | n = mc[i].size; | 
|  | if(o != oo){ | 
|  | memmove(mb->buf + oo, mb->buf + o, n); | 
|  | U16PUT(mb->buf + MetaHeaderSize + mc[i].index*MetaIndexSize, oo); | 
|  | } | 
|  | oo += n; | 
|  | } | 
|  |  | 
|  | mb->size = oo; | 
|  | mb->free = 0; | 
|  | } | 
|  |  | 
|  | uchar * | 
|  | mbAlloc(MetaBlock *mb, int n) | 
|  | { | 
|  | int i, o; | 
|  | MetaChunk *mc; | 
|  |  | 
|  | /* off the end */ | 
|  | if(mb->maxsize - mb->size >= n) | 
|  | return mb->buf + mb->size; | 
|  |  | 
|  | /* check if possible */ | 
|  | if(mb->maxsize - mb->size + mb->free < n) | 
|  | return nil; | 
|  |  | 
|  | mc = metaChunks(mb); | 
|  | if(mc == nil){ | 
|  | fprint(2, "mbAlloc: metaChunks failed: %r\n"); | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | /* look for hole */ | 
|  | o = MetaHeaderSize + mb->maxindex*MetaIndexSize; | 
|  | for(i=0; i<mb->nindex; i++){ | 
|  | if(mc[i].offset - o >= n){ | 
|  | vtfree(mc); | 
|  | return mb->buf + o; | 
|  | } | 
|  | o = mc[i].offset + mc[i].size; | 
|  | } | 
|  |  | 
|  | if(mb->maxsize - o >= n){ | 
|  | vtfree(mc); | 
|  | return mb->buf + o; | 
|  | } | 
|  |  | 
|  | /* compact and return off the end */ | 
|  | mbCompact(mb, mc); | 
|  | vtfree(mc); | 
|  |  | 
|  | if(mb->maxsize - mb->size < n){ | 
|  | werrstr(EBadMeta); | 
|  | return nil; | 
|  | } | 
|  | return mb->buf + mb->size; | 
|  | } | 
|  |  | 
|  | int | 
|  | deSize(DirEntry *dir) | 
|  | { | 
|  | int n; | 
|  |  | 
|  | /* constant part */ | 
|  |  | 
|  | n = 	4 +	/* magic */ | 
|  | 2 + 	/* version */ | 
|  | 4 +	/* entry */ | 
|  | 4 + 	/* guid */ | 
|  | 4 + 	/* mentry */ | 
|  | 4 + 	/* mgen */ | 
|  | 8 +	/* qid */ | 
|  | 4 + 	/* mtime */ | 
|  | 4 + 	/* mcount */ | 
|  | 4 + 	/* ctime */ | 
|  | 4 + 	/* atime */ | 
|  | 4 +	/* mode */ | 
|  | 0; | 
|  |  | 
|  | /* strings */ | 
|  | n += 2 + strlen(dir->elem); | 
|  | n += 2 + strlen(dir->uid); | 
|  | n += 2 + strlen(dir->gid); | 
|  | n += 2 + strlen(dir->mid); | 
|  |  | 
|  | /* optional sections */ | 
|  | if(dir->qidSpace){ | 
|  | n += 	3 + 	/* option header */ | 
|  | 8 + 	/* qidOffset */ | 
|  | 8;	/* qid Max */ | 
|  | } | 
|  |  | 
|  | return n; | 
|  | } | 
|  |  | 
|  | void | 
|  | dePack(DirEntry *dir, MetaEntry *me) | 
|  | { | 
|  | uchar *p; | 
|  | ulong t32; | 
|  |  | 
|  | p = me->p; | 
|  |  | 
|  | U32PUT(p, DirMagic); | 
|  | U16PUT(p+4, 9);		/* version */ | 
|  | p += 6; | 
|  |  | 
|  | p += stringPack(dir->elem, p); | 
|  |  | 
|  | U32PUT(p, dir->entry); | 
|  | U32PUT(p+4, dir->gen); | 
|  | U32PUT(p+8, dir->mentry); | 
|  | U32PUT(p+12, dir->mgen); | 
|  | U64PUT(p+16, dir->qid, t32); | 
|  | p += 24; | 
|  |  | 
|  | p += stringPack(dir->uid, p); | 
|  | p += stringPack(dir->gid, p); | 
|  | p += stringPack(dir->mid, p); | 
|  |  | 
|  | U32PUT(p, dir->mtime); | 
|  | U32PUT(p+4, dir->mcount); | 
|  | U32PUT(p+8, dir->ctime); | 
|  | U32PUT(p+12, dir->atime); | 
|  | U32PUT(p+16, dir->mode); | 
|  | p += 5*4; | 
|  |  | 
|  | if(dir->qidSpace){ | 
|  | U8PUT(p, DeQidSpace); | 
|  | U16PUT(p+1, 2*8); | 
|  | p += 3; | 
|  | U64PUT(p, dir->qidOffset, t32); | 
|  | U64PUT(p+8, dir->qidMax, t32); | 
|  | p += 16; | 
|  | } | 
|  |  | 
|  | assert(p == me->p + me->size); | 
|  | } | 
|  |  | 
|  |  | 
|  | int | 
|  | deUnpack(DirEntry *dir, MetaEntry *me) | 
|  | { | 
|  | int t, nn, n, version; | 
|  | uchar *p; | 
|  |  | 
|  | p = me->p; | 
|  | n = me->size; | 
|  |  | 
|  | memset(dir, 0, sizeof(DirEntry)); | 
|  |  | 
|  | if(0)print("deUnpack\n"); | 
|  | /* magic */ | 
|  | if(n < 4 || U32GET(p) != DirMagic) | 
|  | goto Err; | 
|  | p += 4; | 
|  | n -= 4; | 
|  |  | 
|  | if(0)print("deUnpack: got magic\n"); | 
|  | /* version */ | 
|  | if(n < 2) | 
|  | goto Err; | 
|  | version = U16GET(p); | 
|  | if(version < 7 || version > 9) | 
|  | goto Err; | 
|  | p += 2; | 
|  | n -= 2; | 
|  |  | 
|  | if(0)print("deUnpack: got version\n"); | 
|  |  | 
|  | /* elem */ | 
|  | if(!stringUnpack(&dir->elem, &p, &n)) | 
|  | goto Err; | 
|  |  | 
|  | if(0)print("deUnpack: got elem\n"); | 
|  |  | 
|  | /* entry  */ | 
|  | if(n < 4) | 
|  | goto Err; | 
|  | dir->entry = U32GET(p); | 
|  | p += 4; | 
|  | n -= 4; | 
|  |  | 
|  | if(0)print("deUnpack: got entry\n"); | 
|  |  | 
|  | if(version < 9){ | 
|  | dir->gen = 0; | 
|  | dir->mentry = dir->entry+1; | 
|  | dir->mgen = 0; | 
|  | }else{ | 
|  | if(n < 3*4) | 
|  | goto Err; | 
|  | dir->gen = U32GET(p); | 
|  | dir->mentry = U32GET(p+4); | 
|  | dir->mgen = U32GET(p+8); | 
|  | p += 3*4; | 
|  | n -= 3*4; | 
|  | } | 
|  |  | 
|  | if(0)print("deUnpack: got gen etc\n"); | 
|  |  | 
|  | /* size is gotten from VtEntry */ | 
|  | dir->size = 0; | 
|  |  | 
|  | /* qid */ | 
|  | if(n < 8) | 
|  | goto Err; | 
|  | dir->qid = U64GET(p); | 
|  | p += 8; | 
|  | n -= 8; | 
|  |  | 
|  | if(0)print("deUnpack: got qid\n"); | 
|  | /* skip replacement */ | 
|  | if(version == 7){ | 
|  | if(n < VtScoreSize) | 
|  | goto Err; | 
|  | p += VtScoreSize; | 
|  | n -= VtScoreSize; | 
|  | } | 
|  |  | 
|  | /* uid */ | 
|  | if(!stringUnpack(&dir->uid, &p, &n)) | 
|  | goto Err; | 
|  |  | 
|  | /* gid */ | 
|  | if(!stringUnpack(&dir->gid, &p, &n)) | 
|  | goto Err; | 
|  |  | 
|  | /* mid */ | 
|  | if(!stringUnpack(&dir->mid, &p, &n)) | 
|  | goto Err; | 
|  |  | 
|  | if(0)print("deUnpack: got ids\n"); | 
|  | if(n < 5*4) | 
|  | goto Err; | 
|  | dir->mtime = U32GET(p); | 
|  | dir->mcount = U32GET(p+4); | 
|  | dir->ctime = U32GET(p+8); | 
|  | dir->atime = U32GET(p+12); | 
|  | dir->mode = U32GET(p+16); | 
|  | p += 5*4; | 
|  | n -= 5*4; | 
|  |  | 
|  | if(0)print("deUnpack: got times\n"); | 
|  | /* optional meta data */ | 
|  | while(n > 0){ | 
|  | if(n < 3) | 
|  | goto Err; | 
|  | t = p[0]; | 
|  | nn = U16GET(p+1); | 
|  | p += 3; | 
|  | n -= 3; | 
|  | if(n < nn) | 
|  | goto Err; | 
|  | switch(t){ | 
|  | case DePlan9: | 
|  | /* not valid in version >= 9 */ | 
|  | if(version >= 9) | 
|  | break; | 
|  | if(dir->plan9 || nn != 12) | 
|  | goto Err; | 
|  | dir->plan9 = 1; | 
|  | dir->p9path = U64GET(p); | 
|  | dir->p9version = U32GET(p+8); | 
|  | if(dir->mcount == 0) | 
|  | dir->mcount = dir->p9version; | 
|  | break; | 
|  | case DeGen: | 
|  | /* not valid in version >= 9 */ | 
|  | if(version >= 9) | 
|  | break; | 
|  | break; | 
|  | case DeQidSpace: | 
|  | if(dir->qidSpace || nn != 16) | 
|  | goto Err; | 
|  | dir->qidSpace = 1; | 
|  | dir->qidOffset = U64GET(p); | 
|  | dir->qidMax = U64GET(p+8); | 
|  | break; | 
|  | } | 
|  | p += nn; | 
|  | n -= nn; | 
|  | } | 
|  | if(0)print("deUnpack: got options\n"); | 
|  |  | 
|  | if(p != me->p + me->size) | 
|  | goto Err; | 
|  |  | 
|  | if(0)print("deUnpack: correct size\n"); | 
|  | return 1; | 
|  | Err: | 
|  | if(0)print("deUnpack: XXXXXXXXXXXX EBadMeta\n"); | 
|  | werrstr(EBadMeta); | 
|  | deCleanup(dir); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | deCleanup(DirEntry *dir) | 
|  | { | 
|  | vtfree(dir->elem); | 
|  | dir->elem = nil; | 
|  | vtfree(dir->uid); | 
|  | dir->uid = nil; | 
|  | vtfree(dir->gid); | 
|  | dir->gid = nil; | 
|  | vtfree(dir->mid); | 
|  | dir->mid = nil; | 
|  | } | 
|  |  | 
|  | void | 
|  | deCopy(DirEntry *dst, DirEntry *src) | 
|  | { | 
|  | *dst = *src; | 
|  | dst->elem = vtstrdup(src->elem); | 
|  | dst->uid = vtstrdup(src->uid); | 
|  | dst->gid = vtstrdup(src->gid); | 
|  | dst->mid = vtstrdup(src->mid); | 
|  | } |