add journal rereading

add DelRequest support
add little delete test tool ldapdelete
This commit is contained in:
leitner
2008-04-20 06:59:11 +00:00
parent fcf6ed51f2
commit 05d388f122
16 changed files with 448 additions and 137 deletions

View File

@@ -1,3 +1,4 @@
#define _FILE_OFFSET_BITS 64
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
@@ -196,6 +197,8 @@ struct acl {
struct acl** Acls;
static void load_acls() {
struct acl** oldAcls=Acls;
size_t oldacls=acls;
uint32 ofs;
uint32 acl_ofs;
acl_ofs=0;
@@ -281,11 +284,56 @@ kaputt:
for (i=0; i<filters; ++i)
acl_ec_subjects[i]=(Filters[i]==(struct Filter*)Any);
}
if (oldAcls) {
size_t i;
for (i=0; i<oldacls; ++i)
free(oldAcls[i]);
free(oldAcls);
}
}
/* End of ACL code */
static const char* datafilename;
static struct stat ss_data;
static struct stat ss_journal;
void map_datafile(const char* filename) {
map=mmap_read(datafilename=filename,&filelen);
stat(datafilename,&ss_data);
if (!map) {
buffer_putsflush(buffer_2,"could not open data!\n");
exit(1);
}
uint32_unpack(map,&magic);
uint32_unpack(map+4,&attribute_count);
uint32_unpack(map+2*4,&record_count);
uint32_unpack(map+3*4,&indices_offset);
uint32_unpack(map+4*4,&size_of_string_table);
record_set_length=(record_count+sizeof(unsigned long)*8-1) / (sizeof(long)*8);
/* look up "dn" and "objectClass" */
{
char* x=map+5*4+size_of_string_table;
size_t i;
dn_ofs=objectClass_ofs=userPassword_ofs=any_ofs=0;
for (i=0; i<attribute_count; ++i) {
uint32 j;
j=uint32_read(x);
if (case_equals("dn",map+j))
dn_ofs=j;
else if (case_equals("objectClass",map+j))
objectClass_ofs=j;
else if (case_equals("userPassword",map+j))
userPassword_ofs=j;
x+=4;
}
if (!dn_ofs || !objectClass_ofs) {
buffer_putsflush(buffer_2,"can't happen error: dn or objectClass not there?!\n");
exit(0);
}
}
}
/*
_ _ _
@@ -652,7 +700,7 @@ static void tagmatches(uint32* index,size_t elements,struct string* s,
}
}
uint32 hash(const unsigned char* c,size_t keylen) {
static uint32 hash(const unsigned char* c,size_t keylen) {
size_t h=0,i;
for (i=0; i<keylen; ++i) {
/* from djb's cdb */
@@ -662,7 +710,7 @@ uint32 hash(const unsigned char* c,size_t keylen) {
return (uint32)h;
}
uint32 hash_tolower(const unsigned char* c,size_t keylen) {
static uint32 hash_tolower(const unsigned char* c,size_t keylen) {
size_t h=0,i;
for (i=0; i<keylen; ++i) {
/* from djb's cdb */
@@ -869,7 +917,6 @@ static int checkacl(uint32 recofs,uint32 attrofs,unsigned long operation,struct
}
}
for (k=0; k<Acls[j]->attrs; ++k) {
/* if (Acls[j]->Attrs[k]==any_ofs || !matchstring(&adl->a,map+Acls[j]->Attrs[k])) { */
if (Acls[j]->Attrs[k]==any_ofs || attrofs==Acls[j]->Attrs[k]) {
if (Acls[j]->may&operation)
return 1;
@@ -1087,7 +1134,7 @@ add_attribute:
|___/ |_|
*/
int copystring(struct string* dest,struct string* src) {
static int copystring(struct string* dest,struct string* src) {
dest->s=malloc(src->l+1);
if (!dest->s) return -1;
byte_copy((char*)dest->s,src->l,src->s);
@@ -1340,7 +1387,7 @@ static int lookupdn(struct string* dn,size_t* index, struct hashnode** hn) {
useindex(&f,&result);
if (result.first>result.last)
return 0;
assert(result.last<=record_count);
// assert(result.last<=record_count);
for (i=result.first; i<=result.last; ) {
if (!result.bits[i/(8*sizeof(long))]) {
i+=8*sizeof(long);
@@ -1357,6 +1404,17 @@ static int lookupdn(struct string* dn,size_t* index, struct hashnode** hn) {
return 0;
}
static void normalize_string_dn(struct string* s) {
/* OK this is a kludge. s->s is supposed to be read-only because it points into the
* buffer where we read it into from the network.
* Since normalize_dn ends up using less or equal space, and we are not interested in
* the non-normalized dn, we do the read-write cast and normalize in-place.
* Kids, don't do this at home. */
s->l=normalize_dn((char*)s->s,s->s,s->l);
}
static void update();
/* a standard LDAP session looks like this:
* 1. connect to server
* 2. send a BindRequest
@@ -1368,7 +1426,7 @@ static int lookupdn(struct string* dn,size_t* index, struct hashnode** hn) {
* 5. close
* tinyldap does not complain if you don't unbind before hanging up.
*/
int handle(int in,int out) {
static int handle(int in,int out) {
size_t len;
char buf[BUFSIZE];
for (len=0;;) {
@@ -1395,14 +1453,15 @@ int handle(int in,int out) {
buffer_putulong(buffer_2,op);
buffer_putsflush(buffer_2,".\n");
}
update();
switch (op) {
case BindRequest:
{
unsigned long version,method;
struct string name;
int tmp;
tmp=scan_ldapbindrequest(buf+res,buf+res+len,&version,&name,&method);
if (tmp>=0) {
size_t tmp;
tmp=scan_ldapbindrequest(buf+res,buf+len,&version,&name,&method);
if (tmp>0) {
if (verbose) {
buffer_puts(buffer_2,"bind request: version ");
buffer_putulong(buffer_2,version);
@@ -1418,8 +1477,9 @@ int handle(int in,int out) {
struct hashnode* hn;
int err=success;
scan_ldapstring(buf+res+tmp,buf+res+len,&password);
scan_ldapstring(buf+res+tmp,buf+len,&password);
normalize_string_dn(&name);
switch (lookupdn(&name,&idx,&hn)) {
case -1: err=operationsError; break;
case 1: break;
@@ -1430,7 +1490,7 @@ int handle(int in,int out) {
goto authfailure;
else {
char* c=0;
uint32 authdn;
uint32 authdn=0;
char* authdn_str=0;
if (idx==(size_t)-1) { // found in journal
size_t i;
@@ -1485,9 +1545,9 @@ authfailure:
}
{
char outbuf[1024];
int s=100;
int len=fmt_ldapbindresponse(outbuf+s,0,"","go ahead","");
int hlen=fmt_ldapmessage(0,messageid,BindResponse,len);
size_t s=100;
size_t len=fmt_ldapbindresponse(outbuf+s,0,"","go ahead","");
size_t hlen=fmt_ldapmessage(0,messageid,BindResponse,len);
fmt_ldapmessage(outbuf+s-hlen,messageid,BindResponse,len);
write(out,outbuf+s-hlen,len+hlen);
}
@@ -1497,7 +1557,7 @@ authfailure:
case SearchRequest:
{
struct SearchRequest sr;
int tmp;
size_t tmp;
#if 0
{
int fd=open_write("request");
@@ -1505,7 +1565,7 @@ authfailure:
close(fd);
}
#endif
if ((tmp=scan_ldapsearchrequest(buf+res,buf+res+len,&sr))) {
if ((tmp=scan_ldapsearchrequest(buf+res,buf+len,&sr))) {
size_t returned=0;
#if (debug != 0)
@@ -1542,7 +1602,7 @@ authfailure:
* of the matches in a table. Use findrec to locate
* the records that point to the data. */
useindex(sr.filter,&result);
assert(result.last<=record_count);
// assert(result.last<=record_count);
for (i=result.first; i<=result.last; ) {
size_t ni=i+8*sizeof(long);
if (!result.bits[i/(8*sizeof(long))]) {
@@ -1589,8 +1649,8 @@ authfailure:
}
{
char buf[1000];
long l=fmt_ldapsearchresultdone(buf+100,0,"","","");
int hlen=fmt_ldapmessage(0,messageid,SearchResultDone,l);
size_t l=fmt_ldapsearchresultdone(buf+100,0,"","","");
size_t hlen=fmt_ldapmessage(0,messageid,SearchResultDone,l);
fmt_ldapmessage(buf+100-hlen,messageid,SearchResultDone,l);
write(out,buf+100-hlen,l+hlen);
}
@@ -1602,30 +1662,34 @@ authfailure:
case ModifyRequest:
{
struct ModifyRequest mr;
int tmp,err=success;
size_t tmp,err=success;
buffer_putsflush(buffer_2,"modifyrequest!\n");
if ((tmp=scan_ldapmodifyrequest(buf+res,buf+res+len,&mr))) {
if ((tmp=scan_ldapmodifyrequest(buf+res,buf+len,&mr))) {
struct SearchResultEntry sre;
buffer_puts(buffer_1,"modify request: dn \"");
buffer_put(buffer_1,mr.object.s,mr.object.l);
buffer_putsflush(buffer_1,"\"\n");
switch (mr.m.operation) {
case 0: buffer_puts(buffer_1,"Add\n"); break;
case 1: buffer_puts(buffer_1,"Delete\n"); break;
case 2: buffer_puts(buffer_1,"Replace\n"); break;
if (verbose) {
buffer_puts(buffer_1,"modify request: dn \"");
buffer_put(buffer_1,mr.object.s,mr.object.l);
buffer_putsflush(buffer_1,"\"\n");
switch (mr.m.operation) {
case 0: buffer_puts(buffer_1,"Add\n"); break;
case 1: buffer_puts(buffer_1,"Delete\n"); break;
case 2: buffer_puts(buffer_1,"Replace\n"); break;
}
buffer_put(buffer_1,mr.m.AttributeDescription.s,mr.m.AttributeDescription.l);
buffer_puts(buffer_1,"\n");
{
struct AttributeDescriptionList* x=mr.m.vals;
do {
buffer_puts(buffer_1," -> \"");
buffer_put(buffer_1,x->a.s,x->a.l);
buffer_putsflush(buffer_1,"\"\n");
x=x->next;
} while (x);
}
}
buffer_put(buffer_1,mr.m.AttributeDescription.s,mr.m.AttributeDescription.l);
buffer_puts(buffer_1,"\n");
{
struct AttributeDescriptionList* x=mr.m.vals;
do {
buffer_puts(buffer_1," -> \"");
buffer_put(buffer_1,x->a.s,x->a.l);
buffer_putsflush(buffer_1,"\"\n");
x=x->next;
} while (x);
}
/* TODO: do something with the modify request ;-) */
normalize_string_dn(&mr.object);
if (acls) {
/* convert modifyrequest to searchresultentry */
modreq2sre(&sre,&mr);
@@ -1648,7 +1712,7 @@ authfailure:
#if 1
/* 3. apply modifications to record to get new record */
if (!applymodreq(hn,&mr,&sre)) {
/* 4. write record to "data.upd" */
/* 4. write record to journal */
int fd=open("journal",O_WRONLY|O_APPEND|O_CREAT,0600);
if (fd==-1)
err=operationsError;
@@ -1665,8 +1729,10 @@ authfailure:
#endif
}
}
} else
} else {
buffer_putsflush(buffer_2,"could not parse modifyRequest!\n");
err=protocolError;
}
{
char outbuf[1024];
@@ -1681,7 +1747,7 @@ authfailure:
}
break;
case AbandonRequest:
buffer_putsflush(buffer_2,"AbandonRequest!\n");
if (verbose) buffer_putsflush(buffer_2,"AbandonRequest!\n");
/* do nothing */
break;
case AddRequest:
@@ -1689,11 +1755,12 @@ authfailure:
int err=success;
struct AddRequest ar;
// buffer_putsflush(buffer_2,"AddRequest!\n");
if ((tmp=scan_ldapaddrequest(buf+res,buf+res+len,&ar))) {
if ((tmp=scan_ldapaddrequest(buf+res,buf+len,&ar))) {
struct SearchResultEntry sre;
normalize_string_dn(&ar.entry);
/* convert addrequest to searchresultentry */
addreq2sre(&sre,&ar);
/* TODO: do something with the add request ;-) */
/* 1. check ACLs */
if (!acls || checkacl(0,0,acl_add,&sre)==1) {
/* 2. check if there already is a record with this dn */
@@ -1706,7 +1773,7 @@ authfailure:
default: err=operationsError;
}
if (err==success) {
/* 3. write record to "data.upd" */
/* 3. write record to journal */
int fd=open("journal",O_WRONLY|O_APPEND|O_CREAT,0600);
if (fd==-1)
err=operationsError;
@@ -1740,14 +1807,70 @@ authfailure:
{
char outbuf[1024];
int s=100;
int len=fmt_ldapresult(outbuf+s,err,"","","");
int hlen=fmt_ldapmessage(0,messageid,AddResponse,len);
size_t s=100;
size_t len=fmt_ldapresult(outbuf+s,err,"","","");
size_t hlen=fmt_ldapmessage(0,messageid,AddResponse,len);
fmt_ldapmessage(outbuf+s-hlen,messageid,AddResponse,len);
write(out,outbuf+s-hlen,len+hlen);
}
}
break;
case DelRequest:
{
struct string s;
size_t l=scan_ldapdeleterequest(buf+res,buf+len,&s);
if (l>0) {
struct SearchResultEntry sre;
int err=success;
if (verbose) {
buffer_puts(buffer_2,"Delete Request for DN \"");
buffer_put(buffer_2,s.s,s.l);
buffer_putsflush(buffer_2,"\".\n");
}
normalize_string_dn(&s);
/* convert modifyrequest to searchresultentry */
sre.objectName=s;
sre.attributes=0;
if (acls) {
/* 1. check ACLs */
if (checkacl(0,0,acl_delete,&sre)!=1)
err=insufficientAccessRights;
}
if (err==success) {
/* 2. check if there already is a record with this dn */
struct hashnode* hn;
size_t idx;
switch (lookupdn(&s,&idx,&hn)) {
case -1: err=operationsError; break;
case 1: break;
case 0: err=noSuchObject; break;
default: err=operationsError;
}
if (err==success) {
/* 3. write record to journal */
int fd=open("journal",O_WRONLY|O_APPEND|O_CREAT,0600);
if (fd==-1)
err=operationsError;
else {
if (writesretofd(fd,&sre)==-1)
err=operationsError;
close(fd);
}
}
}
{
char outbuf[1024];
size_t s=100;
size_t len=fmt_ldapresult(outbuf+s,err,"","","");
size_t hlen=fmt_ldapmessage(0,messageid,DelResponse,len);
fmt_ldapmessage(outbuf+s-hlen,messageid,DelResponse,len);
write(out,outbuf+s-hlen,len+hlen);
}
}
}
break;
case ModifyDNRequest:
/* TODO */
default:
buffer_puts(buffer_2,"unknown request type ");
buffer_putulong(buffer_2,op);
@@ -1781,7 +1904,7 @@ extern int (*ldif_parse_callback)(struct ldaprec* l);
extern mstorage_t stringtable;
extern mduptab_t attributes,classes;
unsigned long hash2(const unsigned char* c) {
static unsigned long hash2(const unsigned char* c) {
unsigned long h=0;
if (*c==0) {
uint32 len=uint32_read((char*)c+1);
@@ -1798,7 +1921,7 @@ unsigned long hash2(const unsigned char* c) {
#define HASHTABSIZE 8191
unsigned char* bstrdup(unsigned char* c) {
static unsigned char* bstrdup(unsigned char* c) {
size_t len;
unsigned char* x;
if (*c)
@@ -1812,7 +1935,7 @@ unsigned char* bstrdup(unsigned char* c) {
return x;
}
unsigned char* bstrdup_attrib(unsigned char* c) {
static unsigned char* bstrdup_attrib(unsigned char* c) {
char* x=map+5*4+size_of_string_table;
size_t i,l;
if (*c)
@@ -1865,12 +1988,13 @@ static struct hashnode** dn_in_journal2(const char* dn,size_t dnlen) {
struct hashnode* root;
int parse_callback(struct ldaprec* l) {
static int parse_callback(struct ldaprec* l) {
static struct hashnode** nextinlinearlist=&root;
size_t i;
unsigned long hashval;
struct hashnode** hn;
if (l->dn==(uint32)-1) return -1;
if (l->dn==(uint32)-1)
return -1;
hashval=hash2((unsigned char*)stringtable.root+l->dn);
// printf("journal: \"%s\" -> %lu\n",stringtable.root+l->dn,hashval);
hn=hashtab+(hashval%HASHTABSIZE);
@@ -1912,11 +2036,64 @@ int parse_callback(struct ldaprec* l) {
return 0;
}
int readjournal() {
static void readjournal() {
ldif_parse_callback=parse_callback;
mduptab_init(&attributes);
mduptab_init(&classes);
return ldif_parse("journal");
if (ldif_parse("journal",0,&ss_journal)) {
buffer_putsflush(buffer_2,"Failed to parse journal!\n");
exit(1);
}
}
static void update() {
struct stat new_data,new_journal;
if (stat(datafilename,&new_data)==-1) {
/* no data file?! There is no way to salvage the situation. */
buffer_putsflush(buffer_2,"ABEND: data file suddenly gone.\n");
exit(1);
}
/* now see if the data file changed. If it did, map it anew. */
if (new_data.st_size!=ss_data.st_size ||
new_data.st_mtime!=ss_data.st_mtime ||
new_data.st_ino!=ss_data.st_ino) {
buffer_putsflush(buffer_2,"Data file changed, reloading.\n");
mmap_unmap(map,filelen);
/* If the new data file is corrupt, map_datafile calls exit.
* I don't believe in limping on. If something is broken on such a fundamental level,
* it's better to bail so that the problem does not go unnoticed and things get even
* worse. */
map_datafile(datafilename);
/* OK, now that we have the datafile reloaded, we need to clean our idea of a journal
* and reload the journal from scratch. */
mduptab_reset(&attributes);
mduptab_reset(&classes);
readjournal();
ss_data=new_data;
return;
}
/* the data file did not change. Maybe the journal did. */
if (stat("journal",&new_journal)==-1) {
/* no journal; that means:
* a) there never was one, totaly read-only data
* b) there was one, but it has now been incorporated into the main database
* in this case: delete journal data
*/
mduptab_reset(&attributes);
mduptab_reset(&classes);
return;
}
if (new_journal.st_size!=ss_journal.st_size ||
new_journal.st_mtime!=ss_journal.st_mtime ||
new_journal.st_ino!=ss_journal.st_ino) {
/* Journal changed. Since all we ever do is append, we just read the part from how
* far we got last time, which happens to be ss_journal.st_size. */
if (ldif_parse("journal",ss_journal.st_size,&ss_journal)) {
buffer_putsflush(buffer_2,"Failed to parse journal!\n");
exit(1);
}
ss_data=new_data;
}
}
static int ldap_matchfilter_hn(struct hashnode* hn,struct Filter* f) {
@@ -2010,6 +2187,7 @@ static void answerwith_hn(struct hashnode* hn,struct SearchRequest* sr,long mess
struct SearchResultEntry sre;
struct PartialAttributeList** pal=&sre.attributes;
if (!hn->n) return;
if (acls)
byte_zero(acl_ec_subjects+filters,filters);
@@ -2134,39 +2312,7 @@ int main(int argc,char* argv[]) {
signal(SIGPIPE,SIG_IGN);
map=mmap_read(argc>1?argv[1]:"data",&filelen);
if (!map) {
buffer_putsflush(buffer_2,"could not open data!\n");
return 1;
}
uint32_unpack(map,&magic);
uint32_unpack(map+4,&attribute_count);
uint32_unpack(map+2*4,&record_count);
uint32_unpack(map+3*4,&indices_offset);
uint32_unpack(map+4*4,&size_of_string_table);
record_set_length=(record_count+sizeof(unsigned long)*8-1) / (sizeof(long)*8);
/* look up "dn" and "objectClass" */
{
char* x=map+5*4+size_of_string_table;
size_t i;
dn_ofs=objectClass_ofs=userPassword_ofs=any_ofs=0;
for (i=0; i<attribute_count; ++i) {
uint32 j;
j=uint32_read(x);
if (case_equals("dn",map+j))
dn_ofs=j;
else if (case_equals("objectClass",map+j))
objectClass_ofs=j;
else if (case_equals("userPassword",map+j))
userPassword_ofs=j;
x+=4;
}
if (!dn_ofs || !objectClass_ofs) {
buffer_putsflush(buffer_2,"can't happen error: dn or objectClass not there?!\n");
return 0;
}
}
map_datafile(argc>1?argv[1]:"data");
load_acls();